scripts - script bash linux ejemplos
Shell scripting input redirection rarezas (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).
¿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.
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)
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 utilizando 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.
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
#!/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.
Mi opinión sobre este tema (usando Bash):
read var1 var2 <<< "hello world"
echo $var1 $var2
read var1 var2 < <(echo "hello world")
Una adición reciente a bash
es la opción lastpipe
, que permite que el último comando de una canalización se ejecute en el shell actual, no en una subcapa, cuando el control de tareas 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
¡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.