Wie führe ich die Ausgabe eines Befehls in der aktuellen Shell aus?

87

Mir ist das source(aka .) Dienstprogramm bekannt, das den Inhalt aus einer Datei entnimmt und in der aktuellen Shell ausführt.

Jetzt wandle ich Text in Shell-Befehle um und führe sie dann wie folgt aus:

$ ls | sed ... | sh

lsist nur ein zufälliges Beispiel, der Originaltext kann alles sein. sedAuch nur ein Beispiel für die Transformation von Text. Das Interessante ist sh. Ich pfeife alles, was ich habe shund es läuft.

Mein Problem ist, dass ich eine neue Sub-Shell starten muss. Ich möchte lieber, dass die Befehle in meiner aktuellen Shell ausgeführt werden. Wie ich es tun könnte source some-file, wenn ich die Befehle in einer Textdatei hätte.

Ich möchte keine temporäre Datei erstellen, da sie sich schmutzig anfühlt.

Alternativ möchte ich meine Sub-Shell mit genau den gleichen Eigenschaften wie meine aktuelle Shell starten.

aktualisieren

Ok, die Lösungen mit Backtick funktionieren sicherlich, aber ich muss dies oft tun, während ich die Ausgabe überprüfe und ändere. Daher würde ich es sehr bevorzugen, wenn es am Ende eine Möglichkeit gäbe, das Ergebnis in etwas umzuwandeln.

trauriges Update

Ah, das /dev/stdinDing sah so hübsch aus, aber in einem komplexeren Fall funktionierte es nicht.

Also, ich habe folgendes:

find . -type f -iname '*.doc' | ack -v '\.doc$' | perl -pe 's/^((.*)\.doc)$/git mv -f $1 $2.doc/i' | source /dev/stdin

Dadurch wird sichergestellt .doc, dass die Erweiterung aller Dateien in Kleinbuchstaben geschrieben ist.

Und was übrigens gehandhabt werden kann xargs, aber das ist nicht der Punkt.

find . -type f -iname '*.doc' | ack -v '\.doc$' | perl -pe 's/^((.*)\.doc)$/$1 $2.doc/i' | xargs -L1 git mv

Wenn ich das erstere starte, wird es sofort beendet, nichts passiert.

kch
quelle
Funktioniert Ihr komplexer Befehl, wenn Sie zuerst zu einer temporären Datei leiten und diese dann als Quelle verwenden? Wenn nicht, wo liegt das Problem mit der generierten Ausgabe? Die Ausgabe Ihres Befehls funktioniert nicht, wenn Ihre Dateinamen Leerzeichen enthalten oder wenn bestimmte Sequenzen nicht ordnungsgemäß maskiert sind. Ich möchte mindestens Anführungszeichen um $ 1 und $ 2.doc hinzufügen.
Kaleb Pederson
Gibt es einen guten Grund, dies in der Original-Shell ausführen zu müssen? - Diese Beispiele manipulieren die aktuelle Shell nicht, sodass Sie dadurch nichts gewinnen. Die schnelle Lösung besteht darin, die Ausgabe in eine Datei umzuleiten und diese Datei als Quelle zu verwenden
Nr.
@kaleb die Ausgabe läuft gut. in diesem speziellen Fall, auch wenn ich zu sh pfeife. Die Dateinamen sind platzsparend, aber danke für die Notiz. @nos git Umgebungsvariablen auf der ursprünglichen Shell. und wieder sind dies nur Beispiele. Die Frage ist fürs Leben.
kch
source / dev / stdin hat bei mir nicht funktioniert, wenn zugewiesene Variablen benötigt wurden, um zu bleiben. geirha auf freenode bash hat mich auf mywiki.wooledge.org/BashFAQ/024 verwiesen und vorgeschlagen, eine Prozessersetzungsquelle <(Befehl) zu versuchen, die für mich
funktioniert

Antworten:

83
$ ls | sed ... | source /dev/stdin

UPDATE: Dies funktioniert in der Bash 4.0 sowie tcsh und Bindestrich (wenn Sie ändern sourcezu .). Anscheinend war dies in Bash 3.2 fehlerhaft. Aus den Versionshinweisen zu Bash 4.0 :

Es wurde ein Fehler behoben, der "." Fehler beim Lesen und Ausführen von Befehlen aus nicht regulären Dateien wie Geräten oder Named Pipes.

