Depois de algumas leituras vou tentar responder mais particularmente.
Por que a substituição de comando se comporta dessa maneira?
$ a='echo x; echo y'
$ echo $($a) # expect 'x y'
Substituição de comandos
Vamos notar que a substituição da variável $a
está sendo executada na subshell produzida durante a substituição de comandos, não no shell atual ( 1 , 2 , 3 ). Portanto, o subshell executa o comando $a
, não echo x; echo y
(o valor da variável a
é herdado)
Então, agora só precisamos entender por que o shell se comporta dessa maneira :
$ a='echo x; echo y'
$ $a
x; echo y
Divisão de palavras
De acordo com o [Manual de Referência da Bash]:
… There are seven kinds of expansion … The order of expansions is: brace expansion; tilde expansion, parameter and variable expansion, arithmetic expansion, and command substitution (done in a left-to-right fashion); word splitting; and filename expansion …
- da seção 3.5 Expansões da Shell
… The shell scans the results of parameter expansion, command substitution, and arithmetic expansion that did not occur within double quotes for word splitting.
The shell treats each character of $IFS as a delimiter, and splits the results of the other expansions into words using these characters as field terminators …
- da seção 3.5.7 Divisão de palavras
(O valor padrão de IFS
é <space><tab><newline>
)
… The IFS variable is used to split only the results of expansion, not all words (see Word Splitting) …
- do Apêndice B Principais diferenças do Bourne Shell
metacharacter
A character that, when unquoted, separates words. A metacharacter is a space, tab, newline, or one of the following characters: |
, &
, ;
, (
, )
, <
, or >
.
- da seção 2. Definições
The brief description of the shell’s operation when it reads and executes a command. Basically, the shell does the following:
- Reads its input.
- Breaks the input into words and operators, obeying the quoting rules. These tokens are separated by metacharacters.
-
Parses the tokens into simple and compound commands.
- Performs the various shell expansions.
- Performs any necessary redirections.
- Executes the command
- da seção 3.1.1 Operação da Shell
Agora, podemos ver que há dois tipos diferentes de "divisão de palavras" - a divisão de palavras "inicial" (etapa 2) e a divisão de palavras que são expansões de tipo de shell (passo 4).
A divisão de palavras "inicial" (etapa 2) respeita os metacaracteres como ;
, (
, )
. O resultado da divisão "inital" de palavras é analisado (etapa 3) e todos os seus metacaracteres e palavras-chave são reconhecidos .
Subsequentemente, o bash executa as várias expansões de shell (etapa 4) e, entre outras, a divisão de palavras (como um tipo de expansão). "Esta" divisão de palavras trata apenas caracteres de IFS
como delimitadores. "Esta" divisão de palavras não respeita metacaracteres . Resultado de "this" palavra splitting não é analisado novamente como resultado de qualquer expansão. Assim, metacharecters e palavras-chave no resultado de expansões não são reconhecidos .
É por isso que ;
no valor da variável a
não é tratado como metacaractere. $a
se torna 'echo' 'x;' 'echo' 'y'
. Mesmo se o valor de a
fosse 'echo x ; echo y'
, a palavra ';'
não seria tratada como metacaractere e o comando $a
se tornaria 'echo' 'x' ';' 'echo' 'y'
.
Resumo:
Metacaracteres e palavras-chave ( if
, then
, while
etc.) não podem ser um resultado de expansões.
Mas nomes de programas (assim como comandos embutidos, funções e aliases) podem. É uma fonte de confusão, porque permite colocar comandos simples em variáveis e não permite colocar os compostos.
$ 'echo' 'a' ';' 'echo' 'b' # ';' is a literal
a ; echo b
$ cmd=echo
$ "$cmd" a ; $(printf echo) b # ';' is a metacharacter
a
b
$ cmd='echo a' # simple command is executed properly
$ $cmd
a
$ cmd='echo a;' # but metacharacters are not treated
$ $cmd echo b
a; echo b
$ echo a $(echo ';') echo b # ';' is a literal
a ; echo b
$ $(printf if) true; then echo a; fi # error while parsing
bash: syntax error near unexpected token 'then'
$ $(printf if) true; $(printf then) echo a; $(printf fi) # command not found
bash: if: command not found
bash: then: command not found
bash: fi: command not found
Como executar a substituição do comando para a lista de comandos armazenados em uma variável sem usar eval e bash -c (já que eval é maligno)?
Probalby, a resposta certa é "isso também é ruim", então não precisamos evitar eval:)
Variables hold data. Functions hold code. Don't put code inside variables!..
- do artigo Estou tentando colocar um comando em uma variável, mas os casos complexos sempre falham!
Links
Unix stackexchange:
Bash substituição do comando shell
Por que uma variável é visível em um subshell?
Os parênteses realmente colocam o comando em um subshell?
Outro:
Manual de referência do Bash
Estou tentando colocar um comando em uma variável, mas os casos complexos sempre falham!
conversação no sistema de rastreamento de erros debian