Wiederverwendung der Ausgabe des letzten Befehls in Bash

Antworten:

184

Sie können verwendet werden, $(!!) um recompute den Ausgang des letzten Befehls (nicht wiederverwenden).

Der !!allein führt den letzten Befehl aus.

$ echo pierre
pierre
$ echo my name is $(!!)
echo my name is $(echo pierre)
my name is pierre
ling
quelle
9
$(your_cmd)Mit Backticks können Sie sich das Tippen ersparen: `your_cmd`Laut Gnu Bash-Handbuch sind sie funktional identisch. Dies unterliegt natürlich auch der Warnung von @memecs
In der Praxis ist die Ausgabe von Befehlen deterministisch, so dass ich mich nicht über die Neuberechnung quälen würde. Dies ist sicherlich nützlich, wenn Sie beispielsweise git diff $(!!)den vorherigen Befehl als findAufruf ausführen möchten .
Sridhar Sarnobat
Anscheinend gibt es gute Gründe, $()anstelle von Backticks zu verwenden. Aber wahrscheinlich nichts, um den Schlaf zu verlieren. github.com/koalaman/shellcheck/wiki/SC2006
Michael Crenshaw
1
@ user2065875 Obwohl Backticks in Bash immer noch funktionieren, werden sie zugunsten von abgelehnt $().
kurczynski
87

Die Antwort ist nein. Bash weist keinem Parameter oder Block in seinem Speicher eine Ausgabe zu. Außerdem dürfen Sie nur über die zulässigen Schnittstellenoperationen auf Bash zugreifen. Auf die privaten Daten von Bash kann nur zugegriffen werden, wenn Sie sie hacken.

konsolebox
quelle
2
Ist es möglich, eine benutzerdefinierte Bash-Middleware-Funktion zu haben, die vor jedem Befehl ausgeführt wird, wobei lediglich die resultierende Ausgabe in einer internen Zwischenablage gespeichert wird (z. B. BASH_CLIPBOARD)?
Pat Needham
@ PatNeedham Nicht sicher. Überprüfen Sie die ladbaren Module. Sehen Sie, ob Sie eine schreiben können.
konsolebox
11

Eine Möglichkeit, dies zu tun, besteht darin, Folgendes zu verwenden trap DEBUG:

f() { bash -c "$BASH_COMMAND" >& /tmp/out.log; }
trap 'f' DEBUG

Jetzt sind die zuletzt ausgeführten Befehle stdout und stderr in verfügbar /tmp/out.log

Der einzige Nachteil ist, dass ein Befehl zweimal ausgeführt wird: einmal, um Ausgabe und Fehler umzuleiten, /tmp/out.logund einmal normal. Wahrscheinlich gibt es auch eine Möglichkeit, dieses Verhalten zu verhindern.

Anubhava
quelle
10
Wenn ich also tippe rm -rf * && cd .., wird nicht nur das aktuelle Verzeichnis, sondern auch das übergeordnete Verzeichnis gelöscht. Dieser Ansatz scheint mir sehr gefährlich
phil294
1
Wie mache ich diese Aktion rückgängig?
Kartik Chauhan
5
@ KartikChauhan: Tun Sie einfachtrap '' DEBUG
Anubhava
7

Wenn Sie auf einem Mac arbeiten und Ihre Ausgabe nicht in der Zwischenablage speichern möchten, anstatt in eine Variable zu schreiben, können Sie pbcopy und pbpaste als Problemumgehung verwenden.

Anstatt dies zu tun, um beispielsweise eine Datei zu finden und ihren Inhalt mit einer anderen Datei zu unterscheiden:

$ find app -name 'one.php' 
/var/bar/app/one.php

$ diff /var/bar/app/one.php /var/bar/two.php

Sie könnten dies tun:

$ find app -name 'one.php' | pbcopy
$ diff $(pbpaste) /var/bar/two.php

Die Zeichenfolge /var/bar/app/one.phpbefindet sich in der Zwischenablage, wenn Sie den ersten Befehl ausführen.

Übrigens, pb in pbcopyund pbpastestehe für Pasteboard, ein Synonym für Zwischenablage.

Raghu Dodda
quelle
1
unter Linux $ find app -name 'one.php' | xclip $ diff $(xclip -o) /var/bar/two.php
Si-Mikey
Interessanterweise hätte ich nie daran gedacht, pbpastezusammen mit der Befehlsersetzung zu verwenden.
Sridhar Sarnobat
Was passiert, wenn ich beim ersten Mal und später feststelle, dass es ineffizient ist? Wie kann ich Rechenzeit sparen, ohne es mit der zweiten Option erneut auszuführen?
NelsonGon
4