mark4o
quelle
OK. Dann weiter zu Bash 4.0.
kch
Oh, und da Sie die Shells auflisten, funktioniert es auch in zsh.
kch
1
Ich fand das genial, bis ich es in msys / mingw versuchte (wo es keinen / dev-Ordner (oder sogar Geräte) gibt! Ich habe viele Kombinationen von eval, $ () und Backticks ausprobiert. Ich konnte nichts zum Laufen bringen Also habe ich die Ausgabe schließlich einfach in eine temporäre Datei umgeleitet, die temporäre Datei bezogen und entfernt. Grundsätzlich "sed ...> /tmp/$$.tmp &&. /tmp/$$.tmp && rm / tmp / $$. tmp ". Jeder hat eine msys / mingw-Lösung ohne temporäre Dateien ???
chriv
10
Nur eine Bemerkung: Dieser scheint Exportanweisungen nicht wie erwartet zu behandeln. Wenn die Piped-Zeichenfolge eine Exportanweisung enthält, ist die exportet-Variable anschließend in Ihrer Terminalumgebung nicht mehr verfügbar, während der Export mit eval ebenfalls einwandfrei funktioniert.
Phil
1
Angesichts der Tatsache, dass MacOS X normalerweise Bash 3.2 hat (vielleicht hat Yosemite 4.0?) Und der Notwendigkeit von Lastpipe-Tricks in meinem Anwendungsfall (Exportieren aller Variablen in einer Datei durch Vorverarbeitung jeder Zeile mit sed, um 'Export' hinzuzufügen), habe ich mich entschieden um mit dem eval "$(sed ...)"Ansatz zu gehen - aber danke für das 3.2 Bug Heads-Up!
Alex Dupuy
130

Der evalBefehl existiert genau zu diesem Zweck.

eval "$( ls | sed... )"

Mehr aus dem Bash-Handbuch :

eval

          eval [arguments]

Die Argumente werden zu einem einzigen Befehl zusammengefasst, der dann gelesen und ausgeführt wird, und sein Exit-Status wird als Exit-Status von eval zurückgegeben. Wenn keine oder nur leere Argumente vorhanden sind, ist der Rückgabestatus Null.

Juliano
quelle
8
Das einzige Problem hierbei ist, dass Sie möglicherweise einfügen müssen, um Ihre Befehle zu trennen. Ich verwende diese Methode selbst, um unter AIX, Sun, HP und Linux zu arbeiten.
Tanktalus
Tanktalus, danke für diesen Kommentar, er hat gerade mein Skript zum Laufen gebracht. Auf meinem Computer trennt eval keine Befehle in Zeilenumbrüchen und die Verwendung von source funktioniert auch mit Semikolons nicht. Eval mit Semikolons ist die Lösung. Ich wünschte, ich könnte Ihnen einige Punkte geben.
Milan Babuškov
2
@ MilanBabuškov: Ich habe ihn für dich eingestuft ;-)
Phil
3
Das ist ausgezeichnet. Für meinen Anwendungsfall musste ich jedoch $ () in Anführungszeichen setzen: eval "$( ssh remote-host 'cat ~/.bash_profile' )"Beachten Sie, dass ich tatsächlich bash 3.2 verwende.
Zachary Murray
3
Dies funktioniert bei mir, wo die akzeptierte Antwort nicht funktioniert hat.
Dan Tenenbaum
36

Wow, ich weiß, dass dies eine alte Frage ist, aber ich habe in letzter Zeit genau das gleiche Problem (so bin ich hierher gekommen).

Wie auch immer - ich mag die source /dev/stdinAntwort nicht, aber ich denke, ich habe eine bessere gefunden. Eigentlich ist es täuschend einfach:

echo ls -la | xargs xargs

Schön, oder? Tatsächlich macht dies immer noch nicht das, was Sie wollen, denn wenn Sie mehrere Zeilen haben, werden diese zu einem einzigen Befehl zusammengefasst, anstatt jeden Befehl einzeln auszuführen. Die Lösung, die ich gefunden habe, ist:

ls | ... | xargs -L 1 xargs

Die -L 1Option bedeutet, dass Sie (höchstens) 1 Zeile pro Befehlsausführung verwenden. Hinweis: Wenn Ihre Zeile mit einem Leerzeichen endet, wird sie mit der nächsten Zeile verkettet! Stellen Sie also sicher, dass jede Zeile mit einem Leerzeichen endet.

Endlich können Sie tun

ls | ... | xargs -L 1 xargs -t

