Ich kann nicht erkennen, warum timeout
bei einem Funktionsaufruf eine Schleife angehalten wird. Ich habe eine „Lösung“, aber ich bin wirklich sehr neugierig, wie/warum das passiert! Es scheint etwas damit zu tun zu haben, dass cat
der Befehl eine Zeitüberschreitung erfährt?
Kurz zusammengefasst
while read -r line; do ... done < file
wird beendet, wenn ein timeout
auftritt cat
, was zu einer falschen Ausgabe und einem falschen Exit-Code führt. Die Schleifenichtdurchläuft jede Zeile in der Datei.
Wenn stattdessen zuerst ein Array aus allen Zeilen der Datei erstellt und anschließend ...
in einem ausgeführt wird for line in "${all_lines[@]}"; do
, werden alle Zeilen verarbeitet und die Ausgabe von timeout
hinsichtlich der Exitcodes ist korrekt.
Angenommen, das Skript grade.sh
soll alles lesen tests.txt
und ausführen soln.sh
und sicherstellen, dass soln.sh
es beendet wird. Um ein „funktionierendes“ Beispiel zu demonstrieren, soln.sh
werden wir zuerst 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]"
erwartete Ausgabe
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
Wenn wir soln
etwas tun, das ewig andauert (auf Eingabe warten), endet die Schleife stattdessen
soln.sh
#!/usr/bin/env bash
if [[ "$1" == "third" ]]; then
cat $(find . -name iamnothere.txt) | wc -l
fi
echo "[soln running $1]"
Ausgabe bricht vorzeitig ab, zusätzlicher 2
, falscher exit
Code
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
Die Hack-Lösung besteht darin, zuerst jede Zeile zu durchlaufen und eine for
Schleife zu verwenden, die dies umgeht.
"Fest"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
erwartete Ausgabe
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
ist das ein Feature oder ein Fehler oder übersehe ich etwas?
Es scheint mir, als cat
würde es irgendwie außer Kraft gesetzt timeout
, da der Rest des Skripts ausgeführt wird.
Antwort1
cat $(find . -name iamnothere.txt) | wc -l
vorausgesetzt, dass iamnothere.txt
es nicht existiert, wird
cat | wc -l
die Standardeingabe verbraucht,dieselbe Standardeingabe, while
von der die Schleife die Zeilen liest. for
vermeidet dies, indem es nicht die Standardeingabe verwendet, wie while
dies der Fall ist. Dies kann beobachtet werden, indem man für den Fall der zweiten Zeile ein Leerzeichen verwendet cat
, da dies zeigt, dass die dritte Zeile von diesem gelesen wurde 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
$