linux - variable - Escritura bash, valores de lectura de stdin pipe
pipes in linux with examples (15)
Aquí hay una solución alternativa, que también se puede usar al leer más de una variable y cuando hay más palabras en el tubo que el número de variables que se leen.
eval $(echo hickery dickery dock the mouse | { read A B C ; echo A=/"${A}/" B=/"${B}/" C=/"${C}/" ; })
echo $A
hickery
echo $B
dickery
echo $C
dock the mouse
Estoy intentando que bash procese los datos de stdin que se canalizan, pero no hay suerte, lo que quiero decir es que ninguno de los siguientes trabajos:
echo "hello world" | test=($(< /dev/stdin)); echo test=$test
test=
echo "hello world" | read test; echo test=$test
test=
echo "hello world" | test=`cat`; echo test=$test
test=
donde quiero que la salida sea test=hello world
. Tenga en cuenta que he intentado poner "" comillas alrededor de "$test"
que tampoco funciona.
Creo que estabas tratando de escribir un script de shell que podría tomar la entrada de stdin. pero mientras intentas hacerlo en línea, te perdiste tratando de crear esa prueba = variable. Creo que no tiene mucho sentido hacerlo en línea, y es por eso que no funciona de la manera esperada.
Estaba tratando de reducir
$( ... | head -n $X | tail -n 1 )
para obtener una línea específica de varias entradas. entonces podría escribir ...
cat program_file.c | line 34
entonces necesito un pequeño programa shell capaz de leer de stdin. como tu lo haces.
22:14 ~ $ cat ~/bin/line
#!/bin/sh
if [ $# -ne 1 ]; then echo enter a line number to display; exit; fi
cat | head -n $1 | tail -n 1
22:16 ~ $
ahí tienes.
El primer intento fue bastante cercano. Esta variación debería funcionar:
echo "hello world" | { test=$(< /dev/stdin); echo "test=$test"; };
y el resultado es:
prueba = hola mundo
Necesitas llaves después de la tubería para adjuntar la tarea para probar y el eco.
Sin las llaves, la tarea de probar (después de la tubería) está en un shell, y el eco "test = $ test" está en un shell separado que no sabe acerca de esa asignación. Es por eso que estaba obteniendo "test =" en la salida en lugar de "test = hello world".
El siguiente código:
echo "hello world" | ( test=($(< /dev/stdin)); echo test=$test )
también funcionará, pero abrirá otro nuevo subconjunto después del conducto, donde
echo "hello world" | { test=($(< /dev/stdin)); echo test=$test; }
no lo hará
Tuve que desactivar el control de trabajo para hacer uso del método de chepnars (estaba ejecutando este comando desde la terminal):
set +m;shopt -s lastpipe
echo "hello world" | read test; echo test=$test
echo "hello world" | test="$(</dev/stdin)"; echo test=$test
último tubo
Si se establece, y el control del trabajo no está activo , el shell ejecuta el último comando de una tubería no ejecutada en segundo plano en el entorno de shell actual.
Nota: el control del trabajo está desactivado de forma predeterminada en un shell no interactivo y, por lo tanto, no necesita el set +m
dentro de un script.
En mi opinión, la mejor manera de leer de stdin in bash es la siguiente, que también te permite trabajar en las líneas antes de que finalice la entrada:
while read LINE; do
echo $LINE
done < /dev/stdin
Esta es otra opción
$ read test < <(echo hello world)
$ echo $test
hello world
La sintaxis para un conducto implícito de un comando de shell en una variable bash es
var=$(command)
o
var=`command`
En los ejemplos, está conectando datos a una declaración de asignación, que no espera ninguna entrada.
No soy un experto en Bash, pero me pregunto por qué no se ha propuesto esto:
stdin=$(cat)
echo "$stdin"
Prueba de un trazador de líneas que funciona para mí:
$ fortune | eval ''stdin=$(cat); echo "$stdin"''
Porque me enamoro de eso, me gustaría dejar caer una nota. Encontré este hilo, porque tengo que reescribir un antiguo script sh para que sea compatible con POSIX. Esto básicamente significa evitar el problema de la tubería / subshell introducido por POSIX al reescribir un código como este:
some_command | read a b c
dentro:
read a b c << EOF
$(some_command)
EOF
Y codifique así:
some_command |
while read a b c; do
# something
done
dentro:
while read a b c; do
# something
done << EOF
$(some_command)
EOF
Pero este último no se comporta de la misma manera en la entrada vacía. Con la notación antigua, el ciclo while no se ingresa en la entrada vacía, ¡pero sí en la notación POSIX! Creo que se debe a la nueva línea antes de EOF, que no se puede omitir. El código POSIX que se comporta más como la notación antigua se ve así:
while read a b c; do
case $a in ("") break; esac
# something
done << EOF
$(some_command)
EOF
En la mayoría de los casos, esto debería ser lo suficientemente bueno. Pero desafortunadamente esto todavía no se comporta exactamente como la notación anterior si some_command imprime una línea vacía. En la vieja notación, el cuerpo while se ejecuta y en notación POSIX nos rompemos frente al cuerpo.
Un enfoque para solucionar esto podría verse así:
while read a b c; do
case $a in ("something_guaranteed_not_to_be_printed_by_some_command") break; esac
# something
done << EOF
$(some_command)
echo "something_guaranteed_not_to_be_printed_by_some_command"
EOF
Qué tal esto:
echo "hello world" | echo test=$(cat)
Tirar algo en una expresión que involucra una tarea no se comporta de esa manera.
En cambio, intente:
test=$(echo "hello world"); echo test=$test
Utilizar
IFS= read var << EOF
$(foo)
EOF
Puede engañar a la read
para que acepte de una tubería como esta:
echo "hello world" | { read test; echo test=$test; }
o incluso escribir una función como esta:
read_from_pipe() { read "$@" <&0; }
Pero no tiene sentido, ¡sus asignaciones variables pueden no durar! Una canalización puede engendrar una subshell, donde el entorno se hereda por valor, no por referencia. Esta es la razón por la cual la read
no molesta con la entrada de una tubería; no está definida.
FYI, http://www.etalabs.net/sh_tricks.html es una colección ingeniosa de los restos necesarios para luchar contra las rarezas e incompatibilidades de las conchas bourne, sh.
si desea leer muchos datos y trabajar en cada línea por separado, podría usar algo como esto:
cat myFile | while read x ; do echo $x ; done
si quiere dividir las líneas en múltiples palabras, puede usar múltiples variables en lugar de x como esta:
cat myFile | while read x y ; do echo $y $x ; done
alternativamente:
while read x y ; do echo $y $x ; done < myFile
Pero tan pronto como comiences a querer hacer algo realmente inteligente con este tipo de cosas, es mejor que vayas a un lenguaje de scripting como Perl, donde podrías probar algo como esto:
perl -ane ''print "$F[0]/n"'' < myFile
Hay una curva de aprendizaje bastante empinada con Perl (o supongo que en cualquiera de estos idiomas) pero a la larga te resultará mucho más fácil si quieres hacer algo más que los scripts más sencillos. Recomendaría el Perl Cookbook y, por supuesto, el lenguaje de programación Perl de Larry Wall et al.
bash
4.2 introduce la opción lastpipe
, que permite que tu código funcione tal como está escrito, ejecutando el último comando en una canalización en el shell actual, en lugar de una subshell.
shopt -s lastpipe
echo "hello world" | read test; echo test=$test
read
no leerá desde una tubería (o posiblemente se pierda el resultado porque la tubería crea una subcamada). Sin embargo, puedes usar una cadena aquí en Bash:
$ read a b c <<< $(echo 1 2 3)
$ echo $a $b $c
1 2 3