Skrypt powłoki podczas odczytu pętli linii zatrzymuje się po pierwszej linii

108

Mam następujący skrypt powłoki. Celem jest zapętlenie każdej linii pliku docelowego (którego ścieżka jest parametrem wejściowym skryptu) i wykonanie pracy z każdą linią. Teraz wygląda na to, że działa tylko z pierwszą linią w pliku docelowym i zatrzymuje się po przetworzeniu tej linii. Czy jest coś nie tak z moim skryptem?

#!/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"

W programie do_work.shuruchamiam kilka sshpoleceń.

bcbishop
źródło
1
Twój skrypt jest w porządku, ale może być coś nie tak z do_work.sh
sleepsort
2
Tak, może pochłonąć wszystkie dane wejściowe lub może zostać wywołane jako sourcei po prostu wyjść lub exec. Ale ten kod nie wygląda na prawdziwy, OP zauważyłby, że echo wymaga -eprawidłowego wyświetlania wiersza ...
Michael Krelin - haker
3
Czy do_work.shuruchomić sshprzez przypadek?
zgub do psów
1
tak, do_work.sh uruchamia kilka poleceń ssh. coś specjalnego w tym?
bcbishop
1
Lepiej pokazać do_work.shźródło, a także uruchomić do.shz set -xdebugowania.
koola

Odpowiedzi:

178

Problem polega na tym, że do_work.shuruchamia sshpolecenia i domyślnie sshczyta ze stdin, który jest twoim plikiem wejściowym. W rezultacie widzisz tylko pierwszą przetworzoną linię, ponieważ sshzużywa resztę pliku, a pętla while zostaje zakończona.

Aby temu zapobiec, przekaż -nopcję do sshpolecenia, aby czytał z /dev/nullzamiast stdin.

dogbane
źródło
1
Bardzo przydatne, pomogło mi uruchomić ten oneliner zsh: cat hosts | podczas czytania hosta; wykonaj ssh $ host do_something; gotowe
szczur
3
@rat Nadal chcesz uniknąć bezużytecznych cat. Można by pomyśleć, że szczególnie gryzoń byłby tego ostrożny.
tripleee
while read host ; do $host do_something ; done < /etc/hostsuniknąłby tego. To całkiem uratuje życie, dzięki!
szczur
httpiejest kolejnym poleceniem, które domyślnie odczytuje STDIN i będzie miało to samo zachowanie, gdy zostanie wywołane wewnątrz pętli bash lub fish. Użyj http --ignore-stdinlub ustaw standardowe wejście /dev/nulljak powyżej.
Raman
13

Bardziej ogólnie, obejściem, które nie jest specyficzne dla tego rozwiązania, sshjest przekierowanie standardowego wejścia dla dowolnego polecenia, które w przeciwnym razie mogłoby zużywać whilewejście pętli.

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

Dodanie </dev/nulljest tutaj kluczowym punktem (chociaż poprawione cytowanie jest również dość ważne; zobacz także Kiedy zawijać cudzysłowy wokół zmiennej powłoki? ). Będziesz chciał użyć, read -rchyba że wyraźnie potrzebujesz starszego, nieco dziwnego zachowania, bez którego otrzymujesz -r.

Innym rodzajem obejścia, które jest dość specyficzne, sshjest upewnienie się, że każde sshpolecenie ma powiązane standardowe wejście, np. Poprzez zmianę

ssh otherhost some commands here

zamiast tego czytać polecenia z dokumentu tutaj, który dogodnie (w tym konkretnym scenariuszu) wiąże standardowe wejście sshdla poleceń:

ssh otherhost <<'____HERE'
    some commands here
____HERE
tripleee
źródło
5

Opcja ssh -n zapobiega sprawdzaniu statusu wyjścia ssh podczas używania HEREdoc podczas przesyłania danych wyjściowych do innego programu. Dlatego preferowane jest użycie / dev / null jako 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
źródło
To nie ma sensu. <<EOFNadpisuje </dev/nullprzekierowanie. <<Przekierowanie po doneto źle.
tripleee
1

Stało się to ze mną, ponieważ miałem set -ei grepw pętli zwracano bez wyjścia (co daje niezerowy kod błędu).

JonnyRaa
źródło