Wie übergebe ich den Wert einer Variablen an den Standard eines Befehls?

105

Ich schreibe ein Shell-Skript, das etwas sicher sein sollte, dh keine sicheren Daten über Parameter von Befehlen weiterleitet und vorzugsweise keine temporären Dateien verwendet. Wie kann ich eine Variable an den Standard eines Befehls übergeben? Oder, wenn es nicht möglich ist, wie man temporäre Dateien für eine solche Aufgabe richtig verwendet?

Jonathan Leffler
quelle
Kann sich ein Angreifer ändern $PATH? So dass catersetzt werden kann/bin/cat "$@" | tee /attacker/can/read/this/file
12431234123412341234123

Antworten:

73

Etwas so Einfaches wie:

echo "$blah" | my_cmd
Oliver Charlesworth
quelle
7
Vorsicht vor Leerzeichen in Ihrer Variablen: echo "$blah"ist besser.
Jonathan Leffler
13
Mal sehen, wie Sie damit umgehen blah=-n, blah=-e... printfstattdessen verwenden. unix.stackexchange.com/questions/65803/…
Camilo Martin
1
das scheint fragil und fehleranfällig zu sein, oder?
ThorSummoner
19
6 Jahre später, wenn ich diese Antwort löschen könnte, würde ich (aber ich kann nicht, weil es akzeptiert wurde ...)
Oliver Charlesworth
1
@OliverCharlesworth, warum nicht bearbeiten, um es zu verwenden printf '%s\n' "$blah"? Mit diesem einen Wandel (in vielen Fallen zu vermeiden echo‚s - Spezifikation, die Stephane ausgezeichnete Antwort in auf im Detail geht Warum ist printfbesser als echo? Auf Unix & Linux , oder die die Nutzung von Anwendungen Abschnitt der echoSpezifikation berührt kürzer) ist es ein perfekt vernünftige Antwort.
Charles Duffy
191

Das Übergeben eines Werts an stdinin bash ist so einfach wie:

your-command <<< "$your_variable"

Stellen Sie immer sicher, dass Sie Anführungszeichen um variable Ausdrücke setzen!

Seien Sie vorsichtig, dass dies wahrscheinlich nur in bashund nicht funktioniert sh.

Martin
quelle
39
Herestrings <<<sind garantiert nicht verfügbar, sie befinden sich meines Wissens nicht in POSIX-Basis. Sie funktionieren wie erwartet, solange Sie sie nur ausführen bash. Ich erwähne es nur, weil sie OP sagten "Shell" nicht speziell "Bash". Obwohl er die Frage mit bash... markiert hat, ist dies offensichtlich immer noch eine akzeptable Antwort.
JM Becker
Während dies der Fall sein kann, können vertrauliche Informationen bei Verwendung der echoMethode aufgrund der Erstellung eines Rohrs leichter verloren gehen . Der Herestring-Befehl wird vollständig von verarbeitet bash, obwohl Sie in dieser Situation (wie angegeben) besser wissen sollten, dass er bei Ihrer Bash funktioniert. Andernfalls würde jede Fehlermeldung, die aufgrund der Nichtunterstützung von Herestrings erzeugt wird, diese Informationen ebenfalls verlieren.
Steven Lu
@StevenLu Wait, echoist ein eingebauter. Keine Pfeife da, oder? Es ist Unix, aber es kann nicht so viel Unix sein .
Camilo Martin
4
@StevenLu printf '%s\n' "$var"liefert die gleichen Ergebnisse wie echo "$var", bricht jedoch nicht, wenn z. B. var=-en, und erfordert nicht einmal Bash.
Camilo Martin
1
Beachten Sie auch, dass für diese Zeichenfolgen eine neue Zeile an die Zeichenfolge angehängt wird.
pdr
19

Beachten Sie, dass die ' echo "$var" | commandOperationen bedeuten, dass die Standardeingabe auf die wiedergegebenen Zeilen beschränkt ist. Wenn Sie auch möchten, dass das Terminal angeschlossen wird, müssen Sie schicker sein:

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

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

