timeout causa enquanto o loop de leitura termina quando 'cat' é expirado

0

Não consigo discernir por que timeout em uma chamada de função fará com que um loop pare. Eu tenho uma "solução", mas estou realmente muito intrigado sobre como / porque isso está acontecendo! Parece haver algo a ver com cat sendo o comando expirado?

TL; DR

while read -r line; do ... done < file é finalizado quando um timeout ocorre em cat , produzindo a saída e o código de saída incorretos. O loop faz não percorrer todas as linhas do arquivo.

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

Suponha que o script grade.sh pretenda ler todos os tests.txt e executar soln.sh , certificando-se de que soln.sh termine. Para demonstrar um exemplo de "trabalho", soln.sh será primeiro 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]"

saída esperada

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 alterarmos soln para fazer algo que continuará para sempre (aguardando entrada), o loop será encerrado

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 cedo, extra 2 , errado 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

Hacky fix é fazer um loop em todas as linhas primeiro e usar um loop for que irá ignorar isso.

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

saída esperada

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 está, de alguma forma, substituindo timeout , já que o restante do script é executado.

    
por svenevs 01.06.2017 / 12:29

1 resposta

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

assumindo que iamnothere.txt não existe, torna-se

cat | wc -l

que consome entrada padrão, a mesma entrada padrão que o loop while está lendo as linhas de . for evita isso não usando a entrada padrão como while . Isso pode ser observado usando um cat para o segundo caso de linha, pois isso mostra que a terceira linha foi lida por esse 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
$ 
    
por 01.06.2017 / 16:30