Estoy acostumbrado a la función bash
incorporada read
en 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 make
proyecto 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 bash
la función read
incorporada 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
, cmd1
y cmd4
se ejecutan en el mismo ámbito, mientras que a los comandos cmd2
y cmd3
se 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 -n1
es necesario; read
solo 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 head
en 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 bash
solo. Crea una FIFO (tubería con nombre <(...)
) a la que head
está conectada la salida y la redirige ( <
) al read
proceso.
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 bash
solucionar 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 read
se 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.