imprime uma linha vazia. Por que não 10? Por que a configuração de ambiente temporário no local não funciona?
Quero saber o motivo e a explicação, e não a solução.
eu usei
LANG=C gcc ...
para forçar o gcc, use o idioma de fallback (inglês) em vez do idioma do sistema (chinês). Portanto, presumo que um VAR=valueprefixo configure um ambiente temporário para o comando a seguir. Mas parece que tenho algum mal-entendido.
É uma questão de ordem em que ocorrem as diferentes etapas da avaliação de um comando.
A=10 echo $Aprimeiro analisa o comando em um comando simples composto por três palavras A=10, echoe $A. Então cada palavra passa por substituição de variável, ou seja, a transformação de expansões variáveis, como $Aem seus valores (estou omitindo etapas que não fazem nada visível).
Se Atem o valor fooinicialmente, o resultado dos passos de expansão é um comando simples que ainda tem três palavras: A=10, echoe foo. (O shell também se lembra nesse momento de quais caracteres estavam inicialmente entre aspas - nesse caso, nenhum.) O próximo passo é executar o comando. Como A=10começa com um nome de variável válido seguido por um sinal de igual, é tratado como uma atribuição; a variável Aé configurada 10no shell e no ambiente durante a execução do comando. (Normalmente, você precisa escrever export Apara ter Ano ambiente e não apenas como uma variável de shell; isso é uma exceção.) A próxima palavra não é uma atribuição, portanto é tratada como um nome de comando (é um comando interno). oechoO comando não depende de nenhuma variável, portanto, A=10 echo $Atem exatamente o mesmo efeito que echo $A.
Se você deseja definir uma variável apenas para a duração de um comando, mas levando em consideração a atribuição durante a execução do comando, é possível usar um subshell. Um subshell, indicado entre parênteses, torna todas as alterações de estado (atribuições de variáveis, diretório atual, definições de funções etc.) locais no subshell.
(A=10; echo $A)
Faça isso export A=10se desejar exportar a variável para o ambiente para que ela seja vista por programas externos.
@EarthEngine Não, isso seria um erro de sintaxe. A atribuição deve estar no início de um comando simples (ou seja, apenas um nome de comando e alguns parâmetros e, opcionalmente, algumas atribuições iniciais e alguns redirecionamentos). A=10; (echo $A)saídas, 10mas também define Ao restante do script.
Gilles 'SO- stop be evil'
2
@EarthEngine Mas você pode dizer A=10 eval 'echo $A'. As aspas simples param de $Aser interpretadas até que toda a linha seja avaliada ... Nesse momento A = 10. Considero esta resposta mais correta do que a aceita.
Oli
Eu acho que essa é a explicação correta. A razão para o comportamento é exatamente a ordem de quando a expansão $Ae a atribuição de Aestão acontecendo. Por exemplo, A=5; A=6 let 'a=A'; echo $aretorna 6não 5e acho que não letinicia um subshell, pois é um comando interno.
David Ongaro
@EarthEngine: Quando se diz que a explicação correta é a ordem da avaliação, pode ser enganoso: nãoA=10 echo $A será definido para nenhum comando posteriormente, mesmo se eles estiverem em linhas diferentes (quando claramente a tarefa já foi avaliada). Não se trata de ordem, é de escopoA=10
MestreLion 30/05
37
Quando você usa LANG=C gcc ... o que acontece é que o shell define LANG para gcco ambiente da única , e não para o atual ambiente em si ( ver nota ). Portanto, após a gccconclusão, LANGvolta ao seu valor anterior (ou não definido).
Além disso, quando você o usa A=10 echo $A, é o shell que substitui $ A, não eco, e essa substituição (chamada "expansão") ocorre antes da avaliação da instrução (incluindo a atribuição), portanto, para funcionar como Ao valor esperado já deve estar definido no ambiente atual anterior a essa declaração.
É por isso A=10 echo $Aque não funciona conforme o esperado: A=10será definido para eco, mas o eco ignora internamente o valor da variável de ambiente A. E $Aé substituído pelo valor definido no shell atual (que não é nenhum) e depois passado como argumento para eco.
Portanto, o seu pressuposto é correto: VAR=value commandfaz o trabalho, mas isso só é relevante se commandinternamente usa VAR. Caso contrário, você ainda pode passar valuecomo argumento para command, mas os argumentos são substituídos pelo shell atual ; portanto, eles devem ser definidos antes do uso:VAR=value; command "$VAR"
Se você sabe como criar um script executável, tente isso como um teste:
#!/bin/sh
echo "1st argument is $1"
echo "A is $A"
Salve-o como testscripte tente:
$ A=5; A=10 testscript "$A"; echo "$A"1st argument is 5
A is 105
Por último, mas não menos importante, vale a pena conhecer a diferença entre as variáveis shell e de ambiente e os argumentos do programa .
(*) Nota: tecnicamente, o shell não definida no ambiente atual também, e aqui está o porquê: Alguns comandos, como echo, reade testsão builtins shell , e como tal eles não gerar um processo filho. Eles são executados no ambiente atual. Mas o shell cuida da tarefa apenas durando até que o comando seja executado; portanto, para todos os efeitos práticos, o efeito é o mesmo: a tarefa é vista apenas por esse único comando.
Essa explicação está realmente incorreta, embora resulte na conclusão correta em todos os casos, exceto em alguns casos extremos. A explicação real é a ordem da expansão: $Aé avaliada antes da atribuição. Eu acho que sua explicação falha apenas no caso de utilitários internos regulares cujo comportamento depende do valor da variável: o interno vê o valor atribuído. Um exemplo comum é o seguinte IFS=: read one two three rest: lê campos separados por dois pontos: o readbuilt-in vê o valor de IFS.
Gilles 'SO- stop be evil'
“Não para o próprio shell atual” está errado: a variável é definida no shell atual, mas dura apenas o comando simples atual. echoveria o valor 10para A, se ele se importava.
Gilles 'SO- stop be evil'
@ Gilles: muito obrigado pelo esclarecimento! Eu não estava ciente dessa sutileza. Portanto, se eu entendi corretamente, o bash precisa definir para o ambiente atual , caso contrário, os componentes internos (que não geram um novo pid) não verão a atribuição como outros comandos "regurlar". Mas ele é desabilitado após a execução do comando para limitar o escopo da atribuição. Isso está correto? Corrigirei minha resposta de acordo. PS: técnicos de lado, eu ainda acho que uma resposta deve enfatizar o aspecto escopo, não ordem de avaliação, caso contrário, pode-se pensar A=10 test; echo $Airá imprimir 10
MestreLion
3
Uma maneira possivelmente limpa de fazer o que você aparentemente deseja é emitir o comando:
A=10eval'echo $A'
O que, de fato, adiará a substituição do valor 10 para o lugar de $ A para um contexto posterior (ou seja, 'dentro' da avaliação, que já conhece a atribuição). Observe que as aspas simples são essenciais. Essa construção comunica claramente a atribuição ao comando desejado (eco neste caso) sem correr o risco de poluir o ambiente atual.
A=10 (echo $A)
e receber10
?A=10; (echo $A)
saídas,10
mas também defineA
o restante do script.A=10 eval 'echo $A'
. As aspas simples param de$A
ser interpretadas até que toda a linha seja avaliada ... Nesse momento A = 10. Considero esta resposta mais correta do que a aceita.$A
e a atribuição deA
estão acontecendo. Por exemplo,A=5; A=6 let 'a=A'; echo $a
retorna6
não5
e acho que nãolet
inicia um subshell, pois é um comando interno.A=10 echo $A
será definido para nenhum comando posteriormente, mesmo se eles estiverem em linhas diferentes (quando claramente a tarefa já foi avaliada). Não se trata de ordem, é de escopoA=10
Quando você usa
LANG=C gcc ...
o que acontece é que o shell define LANG paragcc
o ambiente da única , e não para o atual ambiente em si ( ver nota ). Portanto, após agcc
conclusão,LANG
volta ao seu valor anterior (ou não definido).Além disso, quando você o usa
A=10 echo $A
, é o shell que substitui $ A, não eco, e essa substituição (chamada "expansão") ocorre antes da avaliação da instrução (incluindo a atribuição), portanto, para funcionar comoA
o valor esperado já deve estar definido no ambiente atual anterior a essa declaração.É por isso
A=10 echo $A
que não funciona conforme o esperado:A=10
será definido para eco, mas o eco ignora internamente o valor da variável de ambienteA
. E$A
é substituído pelo valor definido no shell atual (que não é nenhum) e depois passado como argumento para eco.Portanto, o seu pressuposto é correto:
VAR=value command
faz o trabalho, mas isso só é relevante secommand
internamente usa VAR. Caso contrário, você ainda pode passarvalue
como argumento paracommand
, mas os argumentos são substituídos pelo shell atual ; portanto, eles devem ser definidos antes do uso:VAR=value; command "$VAR"
Se você sabe como criar um script executável, tente isso como um teste:
Salve-o como
testscript
e tente:Por último, mas não menos importante, vale a pena conhecer a diferença entre as variáveis shell e de ambiente e os argumentos do programa .
Aqui estão algumas boas referências:
Como ler e definir variáveis ambientais e de shell em um VPS Linux
Noções básicas sobre shells Unix e variáveis de ambiente
.
(*) Nota: tecnicamente, o shell não definida no ambiente atual também, e aqui está o porquê: Alguns comandos, como
echo
,read
etest
são builtins shell , e como tal eles não gerar um processo filho. Eles são executados no ambiente atual. Mas o shell cuida da tarefa apenas durando até que o comando seja executado; portanto, para todos os efeitos práticos, o efeito é o mesmo: a tarefa é vista apenas por esse único comando.fonte
$A
é avaliada antes da atribuição. Eu acho que sua explicação falha apenas no caso de utilitários internos regulares cujo comportamento depende do valor da variável: o interno vê o valor atribuído. Um exemplo comum é o seguinteIFS=: read one two three rest
: lê campos separados por dois pontos: oread
built-in vê o valor deIFS
.echo
veria o valor10
paraA
, se ele se importava.pid
) não verão a atribuição como outros comandos "regurlar". Mas ele é desabilitado após a execução do comando para limitar o escopo da atribuição. Isso está correto? Corrigirei minha resposta de acordo. PS: técnicos de lado, eu ainda acho que uma resposta deve enfatizar o aspecto escopo, não ordem de avaliação, caso contrário, pode-se pensarA=10 test; echo $A
irá imprimir 10Uma maneira possivelmente limpa de fazer o que você aparentemente deseja é emitir o comando:
O que, de fato, adiará a substituição do valor 10 para o lugar de $ A para um contexto posterior (ou seja, 'dentro' da avaliação, que já conhece a atribuição). Observe que as aspas simples são essenciais. Essa construção comunica claramente a atribuição ao comando desejado (eco neste caso) sem correr o risco de poluir o ambiente atual.
fonte