Usando “while read…” em um script linux

34

Alguém poderia explicar como o código a seguir funciona?

echo '1 2 3 4 5 6' | while read a b c
do
  echo $c $b $a
done

Especificamente, eu gostaria de saber por que a saída desse loop é 3 4 5 6 2 1, em vez de 3 2 1e 6 5 4em duas linhas separadas? Parece que não consigo entender isso ...

linuxgringo
fonte

Respostas:

41

readlê uma linha inteira da entrada padrão, divide a linha em campos e atribui esses campos às variáveis ​​especificadas. Se houver mais peças do que variáveis, as peças restantes serão atribuídas à última variável.

No seu caso $aé atribuído 1, $bé atribuído 2e $co restante 3 4 5 6.

Florian Diesch
fonte
Obrigado Florian! Agora faz sentido ... Por alguma razão, pensei que os espaços delimitariam cada leitura de variável, mas aparentemente não. Eu aprecio sua ajuda!!
Linuxgringo
24

Reescrever o loop dessa maneira revela o que está acontecendo:

echo '1 2 3 4 5 6' | while read a b c
  do
    echo '(iteration beginning)' a="$a" b="$b" c="$c" '(iteration ending)'
  done

Isso fornece, como saída:

(iteration beginning) a=1 b=2 c=3 4 5 6 (iteration ending)

Observe primeiro que apenas um único comando de eco é executado. Se fosse executado mais de uma vez, você veria, entre outras coisas, as substrings (iteration beginning)e (iteration ending)impressas mais de uma vez.

Isso significa que ter um whileloop aqui não está realmente conseguindo nada. O readbuiltin lê o texto 1 separado por espaço em branco em cada variável especificada. A entrada extra é anexada ao final da última variável especificada. 2 Assim, variáveis ae bassumir os valores 1e 2, respectivamente, enquanto que cassume o valor 3 4 5 6.

Quando a condição do loop ( while read a b c) é avaliada pela segunda vez, não há mais entrada disponível no canal (nós apenas canalizamos uma única linha de texto); portanto, o readcomando é avaliado como falso em vez de verdadeiro e o loop é interrompido (antes de executar o corpo uma segunda vez).

1 : Para ser técnico e específico, o readbuilt-in , quando recebe nomes de variáveis ​​como argumentos, lê a entrada, dividindo-a em "palavras" separadas quando encontra o espaço em branco do IFS (consulte também esta pergunta e este artigo ).

readO comportamento do 2 : de obstruir qualquer campo extra de entrada na última variável especificada não é intuitivo para muitos scripts, a princípio. Torna-se mais fácil entender quando você considera que, como diz a resposta de Florian Diesch , readsempre (tentará) ler uma linha inteira - e isso readse destina a ser utilizável com e sem um loop.

Eliah Kagan
fonte
Eliah, obrigado por dedicar um tempo para explicar todos os detalhes. Eu suspeitava que whilenão servir o seu propósito normal neste exemplo, mas em seguida, o readcomando jogou-me fora ... De alguma forma, eu interpretei-o como "enquanto read a b cnão é falsa, faça echo ...". Obrigado por explicar como realmente funcionou. Me deparei com este código de ontem e sabia que iria me incomodar até que eu percebi isso ... lol
linuxgringo
@linuxgringo Na verdade, o corpo do loop é executado toda vez que é read a b cavaliado como verdadeiro, e a condição do loop ( read a b c) é executada mais de uma vez. Bit avalia apenas como verdadeiro na 1ª vez. Na segunda vez, não há mais entrada a ser lida no canal, portanto, o final do arquivo é encontrado, causando readretorno falso . (Consulte a última seção da saída de help read, em "Status de saída", para obter detalhes, observando que, nos scripts de shell, zero significa verdadeiro e diferente de zero significa falso.) Se você canalizou mais de uma linha de entrada while read ..., o corpo do loop seria executado várias vezes.
Elias Kagan 04/04