Dies bedeutet, dass die erste (n) Zeile (n) der Inhalt ist $var, der Rest jedoch aus dem catLesen der Standardeingabe stammt. Wenn der Befehl nichts Besonderes tut (versuchen Sie, die Befehlszeilenbearbeitung zu aktivieren oder wie ausgeführt auszuführen vim), ist dies in Ordnung. Andernfalls müssen Sie wirklich ausgefallen sein - ich denke, expectoder eines seiner Derivate ist wahrscheinlich angemessen.

Die Befehlszeilennotationen sind praktisch identisch - aber das zweite Semikolon ist bei geschweiften Klammern erforderlich, während es nicht bei Klammern steht.

Jonathan Leffler
quelle
( echo "$LIST"; cat - ) | sed 1qDas funktioniert bei mir, aber ich muss Strg drücken, wenn ich dieses Skript ausführe.
Gert Cuykens
Ja; Das cat -liest weiter von der Tastatur bis EOF oder Interrupt, daher müssen Sie es EOF mitteilen, indem Sie Control-D eingeben.
Jonathan Leffler
Gibt es einen Weg um EOF herum? sed braucht Katze
Gert Cuykens
Sie müssen überhaupt nicht verwenden, catwenn Sie keine Terminaleingabe möchten, wie in der ersten Zeile. Oder Sie können cateine Datei auflisten. Oder ... Wenn der Befehl die Terminal-Eingabe lesen soll, müssen Sie ihm mitteilen, wann er das Ende der Eingabe erreicht hat. Oder Sie könnten verwenden ( echo "$var"; sed /quit/q - ) | command; Dies wird fortgesetzt, bis Sie eine Zeile mit 'quit' eingeben. Sie können unendlich erfinderisch sein, wie Sie damit umgehen. Hüten Sie sich vor der alten urbanen Legende eines Programms, das nicht mehr funktioniert, als die Benutzer mit Ecuador zu arbeiten begannen. Sie tippten den Namen der Hauptstadt Quito ein und das Programm wurde beendet.
Jonathan Leffler
Okay, wenn du meinst. Aber warum nicht einfach echo "$LIST" | sed 1q | ...? Es hängt alles davon ab, worum es Ihnen geht. Die <<EOFNotation sollte einen EOF am Anfang einer eigenen Zeile irgendwo in der Datei erfordern. Ich würde es nicht benutzen, denke ich - aber ich bin mir immer noch nicht sicher, was Sie erreichen wollen. Ein einfaches echo "$LIST" | commandoder echo $LIST | commandwird wahrscheinlich in der Praxis ausreichen (und es ist wichtig, dass Sie die Bedeutung des Unterschieds zwischen den beiden kennen).
Jonathan Leffler
16

Ich mochte Martins Antwort, aber es gibt einige Probleme, je nachdem, was in der Variablen enthalten ist. Dies

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

ist besser, wenn Ihre Variable "oder" enthält!

Robert Jacobs
quelle
4
Aber wieso? Außerdem kann ich keine Probleme reproduzieren: foo1=-; foo2='"'; foo3=\!; cat<<<"$foo1"; cat<<<"$foo2"; cat<<<"$foo3"funktioniert gut für mich. Was genau machen die drei "? AFAIK Sie stellen nur eine leere Zeichenfolge voran und fügen sie hinzu.
Phk
Probieren Sie etwas Reales aus. Als ob dein Befehl ssh jemand ist. und Ihre Variable ist ein Shell-Skript.
Robert Jacobs
16
(cat <<END
$passwd
END
) | command

Das catwird nicht wirklich benötigt, aber es hilft, den Code besser zu strukturieren und ermöglicht es Ihnen, mehr Befehle in Klammern als Eingabe für Ihren Befehl zu verwenden.