Inspiriert von Anubhavas Antwort, die meiner Meinung nach nicht akzeptabel ist, da jeder Befehl zweimal ausgeführt wird.

save_output() { 
   exec 1>&3 
   { [ -f /tmp/current ] && mv /tmp/current /tmp/last; }
   exec > >(tee /tmp/current)
}

exec 3>&1
trap save_output DEBUG

Auf diese Weise befindet sich die Ausgabe des letzten Befehls in / tmp / last und der Befehl wird nicht zweimal aufgerufen.

Olivencoder
quelle
1
Ein Nachteil, den ich dabei festgestellt habe, ist, dass keiner Ihrer Befehle mehr glaubt, dass sie in einem tty ausgeführt werden, was bedeutet, dass Sie besondere Aufmerksamkeit benötigen, damit Farbcodes für Dienstprogramme wie grepoder git diff+1 funktionieren
Marty Neal
2

Wie konsolebox sagte, müssten Sie sich in bash selbst hacken. Hier ist ein ziemlich gutes Beispiel, wie man dies erreichen könnte. Das stderred-Repository (eigentlich zum Färben von stdout gedacht) enthält Anweisungen zum Erstellen.

Ich habe es ausprobiert: Definieren Sie einen neuen Dateideskriptor .bashrcwie

exec 41>/tmp/my_console_log

(Anzahl ist willkürlich) und ändern Sie sie stderred.centsprechend, so dass der Inhalt auch in fd 41 geschrieben wird. Es hat irgendwie funktioniert, enthält aber viele NUL-Bytes, seltsame Formatierungen und ist im Grunde genommen Binärdaten, nicht lesbar. Vielleicht könnte jemand mit einem guten Verständnis von C das ausprobieren.

Wenn ja, ist alles erforderlich, um die letzte gedruckte Zeile zu erhalten tail -n 1 [logfile].

phil294
quelle
1

Ja, warum sollten Sie jedes Mal zusätzliche Zeilen eingeben? Sie können zur Eingabe zurückkehren, aber die Ausgabe zur Eingabe (1> & 0) ist nein. Außerdem möchten Sie nicht immer wieder eine Funktion in jede Datei für dieselbe schreiben.

Wenn Sie die Dateien nirgendwo exportieren und nur lokal verwenden möchten, können Sie die Funktionsdeklaration von Terminal einrichten lassen. Sie müssen die Funktion in ~/.bashrcDatei oder in ~/.profileDatei hinzufügen . Im zweiten Fall müssen Sie aktivieren Run command as login shellaus Edit>Preferences>yourProfile>Command.

Machen Sie eine einfache Funktion, sagen Sie:

get_prev()
{
    # option 1: create an executable with the command(s) and run it
    echo $* > /tmp/exe
    bash /tmp/exe > /tmp/out

    # option 2: if your command is single command (no-pipe, no semi-colons), still it may not run correct in some exceptions
    #echo `$*` > /tmp/out

    # return the command(s) outputs line by line
    IFS=$(echo -en "\n\b")
    arr=()
    exec 3</tmp/out
    while read -u 3 -r line
    do
        arr+=($line)
        echo $line
    done
    exec 3<&-
}

Im Hauptskript:

#run your command:
cmd="echo hey ya; echo hey hi; printf `expr 10 + 10`'\n' ; printf $((10 + 20))'\n'"
get_prev $cmd
#or simply
get_prev "echo hey ya; echo hey hi; printf `expr 10 + 10`'\n' ; printf $((10 + 20))'\n'"

Bash speichert die Variable auch außerhalb des vorherigen Bereichs:

