Por que uma variável global é definida com 'read variable' dentro de um loop 'while', mas local quando definida com 'while read variable'?

0
unset myVariable i;
while [ -z "$i" ]; do
    read myVariable;
    echo "myVariable : '$myVariable'";
    i=foo;
done;
echo "myVariable : '$myVariable'"

(o unset está lá para permitir a repetição do comando)

pressione qualquer tecla + ENTER, você terá:

myVariable : '[what you typed]'
myVariable : '[what you typed]'

O valor de myVariable existe fora do loop while . Agora tente:

tmpFile=$(mktemp);
echo -e 'foo\nbar\nbaz' >> "$tmpFile"
while read myVariable; do
    otherVariable=whatever; 
    echo "myVariable : '$myVariable', otherVariable : '$otherVariable'";
done < "$tmpFile";
echo "myVariable : '$myVariable', otherVariable : '$otherVariable'";
rm "$tmpFile"

você terá:

myVariable : 'foo', otherVariable : 'whatever'
myVariable : 'bar', otherVariable : 'whatever'
myVariable : 'baz', otherVariable : 'whatever'
myVariable : '', otherVariable : 'whatever'

O valor de myVariable é perdido ao sair do loop.

Por que existe um comportamento diferente? Existe um truque de escopo que eu não conheço?

NB: executando o bash GNU, versão 4.4.12 (1) -release (x86_64-pc-linux-gnu)

    
por Httqm 21.08.2018 / 15:05

3 respostas

5
while read myVariable; do

The value of myVariable is lost when leaving the loop.

Não, myVariable tem o valor obtido do último read . O script lê o arquivo, até chegar à posição após a última nova linha. Depois disso, a chamada read final não recebe nada do arquivo, define myVariable para a sequência vazia e sai com um valor falso, pois não viu o delimitador (nova linha). Então o loop termina.

Você pode obter um valor não-vazio do read final se houver uma linha incompleta após a última nova linha:

$ printf 'abc\ndef\nxxx' | 
    { while read a; do echo "got: $a"; done; echo "end: $a"; } 
got: abc
got: def
end: xxx

Ou use while read a || [ "$a" ]; do ... para manipular o fragmento de linha final dentro do corpo do loop.

    
por 21.08.2018 / 15:55
-1

Because you're piping into the while loop, a sub shell is created to run the while loop. Now this child process has it's own copy of the environment and can't pass any variables back to its parent (as in any unix process).

Nesse caso, o redirecionamento done < "$tmpFile" força o bash a gerar um sub-shell (como para o pipe).

Citado no escopo da variável bash no Stack Overflow.

    
por 21.08.2018 / 16:17
-1

Seu segundo exemplo usa um loop while que tem uma entrada redirecionada.

Ele lê usando < "$tmpFile" e muitos shells criam um sub-shell para este caso. Você pode tentar executar esse script com ksh93 . ksh93 não cria um sub-shell neste caso.

No seu caso específico, o motivo é completamente diferente:

  • no primeiro exemplo, você lê uma linha da entrada

  • no segundo exemplo, você lê até que EOF seja alcançado

O comando read lê a entrada e, em seguida, divide a entrada em IFS caracteres e, em seguida, atribui palavras aos argumentos variáveis de read .

Se houver mais palavras do que variáveis como parâmetros no comando read , a última variável obterá uma concatenação do restante das palavras.

Se houver menos palavras que variáveis, as outras variáveis recebem um valor vazio.

Como você atingiu EOF , não tem nenhuma palavra lida, mas uma ou mais variáveis como argumento para read . Isso faz com que todas as variáveis recebam um valor vazio atribuído.

Então, algo aconteceu que você não esperava: EOF faz com que o loop while seja encerrado e você não verá nenhum comando echo dentro do loop while , mas somente o comando final echo após o while loop neste caso EOF .

Este% final echo agora exibe o conteúdo variável que foi removido de EOF .

    
por 21.08.2018 / 16:36