タイムアウトにより、`cat` がタイムアウトしたときに while read ループが終了します。

タイムアウトにより、`cat` がタイムアウトしたときに while read ループが終了します。

timeout関数呼び出しでループが停止する理由がわかりません。 「解決策」はありますが、なぜこのようなことが起こるのか、とても興味があります。catコマンドがタイムアウトになったことと関係があるようです。

要約

while read -r line; do ... done < filetimeoutは で が発生したときに終了しcat、間違った出力と終了コードを生成します。ループはないファイル内のすべての行をループします。

代わりに、最初にファイル内のすべての行の配列を作成し、次に...で実行すると、すべての行が処理され、終了コードに関する の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

ハッキーな修正方法は、まずすべての行をループし、forこれをバイパスするループを使用することです。

"修理済み"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

答え1

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

iamnothere.txt存在しないと仮定すると

cat | wc -l

標準入力を消費し、whileループが行を読み込んでいるのと同じ標準入力は、のforように標準入力を使用しないことでこれを回避しますwhile。これは、2 行目の場合に bare を使用すると確認できますcat。これは、3 行目がによって読み取られたことを示しています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
$ 

関連情報