Leer valores en una variable de shell desde una tubería

Estoy intentando que bash procese datos de stdin que se conectan, 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 . He intentado poner “” comillas alrededor de "$test" que tampoco funciona.

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 rests 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 xy ; do echo $y $x ; done 

alternativamente:

 while read xy ; 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 progtwigción Perl de Larry Wall et al.

Esta es otra opción

 $ read test < <(echo hello world) $ echo $test hello world 

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 abc <<< $(echo 1 2 3) $ echo $a $b $c 1 2 3 

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"' 

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 

La syntax 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 statement de asignación, que no espera ninguna entrada.

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 

El primer bash 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".

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 

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="$( 

Bash Manual dice :

ú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.

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 abc 

dentro:

 read abc << EOF $(some_command) EOF 

Y codifique así:

 some_command | while read abc; do # something done 

dentro:

 while read abc; 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 abc; 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 abc; 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 

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 progtwig 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.

Qué tal esto:

 echo "hello world" | echo test=$(cat)