#get previous command outputs in arr
for((i=0; i<${#arr[@]}; i++)); do echo ${arr[i]}; done
Himanshu Tanwar
quelle
1

Sehr einfache Lösung

Eine, die ich seit Jahren benutze.

Skript (zu Ihrem .bashrcoder hinzufügen .bash_profile)

# capture the output of a command so it can be retrieved with ret
cap () { tee /tmp/capture.out}

# return the output of the most recent command that was captured by cap
ret () { cat /tmp/capture.out }

Verwendung

$ find . -name 'filename' | cap
/path/to/filename

$ ret
/path/to/filename

Ich neige dazu | cap, am Ende aller meiner Befehle hinzuzufügen . Auf diese Weise kann ich eine Textverarbeitung für die Ausgabe eines langsam laufenden Befehls durchführen, mit dem ich sie jederzeit abrufen kann res.

Connor
quelle
Was ist der Sinn cat -in cap () { cat - | tee /tmp/capture.out}hier? Tritt cap () { tee /tmp/capture.out }ein Schluckauf auf, den ich vermisse?
Watson
1
@ Watson guter Punkt! Ich weiß nicht, warum ich das dort habe, wahrscheinlich Gewohnheit. Sollte ich cat - | cat - | cat -vorher hinzufügen , um es interessanter zu machen? 😉 Ich werde es reparieren, sobald ich getestet habe, um sicherzustellen, dass es wirklich ein Zwickel ist
Connor
0

Sie wissen nicht genau, wofür Sie dies benötigen, daher ist diese Antwort möglicherweise nicht relevant. Sie können die Ausgabe eines Befehls jederzeit speichern : netstat >> output.txt, aber ich glaube nicht, dass Sie danach suchen.

Es gibt natürlich Programmieroptionen; Sie können einfach ein Programm veranlassen, die obige Textdatei zu lesen, nachdem dieser Befehl ausgeführt wurde, und sie einer Variablen zuordnen. In Ruby, meiner bevorzugten Sprache, können Sie eine Variable aus der Befehlsausgabe mit 'backticks' erstellen:

output = `ls`                       #(this is a comment) create variable out of command

if output.include? "Downloads"      #if statement to see if command includes 'Downloads' folder
print "there appears to be a folder named downloads in this directory."
else
print "there is no directory called downloads in this file."
end

Stecken Sie dies in eine .rb-Datei und führen Sie es aus: ruby file.rbund es wird eine Variable aus dem Befehl erstellt und Sie können sie bearbeiten .

user4493605
quelle
9
"Ich bin mir nicht sicher, wofür Sie das genau brauchen." Nun, sagen Sie, Sie (womit ich meine, ich ) haben gerade ein Skript mit langer Dauer ausgeführt, möchten die Ausgabe durchsuchen und haben dumm vergessen, die Ausgabe vor der Ausführung umzuleiten. Jetzt möchte ich versuchen, meine Dummheit zu beheben, ohne das Skript erneut auszuführen.
Jscs
0

Ich habe die Idee, dass ich keine Zeit habe, um zu versuchen, sofort zu implementieren.

Aber was ist, wenn Sie so etwas tun:

$ MY_HISTORY_FILE = `get_temp_filename`
$ MY_HISTORY_FILE=$MY_HISTORY_FILE bash -i 2>&1 | tee $MY_HISTORY_FILE
$ some_command
$ cat $MY_HISTORY_FILE
$ # ^You'll want to filter that down in practice!

Möglicherweise liegen Probleme mit der E / A-Pufferung vor. Auch die Datei könnte zu groß werden. Man müsste eine Lösung für diese Probleme finden.

William Navarre
quelle
0

Ich denke, die Verwendung eines Skriptbefehls könnte helfen. Etwas wie,

  1. Skript -c bash -qf fifo_pid

  2. Verwenden von Bash-Funktionen zum Festlegen nach dem Parsen.

der Sudhakar
quelle
0

Wenn Sie den vorherigen Befehl nicht neu berechnen möchten, können Sie ein Makro erstellen, das den aktuellen Terminalpuffer durchsucht, versucht, die vorausgesetzte Ausgabe des letzten Befehls zu erraten, in die Zwischenablage zu kopieren und schließlich in das Terminal einzugeben.

Es kann für einfache Befehle verwendet werden, die eine einzelne Ausgabezeile zurückgeben (getestet unter Ubuntu 18.04 mit gnome-terminal).

Installieren Sie die folgenden Tools: xdootool, xclip,ruby

In gnome-terminalunterwegs Preferences -> Shortcuts -> Select allund setzen Sie ihn auf Ctrl+shift+a.

Erstellen Sie das folgende Ruby-Skript:

cat >${HOME}/parse.rb <<EOF
#!/usr/bin/ruby
stdin = STDIN.read
d = stdin.split(/\n/)
e = d.reverse
f = e.drop_while { |item| item == "" }
g = f.drop_while { |item| item.start_with? "${USER}@" }
h = g[0] 
print h
EOF

Fügen Sie in den Tastatureinstellungen die folgende Tastenkombination hinzu:

bash -c '/bin/sleep 0.3 ; xdotool key ctrl+shift+a ; xdotool key ctrl+shift+c ; ( (xclip -out | ${HOME}/parse.rb ) > /tmp/clipboard ) ; (cat /tmp/clipboard | xclip -sel clip ) ; xdotool key ctrl+shift+v '

Die obige Verknüpfung:

  • kopiert den aktuellen Terminalpuffer in die Zwischenablage
  • extrahiert die Ausgabe des letzten Befehls (nur eine Zeile)
  • gibt es in das aktuelle Terminal ein
Giuliano
quelle