valor temporário do script bash sob comando

11

Como abaixo do comando,

if true; then
   IFS=":" read a b c d e f <<< "$test"

O livro diz que quando o comando de atribuição de valor ( IFS ":") é usado antes do comando principal ( read a b c d e f <<< "$value"), seu valor é efetivo temporariamente no comando principal. Portanto, o readcomando usa delimitador :.

Mas, como este comando,

if true; then
   HOME="hello" echo "$HOME"

A mensagem de eco não é um alô. Qual é o verdadeiro significado do comando acima?

A.Cho
fonte

Respostas:

5

Isso se resume a uma questão de como a avaliação funciona. Ambos os exemplos funcionam da mesma maneira, o problema ocorre devido à forma como o shell (bash, aqui) expande variáveis.

Quando você escreve este comando:

HOME="foo" echo $HOME

O $HOMEé expandido antes da execução do comando . Portanto, ele é expandido para o valor original e não o novo valor definido para o comando. A HOMEvariável foi realmente alterada no ambiente em que o echocomando está sendo executado; no entanto, você está imprimindo a $HOMEpartir do pai.

Para ilustrar, considere o seguinte:

$ HOME="foo" bash -c 'echo $HOME'
foo
$ echo $HOME
/home/terdon

Como você pode ver acima, o primeiro comando imprime o valor alterado temporariamente HOMEe o segundo imprime o original, demonstrando que a variável foi alterada apenas temporariamente. Como o bash -c ...comando está entre aspas simples ( ' ') em vez de aspas duplas ( " "), a variável não é expandida e é passada como está para o novo processo do bash. Esse novo processo o expande e imprime o novo valor para o qual foi definido. Você pode ver isso acontecer se você usar set -x:

$ set -x
$ HOME="hello" echo "$HOME"
+ HOME=hello         
+ echo hello
hello

Como você pode ver acima, a variável $HOME nunca é passada para echo. Ele vê apenas seu valor expandido. Compare com:

$ HOME="hello" bash -c 'echo $HOME'
+ HOME=hello
+ bash -c 'echo $HOME'
hello

Aqui, devido às aspas simples, a variável e não seu valor são passadas para o novo processo.

terdon
fonte
7

Quando o shell estiver analisando uma linha, ele irá tokenizar a linha em palavras, executar várias expansões (em ordem) nas palavras e executar o comando.

Suponha test=1:2:3:4:5:6

Vamos olhar para este comando: IFS=":" read a b c d e f <<< "$test"

Após a tokenização, ocorre a expansão dos parâmetros :IFS=":" read a b c d e f <<< "1:2:3:4:5:6"

O shell definirá a variável IFS para a duração do comando read e readsabe como aplicar $ IFS à sua entrada e fornecer valores aos nomes das variáveis.

Este comando tem uma história semelhante, mas um resultado diferente: HOME="hello" echo "$HOME"

Como a expansão do parâmetro ocorre antes do início do comando, o shell possui:

HOME="hello" echo "/home/username"

E então, durante a execução do comando echo, o novo valor de $ HOME não é usado.

Para conseguir o que você está tentando fazer, escolha um dos

# Delay expansion of the variable until its new value is set
HOME="hello" eval 'echo "$HOME"'

ou

# Using a subshell, so the altered env variable does not affect the parent.
# The semicolon means that the variable assignment will occur before
# the variable expansion
(HOME="hello"; echo "$HOME")

mas não escolha o primeiro.

Glenn Jackman
fonte
Provavelmente é melhor escolher o primeiro. Pelo menos é significativamente mais rápido. Quando eval é a resposta, às vezes você provavelmente está fazendo a pergunta errada. Mas se alguém deve fazer isso por alguns motivos, alterar a resposta não torna a pergunta em si menos errada. Outra solução é envolvê-lo em uma função e uso local.
user23013
Por que a evalsolução deve ser evitada?
DarkHeart 29/02
Se você não controla estritamente a entrada, você deve ter muito cuidado com o código que permite que outras pessoas injetem no seu programa.
Glenn Jackman
-1

Existem dois escopos: variáveis ​​de ambiente e variáveis ​​locais. As variáveis ​​de ambiente são válidas para todos os processos (consulte setenv, getenv), enquanto as variáveis ​​locais estão ativas apenas nesta sessão do shell. (Não é uma distinção óbvia ...)

Implícito env(como no seu exemplo) modifica o ambiente, enquanto echo ...usa os locais - portanto env, não tem efeito.

Para modificar as variáveis ​​locais, use, digamos,

( HOME="foo" ; echo "$HOME" )

Aqui os parênteses definem o escopo dessa atribuição.

Andrew Miloradovsky
fonte
1
Isso não tem nada a ver com o escopo da variável, o problema é que a variável está sendo expandida antes de ser passada para o shell filho.
terdon