Я не могу понять, почему тайм-аут
в вызове функции вызывает остановку цикла. У меня есть «решение», но я действительно очень заинтригован тем, как / почему это происходит! Кажется, это как-то связано с тем, что 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
каким-то образом переопределение тайм-аута
, поскольку остальная часть скрипта должна выполняться.
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
$