Сценарій оболонки, коли цикл читання рядків зупиняється після першого рядка


107

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

#!/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. Але цей код не виглядає справжнім, ОП помітить, що відлуння вимагає -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споживає решту файлу, а цикл у той час закінчується.

Щоб цього не допустити, передайте -nпараметр вашій sshкоманді, щоб змусити її читати /dev/nullзамість stdin.


1
Дуже корисно, допоміг мені запустити цей zsh oneliner: cat hosts | під час читання хоста; робити ssh $ host do_something; зроблено
щур

3
@rat Ви все ще хочете уникати марного cat. Ви можете подумати, що гризун особливо обережно ставиться до цього.
трійчатка

while read host ; do $host do_something ; done < /etc/hostsуникав би цього. Це досить рятівник життя, дякую!
щур

httpie- це ще одна команда, яка читає STDIN за замовчуванням і буде страждати від тієї ж поведінки, коли викликається всередині bash або fish циклу. Використовуйте http --ignore-stdinабо встановіть стандартний вхід, /dev/nullяк зазначено вище.
Раман

12

Більш загальним рішенням, яке не є специфічним, 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

Це не має сенсу. <<EOFСкасовує </dev/nullперепризначення. <<Перенаправлення після того , як doneце не так.
трійчатка

1

Це сталося зі мною, тому що я мав set -eі grepцикл повертався без виводу (що дає ненульовий код помилки).

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.