Come passare il valore di una variabile allo stdin di un comando?


105

Sto scrivendo uno script di shell che dovrebbe essere in qualche modo sicuro, cioè non passa dati protetti attraverso i parametri dei comandi e preferibilmente non utilizza file temporanei. Come posso passare una variabile allo stdin di un comando? Oppure, se non è possibile, come utilizzare correttamente i file temporanei per tale attività?


Un attaccante può cambiare $PATH? In modo che catpossa essere sostituito, essere/bin/cat "$@" | tee /attacker/can/read/this/file
12431234123412341234123

Risposte:


74

Qualcosa di semplice come:

echo "$blah" | my_cmd

7
Fai attenzione agli spazi nella tua variabile: echo "$blah" è meglio.
Jonathan Leffler

13
Vediamo come gestisci blah=-n, blah=-e... usa printfinvece. unix.stackexchange.com/questions/65803/...
Camilo Martin

1
questo sembra che sarebbe fragile e soggetto a errori, no?
ThorSummoner

20
6 anni dopo, se potessi cancellare questa risposta lo farei (ma non posso perché è stata accettata ...)
Oliver Charlesworth

1
@OliverCharlesworth, perché non modificarlo per usarlo printf '%s\n' "$blah"? Con quell'unico cambiamento (evitando le molte insidie ​​nelle echospecifiche, di cui l'eccellente risposta di Stephane entra in dettaglio in Perché è printfmeglio di echo? Su Unix e Linux , o su cui la sezione USO DELL'APPLICAZIONE della echospecifica tocca più brevemente) è perfettamente risposta ragionevole.
Charles Duffy,

192

Passare un valore a stdinin bash è semplice come:

your-command <<< "$your_variable"

Assicurati sempre di inserire virgolette attorno alle espressioni variabili!

Sii cauto, questo probabilmente funzionerà solo in bashe non funzionerà in sh.


39
Non <<<è garantito che le stringhe siano disponibili, non sono nella base POSIX, per quanto ne so. Funzioneranno come previsto, a condizione che tu li esegua solo bash. Lo menziono solo, perché OP ha detto "shell" non specificamente "bash". Anche se ha etichettato la domanda con bash... quindi questa è ancora ovviamente una risposta accettabile.
JM Becker

Anche se questo può essere il caso, è probabile che le informazioni sensibili vengano trapelate più facilmente se si utilizza il echometodo a causa della creazione di una pipe. Il comando herestring verrà elaborato interamente da bash, anche se in questa situazione (come notato) è meglio che tu sappia che funzionerà sulla tua bash, altrimenti qualsiasi messaggio di errore prodotto a seguito del mancato supporto di herestrings perderebbe anche dette informazioni.
Steven Lu

@StevenLu Wait, echoè un built-in. Nessun tubo lì, giusto? È Unix, ma non può essere così tanto Unix .
Camilo Martin

4
@StevenLu printf '%s\n' "$var"produce gli stessi risultati echo "$var"ma non si interromperà se, ad esempio var=-en, e non richiede nemmeno bash.
Camilo Martin

1
Notare anche che una nuova riga viene aggiunta alla stringa per le stringhe here.
pdr

19

Notare che le echo "$var" | commandoperazioni ' significano che lo standard input è limitato alle righe echeggiate. Se vuoi che anche il terminale sia connesso, dovrai essere più elaborato:

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

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

Ciò significa che la prima riga sarà il contenuto di, $varma il resto verrà dalla catlettura del suo input standard. Se il comando non fa nulla di troppo stravagante (prova ad attivare la modifica della riga di comando o esegui come vimfa), allora andrà bene. Altrimenti, devi essere davvero stravagante, pensoexpect o uno dei suoi derivati ​​potrebbe essere appropriato.

Le notazioni della riga di comando sono praticamente identiche, ma il secondo punto e virgola è necessario con le parentesi graffe mentre non è con le parentesi.


( echo "$LIST"; cat - ) | sed 1qquesto funziona per me, ma devo premere ctrl d quando eseguo questo script?
Gert Cuykens

Sì; il cat -continua a leggere dalla tastiera fino a EOF o di interruzione, quindi è necessario raccontarla EOF digitando control-D.
Jonathan Leffler

c'è un modo per aggirare l'EOF? sed ha bisogno di un gatto
Gert Cuykens

Non devi usare cat affatto se non si desidera l'ingresso da terminale, come nella prima riga. Oppure puoi usare catper elencare un file. Oppure ... Se vuoi che il comando legga l'input del terminale, devi dirgli quando ha raggiunto la fine dell'input. Oppure potresti usare ( echo "$var"; sed /quit/q - ) | command; questo continua finché non digiti una riga contenente "quit". Puoi essere infinitamente inventivo su come gestirlo. Fai attenzione alla vecchia leggenda metropolitana di un programma che ha smesso di funzionare quando gli utenti hanno iniziato a lavorare con l'Ecuador. Digitavano il nome della capitale, Quito, e il programma si chiudeva.
Jonathan Leffler,

