2008-08-05 13 views
16

¿Alguien puede explicar este comportamiento? de reproducción:Oportunidades de redirección de entrada del script Shell

#!/bin/sh 
echo "hello world" | read var1 var2 
echo $var1 
echo $var2 

resultado en nada era ouput, mientras que:

#!/bin/sh 
echo "hello world" > test.file 
read var1 var2 < test.file 
echo $var1 
echo $var2 

produce el resultado esperado:

hello 
world 

¿No debería hacer el tubo en un solo paso lo que el cambio de dirección de test.file lo hizo en el segundo ejemplo? Probé el mismo código tanto con el tablero como con el bash y obtuve el mismo comportamiento en ambos.

Respuesta

8

Una adición reciente a bash es la opción lastpipe, lo que permite que el último comando en una tubería para ejecutar en el shell actual, no un subnivel, cuando el control del trabajo está desactivado.

#!/bin/bash 
set +m  # Deactiveate job control 
shopt -s lastpipe 
echo "hello world" | read var1 var2 
echo $var1 
echo $var2 

dignares salida

hello 
world 
3

Esto se debe a que la versión de tubería está creando una subcadena, que lee la variable en su espacio local que luego se destruye cuando sale la subcapa.

ejecutar este comando

$ echo $$;cat | read a 
10637 

y utilizar pstree -p para mirar los procesos en ejecución, verá una cáscara adicional colgando fuera de su concha principal.

|      |-bash(10637)-+-bash(10786) 
    |      |    `-cat(10785) 
5

¡Allright, I lo descubrí!

Este es un error difícil de capturar, pero resulta de la forma en que las tuberías son manejadas por el caparazón. Cada elemento de una tubería se ejecuta en un proceso separado. Cuando el comando de lectura establece var1 y var2, se establece su propia subshell, no el shell principal. Entonces, cuando la subshell sale, los valores de var1 y var2 se pierden. Puede, sin embargo, trata de hacer

var1=$(echo "Hello") 
echo var1 

que devuelve la respuesta esperada. Desafortunadamente, esto solo funciona para variables individuales, no puede establecer muchas a la vez. Con el fin de establecer múltiples variables al mismo tiempo que debe leer bien en una variable y se trocean en múltiples variables o usar algo como esto:

set -- $(echo "Hello World") 
var1="$1" var2="$2" 
echo $var1 
echo $var2 

Aunque admito que no es tan elegante como el uso de un tubo, funciona . Por supuesto, debe tener en cuenta que la lectura debe leer los archivos en variables, por lo que leer desde la entrada estándar debería ser un poco más difícil.

+2

Esto depende de la elección de la cáscara. Ksh93 fue diseñado para limitar la sobrecarga del proceso.También ejecuta el elemento final de una interconexión * dentro * del proceso invocador de shell, conservando así el estado. –

+1

Bash 4.2 introdujo una opción para hacer lo mismo. Desactive el control de trabajos ('set + m') y configure la opción' lastpipe' ('shopt -s lastpipe'). – chepner

+0

Lo investigaré. ¡Gracias! –

9

Esto ya se ha respondido correctamente, pero la solución aún no se ha establecido. Usa ksh, no bash. Compare:

$ echo 'echo "hello world" | read var1 var2 
echo $var1 
echo $var2' | bash -s 

Para:

$ echo 'echo "hello world" | read var1 var2 
echo $var1 
echo $var2' | ksh -s 
hello 
world 

ksh es una cáscara de programación superior debido a pequeños detalles como este. (Bash es el mejor intérprete interactivo, en mi opinión.)

3

La publicación ha sido respondida correctamente, pero me gustaría ofrecer un trazador de líneas alternativo que quizás podría ser de alguna utilidad.

para la asignación de valores separados por espacios de eco (o la salida estándar para esa materia) para bombardear las variables, se puede considerar el uso de arrays de Shell:

$ var=($(echo 'hello world')) 
$ echo ${var[0]} 
hello 
$ echo ${var[1]} 
world 

En este ejemplo var es una matriz y el contenido se puede acceder usando el constructo $ {var [index]}, donde index es el índice de matriz (comienza con 0).

De esta forma puede tener tantos parámetros como desee asignados al índice de matriz relevante.

+2

Buena solución. Funciona muy bien en bash, pero no funciona en el shell de tablero. –

8
read var1 var2 < <(echo "hello world") 
3

Probar:

echo "hello world" | (read var1 var2 ; echo $var1 ; echo $var2) 

El problema, como muchas personas han dicho, es que var1 y var2 se crean en un entorno de subnivel que se destruye cuando que las salidas de subshell. Lo anterior evita la destrucción de la subcapa hasta que el resultado haya sido eco. Otra solución es:

result=`echo "hello world"` 
read var1 var2 <<EOF 
$result 
EOF 
echo $var1 
echo $var2 
10
#!/bin/sh 
echo "hello world" | read var1 var2 
echo $var1 
echo $var2 

no produce ninguna salida porque las tuberías corren cada uno de sus componentes dentro de una subcapa. Las subcapas heredan copias de las variables del shell principal, en lugar de compartirlas. Prueba esto:

#!/bin/sh 
foo="contents of shell variable foo" 
echo $foo 
(
    echo $foo 
    foo="foo contents modified" 
    echo $foo 
) 
echo $foo 

Los paréntesis definen una región de código que se ejecuta en un subnivel, y $ foo conserva su valor original después de ser modificado en su interior.

Ahora intente esto:

#!/bin/sh 
foo="contents of shell variable foo" 
echo $foo 
{ 
    echo $foo 
    foo="foo contents modified" 
    echo $foo 
} 
echo $foo 

Los frenos son puramente para la agrupación, no se crea un subnivel, y los $ foo modificado dentro de las llaves es el mismo $ foo modificado fuera de ellas.

Ahora intente esto:

#!/bin/sh 
echo "hello world" | { 
    read var1 var2 
    echo $var1 
    echo $var2 
} 
echo $var1 
echo $var2 

Entre las llaves, la lectura orden interna crea $ var1 y var2 $ adecuadamente y se puede ver que consiguen hicieron eco. Fuera de las llaves, ya no existen. Todo el código dentro de las llaves ha sido ejecutado en una subcategoría porque es un componente de una tubería.

Puede poner cantidades arbitrarias de código entre llaves, por lo que puede usar esta construcción de tubería en un bloque cada vez que necesite ejecutar un bloque de script de shell que analiza el resultado de otra cosa.

+1

+1 para ejemplos limpios: código agradable y legible –

4

Mi opinión sobre este tema (usando Bash):

read var1 var2 <<< "hello world" 
echo $var1 $var2 
Cuestiones relacionadas