PoltoS
quelle
Aber diese Methode ermöglicht es Ihnen, mehrere Zeilen an Ihren Befehl zu übergeben und erlaubt auch Leerzeichen in der$passwd
PoltoS
4
Dies ist die bisher beste Antwort, bei der kein variabler Inhalt in das Pipe- oder Prozess-Snooping gelangt.
user2688272
8

Gemäß Martins Antwort gibt es eine Bash-Funktion namens Here Strings (die selbst eine Variante der weiter verbreiteten Here Documents- Funktion ist).

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

3.6.7 Hier Strings

Eine Variante dieser Dokumente ist das Format:

<<< word

Das Wort wird erweitert und dem Befehl an seiner Standardeingabe übergeben.

Beachten Sie, dass Here Strings nur für Bashs gedacht sind. Um die Portabilität zu verbessern, sollten Sie mit der ursprünglichen Here Documents-Funktion gemäß der Antwort von PoltoS wahrscheinlich besser dran sein:

( cat <<EOF
$variable
EOF
) | cmd

Oder eine einfachere Variante des oben genannten:

(cmd <<EOF
$variable
EOF
)

Sie können (und weglassen ), es sei denn, Sie möchten dies weiter in andere Befehle umleiten lassen.

cnst
quelle
5

Diese robuste und tragbare Methode wurde bereits in Kommentaren erwähnt. Es sollte eine eigenständige Antwort sein.

printf '%s' "$var" | my_cmd

oder

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

Anmerkungen:

  • Es ist besser als echo, Gründe sind hier: Warum ist printfbesser als echo?
  • printf "$var"ist falsch. Das erste Argument ist Format , bei den verschiedenen Sequenzen mögen %soder \nwerden interpretiert . Um die Variable richtig zu übergeben, darf sie nicht als Format interpretiert werden.
  • Normalerweise enthalten Variablen keine nachgestellten Zeilenumbrüche. Der vorherige Befehl (mit %s) übergibt die Variable so wie sie ist. Tools, die mit Text arbeiten, können jedoch eine unvollständige Zeile ignorieren oder sich darüber beschweren (siehe Warum sollten Textdateien mit einer neuen Zeile enden? ). Vielleicht möchten Sie den letzteren Befehl (mit %s\n), der dem Inhalt der Variablen ein Zeilenumbruchzeichen hinzufügt. Nicht offensichtliche Tatsachen:

    • Hier <<<"$var" my_cmdhängt der String in Bash ( ) eine neue Zeile an.
    • Jede Methode, die einen Zeilenumbruch anfügt, führt zu einem nicht leeren Standardwert von my_cmd, selbst wenn die Variable leer oder undefiniert ist.
Kamil Maciorowski
quelle
4

Versuche dies:

echo "$variable" | command
unbeli
quelle
Aber würde die Variable $ nicht nicht in der Ausgabe ps -uangezeigt, wenn das Echo ausgeführt wird?
2
Nein, wird es nicht. echoist ein eingebautes, so gibt es keinen Prozess, um in ps zu zeigen
unbeli
2
Vorsicht vor Leerzeichen in Ihrer Variablen: echo "$variable"ist besser.
Jonathan Leffler
das ist richtig, bash wird doppelte Leerzeichen essen, intelligentere Muscheln werden nicht
unbeli
-1

Mach einfach:

printf "$my_var" | my_cmd

Wenn die Variable keine Leerzeichen enthält, können die Anführungszeichen weggelassen werden.
Wenn Sie bash verwenden, können Sie auch Folgendes tun:

echo -n "$my_var" | my_cmd

Vermeiden Sie die Verwendung von Echo ohne -n, da dies die Vraiable mit einem zusätzlichen Zeilenumbruch am Ende weiterleitet.

Clox
quelle
1
funktioniert nicht, wenn $ my_var ist %s, printf druckt nichts. my_var="%s"; printf "$my_var"; - Vielleicht versuchen printf "%s" "$my_var" | my_cmd ?
Hanshenrik