тайм-аут вызывает, когда цикл чтения заканчивается, когда `cat` отключен

Я не могу понять, почему timeout в вызове функции приведет к остановке цикла. У меня есть «решение», но я действительно очень заинтригован, как / почему это происходит! Кажется, что что-то связано с тем, что cat стала таймаутом команды?

TL; DR

while read -r line; do ... done < file while read -r line; do ... done < file завершается, когда на cat происходит timeout , создавая неверный код выхода и выхода. Цикл не пересекает каждую строку в файле.

Если вместо этого массив создается в первую очередь строками в файле, а затем ... выполняется в for line in "${all_lines[@]}"; do for line in "${all_lines[@]}"; do , все строки обрабатываются и вывод timeout относительно кодов выхода верен.


Предположим, скрипт grade.sh намеревается прочитать все tests.txt и выполнить soln.sh , убедившись, что soln.sh завершается. Чтобы продемонстрировать «рабочий» пример, soln.sh сначала будет sleep .

tests.txt

 first second third fourth fifth 

grade.sh

 #!/usr/bin/env bash while read -r line; do echo "Test: $line" output="$(timeout 2 ./soln.sh "$line")" timed_exit=$? echo " Soln Output: $output" echo " Timed exit: $timed_exit" done < "tests.txt" 

soln.sh

 #!/usr/bin/env bash if [[ "$1" == "third" ]]; then sleep 3 fi echo "[soln running $1]" 

ожидаемый результат

 Test: first Soln Output: [soln running first] Timed exit: 0 Test: second Soln Output: [soln running second] Timed exit: 0 Test: third Soln Output: Timed exit: 124 Test: fourth Soln Output: [soln running fourth] Timed exit: 0 Test: fifth Soln Output: [soln running fifth] Timed exit: 0 

Если мы soln на то, что будет продолжаться вечно (ожидая ввода), цикл заканчивается

soln.sh

 #!/usr/bin/env bash if [[ "$1" == "third" ]]; then cat $(find . -name iamnothere.txt) | wc -l fi echo "[soln running $1]" 

выход завершается раньше, дополнительно 2 , неправильный код exit

 Test: first Soln Output: [soln running first] Timed exit: 0 Test: second Soln Output: [soln running second] Timed exit: 0 Test: third Soln Output: 2 [soln running third] Timed exit: 0 

Hacky fix – сначала прокручивать каждую строку и использовать цикл for который будет обходить это.

"fixed" grade.sh

 #!/usr/bin/env bash all_lines=() idx=0 while read -r line; do all_lines[idx]="$line" (( idx++ )) done < "tests.txt" for line in "${all_lines[@]}"; do echo "Test: $line" output="$(timeout 2 ./soln.sh "$line")" timed_exit=$? echo " Soln Output: $output" echo " Timed exit: $timed_exit" done 

ожидаемый результат

 Test: first Soln Output: [soln running first] Timed exit: 0 Test: second Soln Output: [soln running second] Timed exit: 0 Test: third Soln Output: Timed exit: 124 Test: fourth Soln Output: [soln running fourth] Timed exit: 0 Test: fifth Soln Output: [soln running fifth] Timed exit: 0 

это функция или ошибка, или я что-то упускаю?

Мне кажется, что cat как-то переопределяет timeout , так как остальная часть скрипта начинает выполняться.

  cat $(find . -name iamnothere.txt) | wc -l 

предполагая, что iamnothere.txt не существует, становится

 cat | wc -l 

который потребляет стандартный вход, тот же стандартный ввод, который цикл while считывает строки . for избежания этого, не используя стандартный ввод, например while . Это можно наблюдать с помощью голого cat для случая второй линии, так как это показывает, что третья строка была прочитана этой cat :

 $ cat lines first secon third $ cat looper #!/bin/sh while read line; do x=$(timeout 2 ./doer "$line") echo "$line out=$x code=$?" done < lines $ cat doer #!/bin/sh if [ "$1" = secon ]; then cat else echo "$1 pid$$" fi $ ./looper first out=first pid42079 code=0 secon out=third code=0 $