um zu sehen, welche Befehle ausgeführt werden (-t ist ausführlich).

Hoffe jemand liest das!

Rabensky
quelle
29

Versuchen Sie, die Prozessersetzung zu verwenden , bei der die Ausgabe eines Befehls durch eine temporäre Datei ersetzt wird, die dann bezogen werden kann:

source <(echo id)
Rishta
quelle
6
Was für eine Zauberei ist das?
Paulotorrens
Dies war auch mein erster Gedanke. Obwohl mir die Eval-Lösung besser gefällt. @PauloTorrens <(xyz) führt einfach xyz aus und ersetzt <(xyz) durch den Namen einer Datei, die die Ausgabe von xyz schreibt. Es ist wirklich leicht zu verstehen, wie dies funktioniert, indem man zum Beispiel: echo <(echo id)die Ausgabe gibt /dev/fd/12(12 ist ein Beispiel), cat <(echo id)die Ausgabe idgibt und dann source <(echo id)die gleiche Ausgabe wie beim einfachen Schreiben gibtid
netigger
2
@PauloTorrens, dies wird als Prozessersetzung bezeichnet . Eine offizielle Erklärung finden Sie in den verknüpften Dokumenten. Die kurze Antwort lautet jedoch, dass "<()" eine spezielle Syntax ist, die für solche Fälle entwickelt wurde.
Mark Stosberg
1
@MarkStosberg, wissen Sie, ob dies eine spezielle Syntax oder nur eine (...)umgeleitete Subshell ist ?
Paulotorrens
2
@PauloTorrens, Es ist eine spezielle Syntax nur für die Prozessersetzung.
Mark Stosberg
6

Ich glaube, das ist "die richtige Antwort" auf die Frage:

ls | sed ... | while read line; do $line; done

Das heißt, man kann in eine whileSchleife pfeifen; Der readBefehl befiehlt eine Zeile von seiner stdinund weist sie der Variablen zu $line. $linewird dann der Befehl, der innerhalb der Schleife ausgeführt wird; und es wird fortgesetzt, bis keine weiteren Zeilen in seiner Eingabe vorhanden sind.

Dies funktioniert bei einigen Kontrollstrukturen (wie einer anderen Schleife) immer noch nicht, passt aber in diesem Fall zur Rechnung.

Geoff Nixon
quelle
5
`ls | sed ...`

Ich Art von Gefühl wie ls | sed ... | source -wäre schöner, aber leider sourcenicht verstehen , -zu verstehen stdin.

Chaos
quelle
Fühlt es sich nach der Antwort von mark4o nicht so an, als wäre es die ganze Zeit richtig in unseren Gesichtern?
kch
Heh, ja. Ich erinnere mich nie daran, dass dieses Zeug existiert.
Chaos
1

Ich denke, Ihre Lösung ist die Befehlssubstitution durch Backticks: http://tldp.org/LDP/Bash-Beginners-Guide/html/sect_03_04.html

Siehe Abschnitt 3.4.5

Eric Wendelin
quelle
3
Wenn Sie bash verwenden, wird $( )möglicherweise die Syntax bevorzugt. 'Kurs Backticks arbeiten in mehr Muscheln ...
dmckee --- Ex-Moderator Kätzchen
6
$ (Befehl) und $ ((1 + 1)) funktionieren in allen Posix-Shells. Ich denke, viele werden dadurch abgeschreckt, dass vim sie als Syntaxfehler markiert, aber das liegt nur daran, dass vim die ursprüngliche Bourne-Shell hervorhebt, die nur sehr wenige verwenden. Um vim zum Hervorheben zu bringen, fügen Sie dies in Ihre .vimrc ein: Lassen Sie g: is_posix = 1
Pixelbeat
1

Um die mark4o-Lösung für bash 3.2 (macos) zu verwenden, kann anstelle von Pipelines wie in diesem Beispiel eine Here-Zeichenfolge verwendet werden:

. /dev/stdin <<< "$(grep '^alias' ~/.profile)"
ish-west
quelle
oder verwenden Sie Backticks :. / dev / stdin <<< `grep '^ alias' ~ / .profile`
William H. Hooper
0

Warum dann nicht verwenden source?

$ ls | sed ... > out.sh ; source out.sh
Kaleb Pederson
quelle
Er erwähnte, dass temporäre Dateien icky sind.
Chaos
Oh ja, habe das verpasst :(.
Kaleb Pederson