Pregunta

¿Alguien puede explicar este comportamiento?Correr:

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

da como resultado que no se genere nada, 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 la tubería hacer en un solo paso lo que hizo la redirección a test.file en el segundo ejemplo?Probé el mismo código con los shells dash y bash y obtuve el mismo comportamiento en ambos.

¿Fue útil?

Solución

Una reciente incorporación a bash es el lastpipe opción, que permite que el último comando de una canalización se ejecute en el shell actual, no en un subshell, cuando el control de trabajos está desactivado.

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

de hecho saldrá

hello
world

Otros consejos

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

no produce ningún resultado porque las canalizaciones ejecutan cada uno de sus componentes dentro de una subcapa.Subcapas heredar 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 subshell y $foo conserva su valor original después de modificarlo 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

Las llaves son puramente para agrupar, no se crea ninguna subcapa y el $foo modificado dentro de las llaves es el mismo $foo modificado fuera de ellas.

Ahora prueba esto:

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

Dentro de las llaves, la lectura incorporada crea $var1 y $var2 correctamente y puedes ver que se repiten.Fuera de los brackets, ya no existen.Todo el código entre llaves se ha ejecutado en una subcapa 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 canalización en un bloque siempre que necesite ejecutar un bloque de script de shell que analice la salida de otra cosa.

Esto ya se ha respondido correctamente, pero aún no se ha indicado la solución.Utilice 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.(en mi opinión, bash es el mejor shell interactivo).

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

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

Para asignar valores separados por espacios 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 su contenido utilizando la construcción ${var[index]}, donde índice es el índice de la matriz (comienza con 0).

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

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

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

¡Muy bien, lo descubrí!

Este es un error difícil de detectar, pero se debe a la forma en que el shell 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, los establece en su propio subshell, no en el shell principal.Entonces, cuando sale la subcapa, 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 puedes configurar muchas a la vez.Para configurar múltiples variables a la vez, debes leer 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

Si bien admito que no es tan elegante como usar una pipa, funciona.Por supuesto, debes tener en cuenta que read estaba destinado a leer desde archivos a variables, por lo que leer desde la entrada estándar debería ser un poco más difícil.

Esto se debe a que la versión pipe está creando un subshell, que lee la variable en su espacio local, que luego se destruye cuando el subshell sale.

Ejecute este comando

$ echo $$;cat | read a
10637

y usa pstree -p para ver los procesos en ejecución, verás un shell adicional colgando de tu shell principal.

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

Intentar:

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

El problema, como han dicho varias personas, es que var1 y var2 se crean en un entorno de subcapa que se destruye cuando esa subcapa sale.Lo anterior evita destruir el subshell hasta que se haya repetido el resultado.Otra solución es:

result=`echo "hello world"`
read var1 var2 <<EOF
$result
EOF
echo $var1
echo $var2
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top