el tiempo de espera hace que el ciclo while de lectura finalice cuando se agota el tiempo de espera de "cat"

el tiempo de espera hace que el ciclo while de lectura finalice cuando se agota el tiempo de espera de "cat"

No puedo discernir por qué timeoutuna llamada a una función provocará la detención de un bucle. Tengo una "solución", pero estoy realmente muy intrigado sobre cómo y por qué sucede esto. ¿Parece tener algo que ver con catque se agote el tiempo de espera del comando?

TL;DR

while read -r line; do ... done < filetermina cuando timeoutocurre a cat, lo que produce una salida y un código de salida incorrectos. El bucle hacenorecorre cada línea del archivo.

Si, en cambio, se crea una matriz primero que todas las líneas del archivo y luego ...se ejecuta en un archivo for line in "${all_lines[@]}"; do, se procesan todas las líneas y la salida timeoutcon respecto a los códigos de salida es correcta.


Supongamos que el script grade.shtiene la intención de leer todo tests.txty ejecutarlo soln.sh, asegurándose de que soln.shfinalice. Para demostrar un ejemplo "funcional", soln.shprimero 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]"

Rendimiento 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

Si cambiamos solnpara hacer algo que continuará para siempre (esperando entrada), el ciclo termina.

soln.sh

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

la salida termina antes de tiempo, código adicional 2incorrectoexit

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

La solución Hacky es recorrer primero cada línea y usar un forbucle que lo omitirá.

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

Rendimiento 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

¿Es esto una característica o un error o me falta algo?

Me parece que catde alguna manera es primordial timeout, ya que el resto del script se ejecuta.

Respuesta1

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

asumir que iamnothere.txtno existe se convierte

cat | wc -l

que consume entrada estándar,la misma entrada estándar desde la que el whilebucle lee las líneas. forevita esto al no utilizar la entrada estándar como whilelo hace. Esto se puede observar usando un catcaso simple para la segunda línea, ya que esto muestra que la tercera línea ha sido leída por 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
$ 

información relacionada