¿Cómo pasar el valor de una variable al stdin de un comando?

105

Estoy escribiendo un script de shell que debería ser algo seguro, es decir, no pasa datos seguros a través de parámetros de comandos y preferiblemente no usa archivos temporales. ¿Cómo puedo pasar una variable al stdin de un comando? O, si no es posible, ¿cómo utilizar correctamente los archivos temporales para dicha tarea?

Jonathan Leffler
fuente
¿Puede un atacante cambiar $PATH? Para que catpueda ser reemplazado/bin/cat "$@" | tee /attacker/can/read/this/file
12431234123412341234123

Respuestas:

74

Algo tan simple como:

echo "$blah" | my_cmd
Oliver Charlesworth
fuente
7
Cuidado con los espacios en tu variable: echo "$blah"es mejor.
Jonathan Leffler
13
Veamos cómo lo manejas blah=-n, blah=-e... usa printfen su lugar. unix.stackexchange.com/questions/65803/…
Camilo Martin
1
esto parece que sería frágil y propenso a errores, ¿no?
ThorSummoner
20
6 años después, si pudiera eliminar esta respuesta, lo haría (pero no puedo porque ha sido aceptada ...)
Oliver Charlesworth
1
@OliverCharlesworth, ¿por qué no editarlo para usarlo printf '%s\n' "$blah"? Con ese cambio (evitando las muchas trampas en echola especificación, que la excelente respuesta de Stephane entra en detalle en ¿Por qué es printfmejor que echo? En Unix y Linux , o que la sección de USO DE APLICACIONES de la echoespecificación toca más brevemente) es un perfecto respuesta razonable.
Charles Duffy
192

Pasar un valor a stdinen bash es tan simple como:

your-command <<< "$your_variable"

¡Siempre asegúrese de poner comillas alrededor de las expresiones variables!

Tenga cuidado, ya que esto probablemente funcionará solo en bashy no funcionará en sh.

Martín
fuente
39
No <<<se garantiza que estas cadenas estén disponibles, no están en la base POSIX, hasta donde yo sé. Funcionarán como se espera, siempre y cuando solo los ejecute bash. Solo lo menciono, porque OP dijeron 'shell', no específicamente 'bash'. Aunque etiquetó la pregunta con bash... así que obviamente esta es una respuesta aceptable.
JM Becker
Si bien este puede ser el caso, es probable que la información confidencial se filtre más fácilmente si se usa el echométodo debido a la creación de una tubería. El comando herestring será procesado por completo bash, aunque en esta situación (como se indicó) es mejor que sepa que funcionará en su bash, de lo contrario, cualquier mensaje de error producido como resultado de no admitir herestrings también filtraría dicha información.
Steven Lu
@StevenLu Wait, echoes una función de. No hay tubería allí, ¿verdad? Es Unix, pero no puede ser tanto Unix .
Camilo Martin
4
@StevenLu printf '%s\n' "$var"produce los mismos resultados que echo "$var"pero no se romperá si, por ejemplo var=-en, y ni siquiera requiere bash.
Camilo Martin
1
Tenga en cuenta también que se agrega una nueva línea a la cadena de aquí cadenas.
pdr
19

Tenga en cuenta que las echo "$var" | commandoperaciones ' significan que la entrada estándar se limita a las líneas repetidas. Si también desea que el terminal esté conectado, deberá ser más elegante:

{ echo "$var"; cat - ; } | command

( echo "$var"; cat -   ) | command

Esto significa que la (s) primera (s) línea (s) serán el contenido de $varpero el resto provendrá de catleer su entrada estándar. Si el comando no hace nada demasiado sofisticado (intente activar la edición de la línea de comandos o ejecutarlo como lo vimhace), estará bien. De lo contrario, debe ser realmente elegante, creo que expectes probable que uno de sus derivados sea apropiado.

Las notaciones de la línea de comando son prácticamente idénticas, pero el segundo punto y coma es necesario con las llaves, mientras que no lo es con los paréntesis.

Jonathan Leffler
fuente
( echo "$LIST"; cat - ) | sed 1qesto funciona para mí pero necesito presionar ctrl d cuando ejecuto este script?
Gert Cuykens
Si; la cat -continúa para leer desde el teclado hasta que EOF o interrupción, por lo que necesita para contarlo EOF mediante la tipificación de control-D.
Jonathan Leffler
¿Hay alguna forma de evitar EOF? sed necesita gato
Gert Cuykens
No tiene que usar catnada si no desea una entrada de terminal, como en la primera línea. O puede usar catpara listar un archivo. O ... Si desea que el comando lea la entrada del terminal, debe informarle cuando haya llegado al final de la entrada. O podrías usar ( echo "$var"; sed /quit/q - ) | command; esto continúa hasta que escriba una línea que contenga 'salir'. Puede ser infinitamente inventivo con la forma en que lo maneja. Cuidado con la vieja leyenda urbana de un programa que dejó de funcionar cuando los usuarios empezaron a trabajar con Ecuador. Tecleaban el nombre de la capital, Quito, y el programa terminaba.
Jonathan Leffler
Bien si tú lo dices. ¿Pero por qué no solo echo "$LIST" | sed 1q | ...? Todo depende de lo que se trate. La <<EOFnotación debería requerir un EOF al comienzo de una línea por sí solo en algún lugar del archivo. No lo usaría, creo, pero todavía no estoy seguro de lo que está tratando de lograr. Un simple echo "$LIST" | commando echo $LIST | commandprobablemente será suficiente en la práctica (y es importante que conozca el significado de la diferencia entre los dos).
Jonathan Leffler
16

