Aquí podría ser tu PUBLICIDAD


Shell scripting input redirection rarezas

votos
16

¿Alguien puede explicar este comportamiento? Corriendo:

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

da como resultado que no se produzca nada, mientras:

#!/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 el tubo hacer en un solo paso lo que hizo la redirección al archivo de prueba en el segundo ejemplo? Probé el mismo código tanto con el tablero como con el bash y obtuve el mismo comportamiento en ambos.

Publicado el 05/08/2008 a las 20:26
fuente por usuario Ryan Ahearn
En otros idiomas...        العربية       

9 respuestas

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

no produce salida porque las tuberías ejecutan cada uno de sus componentes dentro de una subcadena. Las subcadenas 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 una subshell, y $ foo conserva su valor original después de ser modificado dentro de ellos.

Ahora prueba esto:

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

Los refuerzos son puramente para agrupar, no se crea una subescala, y el $ foo modificado dentro de las llaves es el mismo $ foo modificado fuera de ellos.

Ahora prueba esto:

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

Dentro de los corchetes, el builtin de lectura crea $ var1 y $ var2 correctamente y se puede ver que se hacen eco. Fuera de las llaves, ya no existen. Todo el código dentro de las llaves se ha ejecutado en una subcapa porque es un componente de una tubería .

Puede poner cantidades arbitrarias de código entre llaves, para que pueda 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.

Respondida el 19/09/2008 a las 03:20
fuente por usuario flabdablet


Aquí podría ser tu PUBLICIDAD


votos
9

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

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

A:

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

ksh es un shell de programación superior debido a pequeñas sutilezas como esta. (Bash es el mejor intérprete interactivo, en mi opinión).

Respondida el 15/08/2008 a las 04:52
fuente por usuario Jon Ericson

votos
8

Una adición reciente a bashes la lastpipeopción, que permite que el último comando en una tubería para ejecutarse en el shell actual, no un subnivel, cuando se desactiva el control de trabajos.

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

dignares de salida

hello
world
Respondida el 26/07/2012 a las 01:10
fuente por usuario chepner

votos
8
read var1 var2 < <(echo "hello world")
Respondida el 17/09/2008 a las 01:17
fuente por usuario Steve Baker

votos
5

¡Bien, lo descubrí!

Este es un error difícil de capturar, pero resulta de la forma en que el caparazón maneja las tuberías. 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. Sin embargo, puedes intentar 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. Para establecer múltiples variables a la vez, debe leer en una variable y dividirla 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 usar una pipa, 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.

Respondida el 05/08/2008 a las 09:09
fuente por usuario num1

votos
4

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

read var1 var2 <<< "hello world"
echo $var1 $var2
Respondida el 04/03/2009 a las 10:52
fuente por usuario jim

votos
4

La publicación se ha respondido correctamente, pero me gustaría ofrecer una línea alternativa que quizás podría ser de alguna utilidad.

Para asignar valores separados por espacio de echo (o stdout para el caso) a variables de shell, podría considerar usar matrices de shell:

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

En este ejemplo, var es una matriz y se puede acceder a los contenidos usando la construcción $ {var [index]}, donde index es el índice de la matriz (comienza con 0).

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

Respondida el 14/09/2008 a las 06:00
fuente por usuario Dimitris Liappis

votos
4

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 la subshell se cierra.

Ejecuta este comando

$ echo $$;cat | read a
10637

y use pstree -p para observar los procesos en ejecución, verá una capa adicional colgando de su caparazón principal.

    |                       |-bash(10637)-+-bash(10786)
    |                       |             `-cat(10785)
Respondida el 05/08/2008 a las 09:00
fuente por usuario Mark Harrison

votos
3

Tratar:

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

El problema, como han afirmado varias personas, es que var1 y var2 se crean en un entorno subshell que se destruye cuando se cierra esa subcapa. 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
Respondida el 17/09/2008 a las 03:15
fuente por usuario pjz