Por que não posso usar variáveis ​​como prefixo de um comando para definir variáveis ​​de ambiente?

11

Normalmente, é possível definir uma variável de ambiente para um comando prefixando-a da seguinte forma:

hello=hi bash -c 'echo $hello'

Também sei que podemos usar uma variável para substituir qualquer parte de uma chamada de comando, como a seguir:

$ cmd=bash
$ $cmd -c "echo hi" # equivalent to bash -c "echo hi"

Fiquei muito surpreso ao descobrir que você não pode usar uma variável para prefixar um comando para definir uma variável de ambiente. Caso de teste:

$ prefix=hello=hi
$ echo $prefix # prints hello=hi
$ $prefix bash -c 'echo $hello'
hello=hi: command not found

Por que não consigo definir a variável de ambiente usando uma variável? A parte do prefixo é uma parte especial? Consegui fazê-lo funcionar usando eval na frente, mas ainda não entendo o porquê. Estou usando o bash 4.4.

wbkang
fonte
1
Você pode usar, em envvez de eval, qual IIRC é mais seguro, mas mais lento.
wjandrea

Respostas:

14

Eu suspeito que esta é a parte da sequência que está pegando você:

As palavras que não são atribuições ou redirecionamentos de variáveis ​​são expandidas (consulte Expansões do Shell). Se alguma palavra permanecer após a expansão, a primeira palavra será o nome do comando e as palavras restantes serão os argumentos

Isso é do manual de referência do Bash na seção Expansão de comando simples.

No cmd=bashexemplo, nenhuma variável de ambiente é definida e o bash processa a linha de comandos através da expansão de parâmetros, saindo bash -c "echo hi".

No prefix=hello=hiexemplo, novamente não há atribuições de variáveis ​​na primeira passagem; portanto, o processamento continua com a expansão de parâmetros, resultando na primeira palavra de hello=hi.

Depois que as atribuições de variáveis ​​são processadas, elas não são processadas novamente durante a execução do comando.

Veja o processamento e seus resultados em set -x:

$ prefix=hello=hi
+ prefix=hello=hi
$ $prefix bash -c 'echo $hello'
+ hello=hi bash -c 'echo $hello'
-bash: hello=hi: command not found
$ hello=42 bash -c 'echo $hello'
+ hello=42
+ bash -c 'echo $hello'
42

Para uma variação mais segura de "expansão variável" -com- "variáveis ​​de ambiente" que eval, considere a sugestão de wjandrea deenv :

prefix=hello=hi
env "$prefix" bash -c 'echo "$hello"'
hi

Não é estritamente uma atribuição de variável da linha de comando, já que estamos usando a envfunção principal do utilitário de atribuir variáveis ​​de ambiente a um comando, mas cumpre o mesmo objetivo. A $prefixvariável é expandida durante o processamento da linha de comandos, fornecendo o nome = valor para envquem o transmite bash.

Jeff Schaller
fonte
3
Da mesma forma, $foo=bar bash -c ...não funcionará, porque $foo=barnão é uma atribuição de variável (seja qual for o valor $foo), porque $foo=barnão se encaixa no name=valuepadrão para atribuição de variável.
Muru
3

Porque $prefixnão é uma tarefa. @ Jeff tem uma explicação mais longa .

Você poderia fazer algo semelhante com uma função:

$ prefix() { hello=hi "$@"; }
$ prefix bash -c 'echo "$hello"'
hi

... e você pode até empilhá-las, se quiser:

$ foo() { foo=123 "$@"; }
$ bar() { bar=456 "$@"; }
$ foo bar bash -c 'echo "$bar $foo"'
456 123
ilkkachu
fonte