o tempo limite faz com que o loop de leitura termine quando `cat` expirar

o tempo limite faz com que o loop de leitura termine quando `cat` expirar

Não consigo discernir por que timeoutuma chamada de função fará com que um loop pare. Eu tenho uma "solução", mas estou realmente muito intrigado em saber como/por que isso está acontecendo! Parece ter algo a ver com cato tempo limite do comando expirar?

DR

while read -r line; do ... done < fileé encerrado quando timeoutocorre on cat, produzindo a saída e o código de saída incorretos. O loop faznãopercorrer cada linha do arquivo.

Se, em vez disso, uma matriz for criada primeiro de todas as linhas do arquivo e depois ...executada em a for line in "${all_lines[@]}"; do, todas as linhas serão processadas e a saída de timeoutem relação aos códigos de saída estará correta.


Suponha que o script grade.shpretenda ler tudo tests.txte executar soln.sh, certificando-se de que soln.shtermine. Para demonstrar um exemplo "funcional", soln.shprimeiro 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]"

resultado esperado

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

Se mudarmos solnpara fazer algo que continuará para sempre (aguardando entrada), o loop terminará

soln.sh

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

a saída termina antecipadamente, código extra 2e erradoexit

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

A solução hacky é percorrer cada linha primeiro e usar um forloop que irá contornar isso.

"fixo"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

resultado esperado

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

isso é um recurso ou um bug ou estou faltando alguma coisa?

Parece-me que caté de alguma forma predominante timeout, já que o restante do script é executado.

Responder1

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

assumir que iamnothere.txtnão existe torna-se

cat | wc -l

que consome entrada padrão,a mesma entrada padrão da qual o whileloop está lendo as linhas. forevita isso não usando entrada padrão como whilefaz. Isso pode ser observado usando um bare catpara o caso da segunda linha, pois mostra que a terceira linha foi lida por aquele 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
$ 

informação relacionada