Me gustó la respuesta de Martin, pero tiene algunos problemas dependiendo de lo que haya en la variable. Esta

your-command <<< """$your_variable"""

es mejor si la variable contiene "o!

Robert Jacobs
fuente
4
¿Pero por qué? Además, no puedo reproducir ningún problema: foo1=-; foo2='"'; foo3=\!; cat<<<"$foo1"; cat<<<"$foo2"; cat<<<"$foo3"funciona bien para mí. ¿Qué hacen exactamente los tres "? AFAIK, solo está anteponiendo y agregando una cadena vacía.
phk
Prueba algo real. Como si tu comando fuera ssh somehost. y su variable es un script de shell.
Robert Jacobs
16
(cat <<END
$passwd
END
) | command

El catno es realmente necesario, pero ayuda a estructurar el código mejor y le permite usar más comandos paréntesis como entrada a su comando.

PoltoS
fuente
Pero este método le permite pasar varias líneas a su comando y también permite espacios en el$passwd
PoltoS
4
Esta es la mejor respuesta hasta ahora que no filtra contenidos variables a la tubería o al proceso de indagación.
user2688272
8

Según la respuesta de Martin, hay una función de bash llamada Here Strings (que en sí misma es una variante de la función Here Documents más ampliamente compatible ).

http://www.gnu.org/software/bash/manual/bashref.html#Here-Strings

3.6.7 Aquí cadenas

Una variante de estos documentos, el formato es:

<<< word

La palabra se expande y se suministra al comando en su entrada estándar.

Tenga en cuenta que Here Strings parecería ser solo bash, por lo que, para una mejor portabilidad, probablemente estaría mejor con la función Here Documents original, según la respuesta de PoltoS:

( cat <<EOF
$variable
EOF
) | cmd

O una variante más simple de lo anterior:

(cmd <<EOF
$variable
EOF
)

Puede omitir (y ), a menos que desee que esto se redirija más a otros comandos.

cnst
fuente
5

Esta forma robusta y portátil ya ha aparecido en los comentarios. Debería ser una respuesta independiente.

printf '%s' "$var" | my_cmd

o

printf '%s\n' "$var" | my_cmd

Notas:

  • Es mejor que echo, las razones están aquí: ¿Por qué es printfmejor que echo?
  • printf "$var"Está Mal. El primer argumento es el formato donde se interpretan%s o gustan varias secuencias . Para pasar la variable correctamente, no debe interpretarse como formato.\n
  • Por lo general, las variables no contienen nuevas líneas finales. El primer comando (con %s) pasa la variable tal como está. Sin embargo, las herramientas que funcionan con texto pueden ignorar o quejarse de una línea incompleta (consulte ¿Por qué los archivos de texto deben terminar con una nueva línea? ). Por lo tanto, es posible que desee el último comando (con %s\n) que agrega un carácter de nueva línea al contenido de la variable. Hechos no obvios:

    • Aquí la cadena en Bash ( <<<"$var" my_cmd) agrega una nueva línea.
    • Cualquier método que agregue una nueva línea da como resultado un stdin de no vacío my_cmd, incluso si la variable está vacía o no está definida.
Kamil Maciorowski
fuente
4

Prueba esto:

echo "$variable" | command
unbeli
fuente
pero, ¿no aparecería la variable $ de contenido en, por ejemplo, la salida de ps -ucuando se está ejecutando echo?
2
no, no lo hará. echoes una función incorporada, por lo que no hay ningún proceso para mostrar en ps
unbeli
2
Cuidado con los espacios en tu variable: echo "$variable"es mejor.
Jonathan Leffler
así es, bash comerá espacios dobles, las conchas más inteligentes no lo harán
unbeli
-1

Solo haz:

printf "$my_var" | my_cmd

Si la var no contiene espacios, las comillas pueden omitirse.
Si usa bash, también puede hacer:

echo -n "$my_var" | my_cmd

Evite el uso de echo sin -n porque canalizará la variable con un salto de línea adicional al final.

Clox
fuente
1
no funciona si $ my_var es %s, printf no imprimirá nada. my_var="%s"; printf "$my_var"; - ¿Quizás intentarlo printf "%s" "$my_var" | my_cmd ?
hanshenrik