Um loop while e um aqui-documento - o que acontece quando?

6

Eu tenho esse loop while e o combo here-document que eu executo no Bash 4.3.48 (1) e eu não entendo sua lógica.

while read file; do source ~/unwe/"$file"
done <<-EOF
    x.sh
    y.sh
EOF

A minha pergunta é composta por estas partes:

  1. O que o read faz aqui (sempre uso read para declarar uma variável e atribuir seu valor interativamente , mas não sei o que fazer aqui) .

  2. Qual é o significado de while read ? Onde o conceito de while entra aqui?

  3. Se o próprio documento aqui vier após o loop, como ele é afetado pelo loop? Quero dizer, vem depois de done , e não dentro do loop, então qual é a associação real entre essas duas estruturas?

  4. Por que isso falha?

    while read file; do source ~/unwe/"$file" done <<-EOF
        x.sh
        y.sh
    EOF
    

    Quero dizer, done é done ... Então, por que importa se done <<-EOF estiver na mesma linha que o loop? Se bem me lembro, eu tive um caso em que um loop for era one-liner e ainda funcionava.

por user9303970 07.02.2018 / 16:05

2 respostas

12
  1. O comando read lê seu fluxo de entrada padrão e atribui o que é lido à variável file (é um pouco mais complicado do que isso, consulte longa discussão aqui ). O fluxo de entrada padrão vem do documento aqui redirecionado para o loop após done . Se não for dado dados de qualquer lugar , ele lerá do terminal, interativamente. Neste caso, porém, o shell conseguiu conectar seu fluxo de entrada ao documento here.

  2. while read fará com que o loop faça uma iteração até que o comando read retorne um status de saída diferente de zero. Isso acontecerá se houver algum erro ou (mais comumente) quando não houver mais dados a serem lidos (seu fluxo de entrada está em um estado de fim de arquivo).

    A convenção é que qualquer utilitário que deseje sinalizar um erro ou "falso" ou "não" para o shell de chamada faz isso retornando um status de saída diferente de zero. Um status de saída zero sinaliza "true" ou "yes" ou "sem erro". Este status, você gostaria de inspecioná-lo, está disponível em $? (somente a partir do último utilitário executado). O status de saída pode ser usado em if declarações e while loops ou em qualquer lugar onde um teste é necessário. Por exemplo

    if grep -q 'pattern' file; then ...; fi
    
  3. Um documento aqui é uma forma de redirecionamento . Neste caso, é um redirecionamento para o loop. Qualquer coisa dentro do loop poderia ler a partir dele, mas neste caso é apenas o comando read que faz. Leia os documentos aqui. Se a entrada estava vindo de um arquivo comum, a última linha teria sido

    done <filename
    

    Ver o loop como um único comando pode tornar isso mais intuitivo:

    while ...; do ...; done <filename
    

    que é um caso de

    somecommand <filename
    

    Algumas shells também suportam "here-strings" com <<<"string" :

    cat <<<"This is the here-string"
    

    DavidFoerster aponta que se qualquer um dos dois scripts x.sh e y.sh ler a entrada padrão, sem explicitamente receber dados para ler de um arquivo ou de outro lugar, os dados lidos na verdade virão daqui -documento.

    Com um x.sh que contém apenas read a , isso faria com que a variável a contivesse a string y.sh e o script y.sh nunca fosse executado. Isso se deve ao fato de que a entrada padrão é redirecionada para os comandos all no loop while (e também "herdada" por qualquer script ou comando invocado) e a segunda linha é "consumida" por x.sh antes que o while do read possa lê-lo.

    Se esse comportamento não for desejado, ele poderá ser evitado, mas é um pouco complicado .

  4. Falha porque não há ; ou nova linha antes de done . Sem ; ou newline antes de done , a palavra done será considerada como um argumento de source , e o loop também não será fechado adequadamente (isso é um erro de sintaxe).

    É quase verdade que qualquer ; pode ser substituído por uma nova linha (pelo menos quando é um delimitador de comando). Ele sinaliza o final de um comando, assim como | , & , && e || (e provavelmente outros que eu esqueci).

por 07.02.2018 / 16:21
2

Você está, na verdade, criando um objeto como um documento e, em seguida, redirecionando-o de volta para um loop while que o lê linha por linha até que não haja mais linhas, executando um comando de origem nessa linha.

Outra maneira de executá-lo é a seguinte:

Supondo que as linhas

x.sh
y.sh

estavam em um arquivo chamado:

input.txt

Poderíamos executar

cat input.txt | while read file
   do source ~/unwe/"$file"
done

O documento aqui torna as coisas mais interativas, mas o exemplo acima pode permitir que você entenda melhor.

    
por 07.02.2018 / 16:21