тайм-аут приводит к завершению цикла while read, когда истекает тайм-аут «cat»

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

TL; DR

при чтении -r строка; do ... done завершается, когда тайм-аут происходит на cat , что приводит к неправильному результату и коду выхода. Цикл не , а не перебирает каждую строку в файле.

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


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

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 , неправильный код выхода

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

Хакерское исправление - сначала перебирать каждую строку и использовать для цикла , который будет обходить это.

"исправлено" 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 каким-то образом переопределение тайм-аута , поскольку остальная часть скрипта должна выполняться.

0
01.06.2017, 15:53
1 ответ
 cat $(find . -name iamnothere.txt) | wc -l

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

cat | wc -l

, который использует стандартный ввод, тот же стандартный ввод, что и цикл while считывает строки из . ]. for позволяет избежать этого, не используя стандартный ввод, как , тогда как использует. Это можно наблюдать, используя голый 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
$ 
2
28.01.2020, 02:45

Теги

Похожие вопросы