Utilice la lectura incorporada de bash sin un bucle while

Utilice la lectura incorporada de bash sin un bucle while

Estoy acostumbrado a la función bashincorporada readen los bucles while, por ejemplo:

echo "0 1
      1 1
      1 2
      2 3" |\
while read A B; do
    echo $A + $B | bc;
done

He estado trabajando en algún makeproyecto y resultó prudente dividir archivos y almacenar resultados intermedios. Como consecuencia, a menudo termino triturando líneas individuales en variables. Si bien el siguiente ejemplo funciona bastante bien,

head -n1 somefile | while read A B C D E FOO; do [... use vars here ...]; done

es algo estúpido, porque el ciclo while nunca se ejecutará más de una vez. Pero sin el while,

head -n1 somefile | read A B C D E FOO; [... use vars here ...]

Las variables de lectura siempre están vacías cuando las uso. Nunca noté este comportamiento de read, porque normalmente uso bucles while para procesar muchas líneas similares. ¿Cómo puedo usar bashla función readincorporada sin un bucle while? ¿O hay otra forma (o incluso mejor) de leer una sola línea en múltiples (!) variables?

Conclusión

Las respuestas nos enseñan que es un problema de alcance. La declaración

 cmd0; cmd1; cmd2 | cmd3; cmd4

se interpreta de manera que los comandos cmd0, cmd1y cmd4se ejecutan en el mismo ámbito, mientras que a los comandos cmd2y cmd3se les asigna cada uno su propia subcapa y, en consecuencia, ámbitos diferentes. El caparazón original es el padre de ambos subcapas.

Respuesta1

Es porque la parte donde usas vars es un nuevo conjunto de comandos. Utilice esto en su lugar:

head somefile | { read A B C D E FOO; echo $A $B $C $D $E $FOO; }

Tenga en cuenta que, en esta sintaxis, debe haber un espacio después de { y un ;(punto y coma) antes de }. Tampoco -n1es necesario; readsolo lee la primera línea.

Para una mejor comprensión, esto puede ayudarle; hace lo mismo que arriba:

read A B C D E FOO < <(head somefile); echo $A $B $C $D $E $FOO

Editar:

A menudo se dice que las siguientes dos declaraciones hacen lo mismo:

head somefile | read A B C D E FOO
read A B C D E FOO < <(head somefile)

Bueno no exactamente. El primero es una tubería incorporada headen bash's' read. La salida estándar de un proceso a la entrada estándar de otro proceso.

La segunda afirmación es la redirección y sustitución de procesos. Se maneja bashsolo. Crea una FIFO (tubería con nombre <(...)) a la que headestá conectada la salida y la redirige ( <) al readproceso.

Hasta ahora parecen equivalentes. Pero cuando se trabaja con variables, esto puede ser importante. En el primero las variables no se configuran después de la ejecución. En el segundo están disponibles en el entorno actual.

Cada caparazón tiene un comportamiento diferente en esta situación. Verese enlacepara lo que son. Puede bashsolucionar ese comportamiento con agrupación de comandos {}, sustitución de procesos ( < <()) o cadenas Aquí ( <<<).

Respuesta2

Para citar un artículo muy útil.wiki.bash-hackers.org:

Esto se debe a que los comandos de la canalización se ejecutan en subshells que no pueden modificar el shell principal. Como resultado, las variables del shell principal no se modifican (ver artículo:Bash y el árbol de procesos).

Como la respuesta se ha proporcionado varias veces, una forma alternativa (usando comandos no integrados...) es esta:

$ eval `echo 0 1 | awk '{print "A="$1";B="$2}'`;echo $B $A
$ 1 0

Respuesta3

Como notó, el problema era que una tubería readse ejecuta en una subcapa.

Una respuesta es utilizar unheredoc:

numbers="01 02"
read first second <<INPUT
$numbers
INPUT
echo $first
echo $second

Este método es bueno porque se comportará de la misma manera en cualquier shell tipo POSIX.

información relacionada