Ok se lo dici tu. Ma perché non solo echo "$LIST" | sed 1q | ...? Tutto dipende da cosa stai facendo. La <<EOFnotazione dovrebbe richiedere un EOF all'inizio di una riga da solo da qualche parte nel file. Non lo userei, penso, ma non sono ancora sicuro di cosa stai cercando di ottenere. Un semplice echo "$LIST" | commando echo $LIST | commandprobabilmente sarà sufficiente in pratica (ed è importante che tu conosca il significato della differenza tra i due).
Jonathan Leffler

16

Mi è piaciuta la risposta di Martin, ma ha alcuni problemi a seconda di cosa c'è nella variabile. Questo

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

è meglio se la tua variabile contiene "o!


4
Ma perché? Inoltre, non riesco a riprodurre alcun problema: foo1=-; foo2='"'; foo3=\!; cat<<<"$foo1"; cat<<<"$foo2"; cat<<<"$foo3"funziona bene per me. Cosa fanno esattamente i tre "? Per quanto ne so, stai solo anteponendo e aggiungendo una stringa vuota.
phk

Prova qualcosa di reale. Come se il tuo comando fosse ssh somehost. e la tua variabile è uno script di shell.
Robert Jacobs

16
(cat <<END
$passwd
END
) | command

Non catè realmente necessario, ma aiuta a strutturare meglio il codice e consente di utilizzare più comandi tra parentesi come input per il comando.


Ma questo metodo ti consente di passare più righe al tuo comando e consente anche spazi in$passwd
PoltoS

4
Questa è la migliore risposta finora che non fa trapelare contenuti variabili per pipe o processare lo snooping.
user2688272

8

Secondo la risposta di Martin, esiste una funzione bash chiamata Here Strings (che a sua volta è una variante dei documenti Here più ampiamente supportati funzionalità ).

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

3.6.7 Here Strings

Una variante dei documenti qui, il formato è:

<<< word

La parola viene espansa e fornita al comando nel suo standard input.

Nota che Here Strings sembrerebbe essere solo bash, quindi, per una migliore portabilità, probabilmente staresti meglio con la funzione Here Documents originale, come da risposta di PoltoS:

( cat <<EOF
$variable
EOF
) | cmd

Oppure, una variante più semplice di quanto sopra:

(cmd <<EOF
$variable
EOF
)

Puoi omettere (e ), a meno che tu non voglia reindirizzarlo ulteriormente ad altri comandi.


5

Questo modo robusto e portatile è già apparso nei commenti. Dovrebbe essere una risposta autonoma.

printf '%s' "$var" | my_cmd

o

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

Appunti:

  • È meglio di echo, le ragioni sono qui: perché è printfmeglio di echo?
  • printf "$var"è sbagliato. Il primo argomento è formato in cui vengono interpretate varie sequenze come %so\n . Per passare la variabile a destra, non deve essere interpretata come formato.
  • Di solito le variabili non contengono nuove righe finali. Il primo comando (con %s) passa la variabile così com'è. Tuttavia, gli strumenti che funzionano con il testo possono ignorare o lamentarsi di una riga incompleta (vedere Perché i file di testo dovrebbero terminare con una nuova riga? ). Quindi potresti volere l'ultimo comando (con %s\n) che aggiunge un carattere di nuova riga al contenuto della variabile. Fatti non ovvi:

    • Qui stringa in Bash (<<<"$var" my_cmd ) aggiunge una nuova riga.
    • Qualsiasi metodo che aggiunge una nuova riga risulta in uno stdin non vuoto di my_cmd, anche se la variabile è vuota o non definita.

4

Prova questo:

echo "$variable" | command

ma la variabile $ del contenuto non sarebbe mostrata, ad esempio, nell'output di ps -uquando echo è in esecuzione?

2
no non lo farà. echoè un built-in, quindi non c'è alcun processo da mostrare in ps
unbeli

2
Fai attenzione agli spazi nella tua variabile:echo "$variable" è meglio.
Jonathan Leffler


-1

Basta fare:

printf "$my_var" | my_cmd

Se la variabile non contiene spazi, le virgolette possono essere omesse.
Se usi bash, puoi anche fare:

echo -n "$my_var" | my_cmd

Evita di usare echo senza -n perché reindirizzerà il vraiable con un'interruzione di riga aggiunta alla fine.


1
non funziona se $ my_var è %s, printf non stamperà nulla. my_var="%s"; printf "$my_var"; - forse provare printf "%s" "$my_var" | my_cmd ?
hanshenrik
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.