¿Por qué obtengo valores diferentes para $x
los fragmentos a continuación?
#!/bin/bash
x=1
echo fred > junk ; while read var ; do x=55 ; done < junk
echo x=$x
# x=55 .. I'd expect this result
x=1
cat junk | while read var ; do x=55 ; done
echo x=$x
# x=1 .. but why?
x=1
echo fred | while read var ; do x=55 ; done
echo x=$x
# x=1 .. but why?
Respuestas:
La explicación correcta ya ha sido dada por jsbillings y geekosaur , pero permítanme ampliarlo un poco.
En la mayoría de los shells, incluido bash, cada lado de una tubería se ejecuta en un subshell, por lo que cualquier cambio en el estado interno del shell (como el establecimiento de variables) permanece limitado a ese segmento de una tubería. La única información que puede obtener de una subshell es lo que genera (a la salida estándar y otros descriptores de archivo) y su código de salida (que es un número entre 0 y 255). Por ejemplo, el siguiente fragmento imprime 0:
En ksh (las variantes derivadas del código AT&T, no las variantes pdksh / mksh) y zsh, el último elemento de una canalización se ejecuta en el shell principal. (POSIX permite ambos comportamientos). Por lo tanto, el fragmento anterior imprime 2.
Una expresión útil es incluir la continuación del ciclo while (o lo que sea que tenga en el lado derecho de la tubería, pero un ciclo while es realmente común aquí) en la tubería:
fuente
< <(locate -ber ^\.tag$)
Logré ejecutar mi script con , gracias a la respuesta original poco clara y los comentarios de geekosaur y glenn jackman ... Inicialmente tuve un dilema sobre aceptar la respuesta, pero el resultado neto fue bastante claro, especialmente con el comentario de seguimiento de jsbillings :)Se encuentra con un problema de alcance variable. Las variables definidas en el ciclo while que está en el lado derecho de la tubería tienen su propio contexto de alcance local, y los cambios en la variable no se verán fuera del ciclo. El ciclo while es esencialmente un subshell que obtiene una COPIA del entorno del shell, y cualquier cambio en el entorno se pierde al final del shell. Vea esta pregunta de StackOverflow .
ACTUALIZADO : Me olvidé de señalar el hecho importante de que el ciclo while con su propio subshell se debía a que era el punto final de una tubería, lo actualicé en la respuesta.
fuente
while
bucle como el extremo final de una tubería que lo arroja a una subshell.blah|blah|while read ...
, puedes tenerwhile read ...; done < <(blah|blah)
Como se mencionó en otras respuestas , las partes de una tubería se ejecutan en subcapas, por lo que las modificaciones realizadas allí no son visibles para el shell principal.
Si consideramos solo Bash, hay otras dos soluciones además de la
cmd | { stuff; more stuff; }
estructura:Redireccionar la entrada de la sustitución del proceso :
La salida del comando en
<(...)
se hace aparecer como si fuera una tubería con nombre.La
lastpipe
opción, que hace que Bash funcione como ksh, y ejecuta la última parte de la tubería en el proceso de shell principal. Aunque solo funciona si el control de trabajo está desactivado, es decir, no en un shell interactivo:o
Por supuesto, la sustitución de procesos también es compatible con ksh y zsh. Pero dado que ejecutan la última parte de la tubería en el shell principal de todos modos, usarlo como una solución no es realmente necesario.
fuente
puede funcionar
fuente