en bash, leer después de que una tubería no establezca valores

22

Editar: el título original era "la lectura falla en bash"

Con ksh estoy usando leer como una forma conveniente de separar valores:

$ echo 1 2 3 4 5 | read a b dump
$ echo $b $a 
2 1
$

Pero falla en bash:

$ echo 1 2 3 4 5 | read a b dump
$ echo $b $a 

$

No encontré una razón en la página del manual por la que falla, ¿alguna idea?

Emmanuel
fuente
2
Esto se discute (algo oscuro) en la página 024 de las Preguntas frecuentes de Greg's Bash .
Scott

Respuestas:

27

bash ejecuta el lado derecho de una tubería en un contexto de subshell , por lo que los cambios en las variables (que es lo que readhace) no se conservan; mueren cuando la subshell lo hace, al final del comando.

En su lugar, puede usar la sustitución de procesos :

$ read a b dump < <(echo 1 2 3 4 5)
$ echo $b $a
2 1

En este caso, readse ejecuta dentro de nuestro shell primario, y nuestro comando de producción de salida se ejecuta en el subshell. La <(...)sintaxis crea una subshell y conecta su salida a una tubería, que redirigimos a la entrada de readcon la <operación ordinaria . Debido a que se readejecutó en nuestro shell principal, las variables están establecidas correctamente.

Como se señaló en un comentario, si su objetivo es literalmente dividir una cadena en variables de alguna manera, puede usar una cadena aquí :

read a b dump <<<"1 2 3 4 5"

Supongo que hay más que eso, pero esta es una mejor opción si no la hay.

Michael Homer
fuente
3
O incluso read a b dump <<< '1 2 3 4 5'.
choroba
Gracias a todos, por cierto, noté que mksh (en cygwin) está haciendo lo mismo que bash.
Emmanuel
@Michael Homer ¡Buena explicación! He encontrado otro ejemplo que puede explicar que cada comando en la tubería ejecuta en el propio subnivel: cat /etc/passwd | (read -r line ; echo $line). Pero al lado echode $linela cual no se encuentra en la tubería ponga nada en la pantalla, ya que fue existía justo valor entre paréntesis (subcapa). Esperanza, ayuda a alguien.
Yurij Goncharuk
17

Esto no es un bashfallo que POSIXpermite tanto bashy kshcomportamientos, lo que lleva a la desafortunada discrepancia que está observando.

http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_12

Además, cada comando de una tubería de comandos múltiples está en un entorno de subshell; como extensión, sin embargo, cualquiera o todos los comandos en una tubería pueden ejecutarse en el entorno actual. Todos los demás comandos se ejecutarán en el entorno de shell actual.

Sin embargo, con bash 4.2y más reciente, puede configurar la lastpipeopción en scripts no interactivos para obtener el resultado esperado, por ejemplo:

#!/bin/bash

echo 1 2 3 4 5 | read a b dump
echo before: $b $a 
shopt -s lastpipe
echo 1 2 3 4 5 | read a b dump
echo after: $b $a 

Salida:

before:
after: 2 1
jlliagre
fuente
1
+1 gracias por la información de "lastpipe". perdón por el retraso
Emmanuel
1
El problema lastpipees que no funciona en otros shells (p. ej., guión). Básicamente no hay forma de hacer que este portátil no ejecute todo en esa subshell, consulte stackoverflow.com/questions/36268479/…
anarcat
1
@anarcat Esto es correcto, pero la pregunta que se hizo aquí fue sobre bash.
jlliagre
@anarcat: Esto puede cambiar en el futuro ya que existe el deseo de cambiar POSIX por otro motivo (estado de la TUBERÍA), ver aquí: unix.stackexchange.com/questions/476834/... Cambiar otras shells no es trivial, me tomó varios meses reescribir el analizador e interpeter en Bourne Shell (bosh) para implementar el comportamiento más rápido del ksh moderno.
schily