Сценарий оболочки при чтении цикла строки останавливается после первой строки

108

У меня есть следующий сценарий оболочки. Цель состоит в том, чтобы пройти через каждую строку целевого файла (путь которого является входным параметром скрипта) и выполнить работу с каждой строкой. Теперь, похоже, работает только с самой первой строкой в ​​целевом файле и останавливается после обработки этой строки. Что-то не так с моим сценарием?

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

В do_work.sh, я запускаю пару sshкоманд.

епископ
источник
1
Ваш сценарий в порядке, но может быть что-то не так с do_work.sh
sleepsort
2
Да, он может поглотить весь ввод, или он может быть вызван как sourceи просто выйти или exec. Но этот код не выглядит подлинным, OP заметит, что эхо требует -eправильного отображения перевода строки ...
Майкл Крелин - хакер
3
Есть ли do_work.shзапустить sshлюбой шанс?
dogbane
1
да, do_work.sh запускает пару команд ssh. что-нибудь особенное в этом?
bcbishop
1
Лучше вы показать do_work.shисточник , а также работать do.shс set -xотлаживать.
koola

Ответы:

178

Проблема в том, что do_work.shзапускает sshкоманды и по умолчанию sshчитает из stdin, который является вашим входным файлом. В результате вы видите только обработанную первую строку, потому что sshпотребляет остальную часть файла, и ваш цикл while завершается.

Чтобы предотвратить это, передайте -nсвоей sshкоманде параметр, чтобы она читалась из /dev/nullстандартного ввода.

кендырь
источник
1
Очень полезно, помогло мне запустить этот zsh oneliner: cat hosts | пока читал хост; сделать ssh $ host do_something; done
rat
3
@rat Вы все еще хотите избежать бесполезного cat. Можно подумать, что этого особенно опасаются грызуны.
Tripleee
while read host ; do $host do_something ; done < /etc/hostsизбежал бы этого. Это спасает жизнь, спасибо!
rat
httpie- это еще одна команда, которая по умолчанию читает STDIN и будет иметь такое же поведение при вызове внутри цикла bash или fish. Используйте http --ignore-stdinили установите стандартный ввод, /dev/nullкак указано выше.
Raman
13

В более общем плане обходной путь, который не является специфическим, sshзаключается в перенаправлении стандартного ввода для любой команды, которая в противном случае могла бы использовать whileввод цикла.

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

</dev/nullВажнейшим моментом здесь является добавление (хотя исправленное цитирование также в некоторой степени важно; см. Также Когда заключать кавычки в переменную оболочки? ). Вы захотите использовать, read -rесли вам специально не требуется устаревшее немного странное поведение, без которого вы получаете -r.

Другой способ обхода, который несколько специфичен, ssh- убедиться, что у любой sshкоманды есть привязанный стандартный ввод, например, путем изменения

ssh otherhost some commands here

вместо этого читать команды из документа здесь, который удобно (для этого конкретного сценария) связывает стандартный ввод sshдля команд:

ssh otherhost <<'____HERE'
    some commands here
____HERE
тройной
источник
5

Параметр ssh -n предотвращает проверку статуса выхода ssh при использовании HEREdoc при передаче вывода в другую программу. Поэтому предпочтительнее использовать / dev / null в качестве 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
источник
В этом нет смысла. <<EOFОтменяет </dev/nullпереназначение. <<Перенаправление после того , как doneэто не так.
tripleee
1

Это происходит со мной , потому что я set -eи grepв петле возвращается без выхода (который дает ненулевой код ошибки).

JonnyRaa
источник