¿Cómo leo varias líneas de STDIN en una variable?


24

He estado buscando en Google esta pregunta en vano. Estoy automatizando un proceso de compilación aquí en el trabajo, y todo lo que intento hacer es obtener números de versión y una pequeña descripción de la compilación que puede ser de varias líneas. El sistema en el que se ejecuta es OSX 10.6.8.

He visto todo, desde usar CAT hasta procesar cada línea según sea necesario. No puedo entender qué debo usar y por qué.

Intentos

read -d '' versionNotes

Da como resultado una entrada confusa si el usuario tiene que usar la tecla de retroceso. Además, no hay una buena manera de terminar la entrada ya que ^ D no termina y ^ C simplemente sale del proceso.

read -d 'END' versionNotes

Funciona ... pero aún distorsiona la entrada si se necesita la tecla de retroceso.

while read versionNotes
do
  echo "  $versionNotes" >> "source/application.yml"
done

No finaliza correctamente la entrada (porque soy demasiado tarde para buscar coincidencias con una cadena vacía).


Está recibiendo esta información del usuario, ¿correcto?
Glenn Jackman

Correcto; Quiero que el usuario ingrese esta información en el terminal cuando ejecute el script.
Robert K

1
No te has dejado claro, prefiero decir.
poige

Respuestas:


20

man bash menciona «...

La sustitución de comando $ (archivo cat) se puede reemplazar por el equivalente pero más rápido $ (<archivo).

... »

$ myVar=$(</dev/stdin)
hello
this is test
$ echo $myVar
hello this is test
$ echo "$myVar"
hello
this is test

y estoy de acuerdo en que vale la pena mencionar esto: echo "$myVar"habría mostrado la entrada tal como fue dada.


1
¿Cómo parar después de una entrada multilínea? Si presiono Ctrl C, se detiene pero la variable no se guarda.
Nikhil

2
Los terminales UNIX® tienen "disciplina": en.wikipedia.org/wiki/Line_discipline • Verifique qué significa "eof" • Lea man stty, descubra cómo obtener la configuración actual
poige

2
Ctrl-D para detener.
freb

9

Prueba esto:

user@host:~$ read -d '' x <<EOF
> mic
> check
> one
> two
> EOF

Sin saltos de línea:

user@host:~$ echo $x
mic check one two

Con saltos de línea:

user@host:~$ echo "$x"
mic
check
one
two

2
-d ''es exactamente lo que necesitaba Gracias
Bruno Bronosky

@Bruno Un placer; Es complicado. Especialmente a primera vista, parece bastante estándar. Sin embargo, puede ser bastante útil una vez que domine las diversas idiosincrasias.
voces


2

He resuelto este problema tratando con cada línea hasta que apareció una línea en blanco. Funciona lo suficientemente bien para mi situación. Pero si alguien quiere agregar una mejor solución, siéntase libre de hacerlo.

echo "---
notes: |" > 'version.yml'

while read line
do
  # break if the line is empty
  [ -z "$line" ] && break
  echo "  $line" >> "source/application.yml"
done

Usar en su read -rlugar.
Adaptr

2

Puedes iniciar un editor como vim, pico ...

${VISUAL:-${EDITOR:-vi}} source/application.yml

Um ... ¿cómo responde eso a la pregunta del OP de alguna manera?
Adaptr

Puede iniciar el editor desde el guión.
Mircea Vutcovici

¿Y eso logra qué, exactamente? Quiere usar texto de un archivo en el script, no escribir información en un archivo.
Adaptr

No quería leer el texto del usuario y escribirlo en un archivo. Por favor lea los comentarios agregados a la pregunta.
Mircea Vutcovici

1

Primero algunas correcciones:

  1. Para permitir la "edición" en el uso de línea -eque usa readline (para que tenga el historial de bash y todas las funciones de edición)
  2. -dSolo toma un personaje. Por ejemplo, desde 'FIN' toma 'E' y cada vez que el usuario escribe una 'E' la lectura se detiene (supongo que eso no es lo que quieres ...)

Hay algunas posibilidades para hacer esto. Iría a leer línea por línea y me detendría cuando se encuentra una línea vacía (aunque podría establecer cualquier palabra de detención):

unset tmp
while :
do 
 read line
 [[ $line == "" ]] && tmp="${tmp:0:$((${#tmp}-1))}" && break
 tmp="$tmp"$line$'\n'
done

0

Tienes varios métodos.

Uno de los métodos más simples es:

MYVAR=$(yourcommand)
echo $"MYVAR"

Por ejemplo:

MYVAR=$(ls /)
echo $"MYVAR"

Excepto que no estoy ejecutando un comando de shell como LS. Estoy solicitando entrada de varias líneas del usuario.
Robert K

Esta es una muy mala práctica; no procese la salida de ls!
Adaptr

@adaptr Ya lo hemos escuchado antes, ¿te importaría sugerir una buena alternativa, por favor?
voces

0

Estamos usando una construcción usando xargs y ctrl-d para romper. No estoy completamente satisfecho con él, pero ciertamente hace el trabajo de tomar la entrada del usuario multilínea y ponerla en una variable (formato intacto). (La primera y la tercera asignación agregan comillas alrededor del contenido de la entrada xargs).

    printf "\\033[1mWhen finished hit ctrl-d on a new line to proceed.\\033[0m  \\n\\n" 
    # this will load the user's input into a variable instead of a file 
    reminderBody="\"" 
    reminderBody+=$( xargs -0 ) 
    reminderBody+="\"" 

Usamos reminderBody como el tema de un correo electrónico que se envía por correo (a través de bash).


-1

Ver: http://tldp.org/LDP/abs/html/internal.html#READR

Use ready tenga en cuenta que si termina una línea con \la nueva línea se ignora. Entonces podrías hacer:

#! / bin / bash

leer -p "versión:" versión
echo $ version

# ingrese alguna entrada y termine las líneas largas con "\", luego simplemente ingrese al final
leer -p "descripción:" descripción
echo -e "$ version \ n $ descripción >> yourfile.txt

Si solo quisiera ignorar un párrafo con saltos de línea, sí. Pero como se trata de una lista generada de notas de lanzamiento para un script de compilación, necesito poder preservar esos saltos de línea; Podría intentar ingresar una lista con viñetas, por ejemplo.
Robert K

El ABS es una monstruosidad de proporciones horribles. El 90% de eso está completamente equivocado.
Adaptr

No es que malo.
voces
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.