Shell script enquanto o loop de linha de leitura para após a primeira linha

107

Eu tenho o seguinte script de shell. O objetivo é percorrer cada linha do arquivo de destino (cujo caminho é o parâmetro de entrada para o script) e trabalhar em cada linha. Agora, parece funcionar apenas com a primeira linha no arquivo de destino e para depois que essa linha foi processada. Há algo de errado com meu script?

#!/bin/bash
# SCRIPT: do.sh
# PURPOSE: loop thru the targets 

FILENAME=$1
count=0

echo "proceed with $FILENAME"

while read LINE; do
   let count++
   echo "$count $LINE"
   sh ./do_work.sh $LINE
done < $FILENAME

echo "\ntotal $count targets"

Em do_work.sh, executo alguns sshcomandos.

bispo
fonte
1
Seu script está bom, mas pode haver algo errado com do_work.sh
sleepsort
2
Sim, ele pode consumir todas as entradas ou pode ser chamado como sourcee simplesmente sair ou exec. Mas este código não parece genuíno, o OP notaria que o echo requer -epara exibir o feed de linha corretamente ...
Michael Krelin - hacker
3
Será do_work.shexecutado sshpor acaso?
dogbane de
1
sim, do_work.sh executa alguns comandos ssh. algo de especial sobre isso?
bcbishop
1
Melhor você mostrar o do_work.shcódigo fonte e também rodar do.shcom set -xpara depurar.
Koola

Respostas:

178

O problema é que do_work.shexecuta sshcomandos e, por padrão, sshlê stdin, que é o seu arquivo de entrada. Como resultado, você só vê a primeira linha processada, porque sshconsome o resto do arquivo e seu loop while termina.

Para evitar isso, passe a -nopção para o seu sshcomando de torná-lo lido em /dev/nullvez de stdin.

dogbane
fonte
1
Muito útil, me ajudou a rodar este zsh oneliner: cat hosts | enquanto lê o host; fazer ssh $ host do_something; feito
rato
3
@rat Você ainda quer evitar o inútil cat. Você pensaria que um roedor em particular desconfiaria disso.
tripleee
while read host ; do $host do_something ; done < /etc/hostsiria evitá-lo. Isso é um salva-vidas e tanto, obrigado!
rato
httpieé outro comando que lê STDIN por padrão e terá o mesmo comportamento quando chamado dentro de um bash ou fish loop. Use http --ignore-stdinou defina a entrada padrão /dev/nullcomo acima.
Raman
12

De forma mais geral, uma solução alternativa que não é específica para sshé redirecionar a entrada padrão para qualquer comando que, de outra forma, poderia consumir a whileentrada do loop.

while read -r LINE; do
   let count++
   echo "$count $LINE"
   sh ./do_work.sh "$LINE" </dev/null
done < "$FILENAME"

A adição de </dev/nullé o ponto crucial aqui (embora as aspas corrigidas também sejam um pouco importantes; consulte também Quando envolver aspas em uma variável de shell? ). Você vai querer usar, a read -rmenos que exija especificamente o comportamento legado ligeiramente estranho que você consegue sem -r.

Outra solução alternativa que é um tanto específica sshé garantir que qualquer sshcomando tenha sua entrada padrão ligada, por exemplo, alterando

ssh otherhost some commands here

em vez disso, leia os comandos de um documento here, que convenientemente (para este cenário específico) vincula a entrada padrão de sshpara os comandos:

ssh otherhost <<'____HERE'
    some commands here
____HERE
triplo
fonte
5

A opção ssh -n evita a verificação do status de saída do ssh ao usar HEREdoc enquanto canaliza a saída para outro programa. Portanto, é preferível usar / dev / null como stdin.

#!/bin/bash
while read ONELINE ; do
   ssh ubuntu@host_xyz </dev/null <<EOF 2>&1 | filter_pgm 
   echo "Hi, $ONELINE. You come here often?"
   process_response_pgm 
EOF
   if [ ${PIPESTATUS[0]} -ne 0 ] ; then
      echo "aborting loop"
      exit ${PIPESTATUS[0]}
   fi
done << input_list.txt
jacobm654321
fonte
Isso não faz sentido. O <<EOFsubstitui o </dev/nullredirecionamento. O <<redirecionamento após o doneestá errado.
tripleee
1

Isso estava acontecendo comigo porque eu tinha set -ee um grepem um loop estava retornando sem saída (o que dá um código de erro diferente de zero).

JonnyRaa
fonte