Como faço para definir uma variável de ambiente na linha de comando e fazer com que ela apareça nos comandos?

152

Se eu correr

export TEST=foo
echo $TEST

Ele produz foo.

Se eu correr

TEST=foo echo $TEST

Isso não. Como posso obter essa funcionalidade sem usar um script de exportação ou?

ashleysmithgpu
fonte
Cuidado, há mais nessa história do que parece inicialmente. Convido você a verificar minha resposta.
jasonleonhard

Respostas:

180

Isso ocorre porque o shell expande a variável na linha de comando antes de realmente executar o comando e nesse momento a variável não existe. Se você usar

TEST=foo; echo $TEST

vai funcionar.

exportfará com que a variável apareça no ambiente de comandos executados subseqüentemente (veja como isso funciona no bash help export). Se você só precisa que a variável apareça no ambiente de um comando, use o que você tentou, ou seja:

TEST=foo your-application
peterph
fonte
1
E quanto a / usr / bin / env? isso também não funciona, você sabe por quê?
Benubird
5
O mesmo motivo - o shell expande $TESTantes de a linha de comando ser executada. Uma vez que echoestá em execução (observe também que echonormalmente será traduzido para o comando interno do shell e não para /bin/echo), ele vê a variável definida em seu ambiente. No entanto, echo $TESTnão informa echoa saída do conteúdo da variável TESTde seu ambiente. Ele diz ao shell para executar echocom argumento sendo o que está atualmente na variável chamada TEST- e essas são duas coisas muito diferentes.
Peterph
@peterph Se o shell expande a variável na linha de comando antes de realmente executar o comando, por que não o expande var=value sh -c 'echo "$var"'?
haccks
Como expliquei a você aqui : é porque as variáveis ​​são expandidas dentro de aspas duplas (por exemplo, "… $var …") , mas não dentro de aspas simples (por exemplo, '… $var …'). Como echo "$var"está entre aspas simples, toda a cadeia de caracteres é passada para o sh -cshell new ( ) sem ser interpretada pelo shell interativo externo. ... (continua)
G-Man
(Continua)… Como igal disse ontem , há duas rodadas de análise - embora eu acredite que igal tenha interpretado mal sua pergunta. Não há duas rodadas de análise além da análise feita pelo shell pai. Há duas rodadas de análise - uma feita pelo shell externo interativo (pai) e outra pelo novo sh -cshell filho ( ).
G-Man
39

Eu suspeito que você deseja que variáveis ​​de shell tenham um escopo limitado, em vez de variáveis ​​de ambiente. Variáveis ​​de ambiente são uma lista de cadeias de caracteres passadas para comandos quando são executadas .

No

var=value echo whatever

Você está passando a var=valuestring para o ambiente que o eco recebe. No entanto, echonão faz nada com sua lista de ambientes e, de qualquer maneira, na maioria dos shells, echoé incorporado e, portanto, não é executado .

Se você tivesse escrito

var=value sh -c 'echo "$var"'

Isso teria sido outra questão. Aqui, estamos passando var=valuepara o shcomando e sh, por acaso, usamos seu ambiente. Os shells convertem cada uma das variáveis ​​que recebem de seu ambiente em uma variável de shell, para que a varvariável de ambiente shrecebida seja convertida em uma $varvariável e, quando a expandir nessa echolinha de comando, ela se tornará echo value. Como o ambiente é herdado por padrão, echotambém receberá var=valueem seu ambiente (ou seria se fosse executado), mas, novamente, echonão se importa com o ambiente.

Agora, se eu suspeito, o que você deseja é limitar o escopo das variáveis ​​do shell, existem várias abordagens possíveis.

Portably (Bourne e POSIX):

(var=value; echo "1: $var"); echo "2: $var"

O (...) acima inicia um sub-shell (um novo processo de shell na maioria dos shells), portanto, qualquer variável declarada lá afetará apenas esse sub-shell, portanto, espero que o código acima produza "1: value" e "2:" ou "2: o que-var-foi-definido-antes".

Com a maioria dos shells Bourne, você pode usar funções e o built-in "local":

f() {
  local var
  var=value
  echo "1: $var"
}
f
echo "2: $var"

Com o zsh, você pode usar funções embutidas:

(){ local var=value; echo "1: $var"; }; echo "2: $var"

ou:

function { local var=value; echo "1: $var"; }; echo "2: $var"

Com bash e zsh (mas não ash, pdksh ou AT&T ksh), esse truque também funciona:

var=value eval 'echo "1: $var"'; echo "2: $var"

Uma variante que funciona em mais algumas conchas ( dash, mksh, yash), mas não zsh(a menos que em sh/ kshemulação):

var=value command eval 'echo "1: $var"'; echo "2: $var"

(usar commandna frente de um built-in especial (aqui eval) em shells POSIX remove sua especialidade (aqui as atribuições de variáveis ​​delas permanecem em vigor após o retorno))

Stéphane Chazelas
fonte
Para meus propósitos, esta é a solução correta. Não quero que a variável 'var' polua o ambiente, como na resposta aceita.
kronenpj
6

Você está fazendo isso corretamente, mas a sintaxe do bash é fácil de interpretar mal: você pode pensar que isso echo $TESTfaz echocom que a busca seja TESTenv var e, em seguida, imprima-a, não é. Tão dado

export TEST=123

então

TEST=456 echo $TEST

envolve a seguinte sequência:

  1. O shell analisa toda a linha de comandos e executa todas as substituições de variáveis, para que a linha de comandos se torne

    TEST=456 echo 123
  2. Ele cria os vars temporários definidos antes do comando, para salvar o valor atual de TESTe substituí-lo por 456; a linha de comando é agora

    echo 123
  3. Ele executa o comando restante, que nesse caso imprime 123 no stdout (portanto, o comando shell que permanece permanece nem usa o valor temp de TEST)

  4. Restaura o valor de TEST

Use printenv, pois não envolve substituição de variável:

>> export TEST=123
>> printenv TEST
123
>> TEST=456 printenv TEST
456
>> printenv TEST && TEST=456 printenv TEST && TEST=789 printenv TEST && printenv TEST
123
456
789
123
>>
Oliver
fonte
+1 Eu achei printenvútil para teste / prova de conceito (se comporta como um script, em oposição a echo)
Daryn
5

Você pode fazer isso funcionar usando:

TEST=foo && echo $TEST
pradeepchhetri
fonte
6
Nesse caso, TEST=fooestá sendo executado como uma instrução separada - não é apenas configurada no contexto de echo.
codeforester