Kann zsh auf die Standardausgabe des zuletzt ausgeführten Programms zugreifen?

14

Ich benutze findoder locateum Wege zu finden.

(~) locate foobar.mmpz
/home/progo/lmms/projects/foobar.mmpz

Der nächste Schritt besteht häufig darin, die Dateien zu öffnen oder auf andere Weise zu bearbeiten. In einem glücklichen Fall wie oben kann ich dies tun:

(~) ls `!!`
ls `locate foobar.mmpz`
/home/progo/lmms/projects/foobar.mmpz

Aber niemand ist zu glücklich, wenn es viele Ausgabezeilen gibt, von denen einige möglicherweise keine Pfade oder ähnliches sind. Außerdem ist es auch nicht besonders elegant, potenziell verschwenderische Befehle erneut auszuführen.

Gibt es eine Möglichkeit, zsh anzuschließen, um die stdout für spätere Manipulationen in einem Array zu speichern? Schließlich ist es die Aufgabe der Shell, die Streams an den Benutzer umzuleiten. Ich denke, es könnte die ersten N und letzten N Zeilen in einer Variablen für die sofortige spätere Verwendung speichern, wie $?und andere.

Ok , das ist ziemlich cool: /unix//a/59704/5674 . Ich frage jetzt nach dem zsh-Know-how (und dem Portieren des Codes nach zsh), um diese Art der Erfassung nach jeder Ausführungszeile zu manipulieren.

unperson325680
quelle
Es ist nicht wirklich Aufgabe der Shell, den Stream an den Benutzer weiterzuleiten. Die Anwendungen schreiben ihre Ausgabe auf das Endgerät, die Shell ist daran nicht beteiligt.
Stéphane Chazelas
@StephaneChazelas, aber es ist die Aufgabe der Shell, beispielsweise Streams je nach Benutzeranforderung in Dateien umzuleiten. Es gibt auch Rezepte für zsh, die stderr in rot färben, um es von stdout zu trennen. Ich denke, das zeigt die Rolle der Shell in den Streaming-Angelegenheiten.
unperson325680
Ja, Sie könnten dies mit den Hooks screenor scriptund precmd und preexec tun.
Stéphane Chazelas

Antworten:

6

Bei den meisten Terminalemulatoren gibt es keine Funktion zum Erfassen der Ausgabe vom Bildschirm. Ich erinnere mich an den Autor von xterm (dem "Referenz" -Terminal-Emulator), der sagte, dass es schwierig sein würde, es zu implementieren. Selbst wenn dies möglich wäre, müsste die Shell nachverfolgen, wo die letzte Eingabeaufforderung war.

Sie müssen den Befehl also nur dann erneut ausführen, wenn Sie einen terminalspezifischen manuellen Mechanismus verwenden, z. B. das Kopieren mit der Maus in xterm oder mit der Tastatur in Screen.

Es wäre sehr unpraktisch für die Shell, die Ausgabe von Befehlen automatisch zu erfassen, da sie nicht zwischen Befehlen mit komplexen Terminal- und Benutzerinteraktionen und Befehlen, die einfach druckbare Zeichen ausgeben, unterscheiden kann.

Sie können den Befehl erneut ausführen und seine Ausgabe erfassen. Es gibt verschiedene Möglichkeiten. Um den Befehl erneut auszuführen, können Sie Folgendes verwenden:

  • !! Historiensubstitution - am bequemsten zu tippen;
  • fc -e -, die in einer Funktion verwendet werden können.

Um die Ausgabe zu erfassen, können Sie eine Befehlsersetzung oder eine Funktion wie die folgende verwenden:

K () {
  lines=("${(f@)$(cat)}")
}
!! |K

Dadurch wird das linesArray auf die Ausgabe des Befehls gesetzt, der in das Array geleitet wird.

Gilles 'SO - hör auf böse zu sein'
quelle
So sei es. Ich stelle jetzt vor dem Hintergrund guter Erklärungen fest, dass ein vollständig automatischer Ansatz zu schwierig ist, und das zweitbeste ist so etwas wie das Kvon Ihnen.
unperson325680
3

Hier ist ein erster Schritt, um die letzte Ausgabezeile in eine Variable mit dem Namen zu schreiben $lastline.

precmd () {                                                                                                                                                                                                        
    exec 2>&- >&-
    lastline=$(tail -1 ~/.command.out) 
    sleep 0.1   # TODO: synchronize better
    exec > /dev/tty 2>&1
}

preexec() {
    exec > >(tee ~/.command.out&)
}

Diese nutzt zsh des preexecHaken zu laufen execmit teeeiner Kopie des Befehls des stdout zu speichern, verwendet dann precmddie gespeicherte Ausgabe und wiederherstellen stdout sein nur das Endgerät zur Darstellung der Eingabeaufforderung zu lesen.

Aber es braucht noch etwas Arbeit. Da stdout beispielsweise kein Terminal mehr ist, funktionieren Programme wie vimund lessnicht richtig.

In diesen Fragen sind einige nützliche Informationen enthalten:

Mikel
quelle
Ja, vielleicht funktioniert ein 100% automatischer Ansatz nicht. execDer Code enthält viele Aufrufe. Wird der Befehl mehrmals ausgeführt oder ist mir eine spezielle Semantik aufgefallen?
unperson325680
execOhne Programmname werden nur Umleitungen gesetzt.
Mikel
2

Sie können dies tun, indem Sie einfach Ihre Ergebnisse an weiterleiten tee, wodurch die Ausgabe in einer Datei gespeichert und gleichzeitig angezeigt wird.

Sie können dies beispielsweise tun, um Ihre Ausgabe wie gewohnt anzuzeigen, sie jedoch auch in der Datei zu speichern /tmp/it

locate foobar.mmpz | tee /tmp/it

dann katze diese Datei und greife danach, um Dinge auszuwählen, z

cat /tmp/it | grep progo | grep lms

Um es dann zu verwenden, können Sie einfach Folgendes tun:

vi $(!!)

Brad Parks
quelle
2

Ich habe mir diese halbgebackene Lösung ausgedacht:

alias -g ___='${"${(f@)$(eval "$(fc -ln -1)")}": -1}'

Auf diese Weise können Sie ___an einer beliebigen Stelle in der Befehlszeile schreiben . Der vorherige Befehl wird erneut ausgeführt und ___durch die letzte Zeile seiner Ausgabe ersetzt. Anwendungsbeispiel:

$ touch foo bar baz
$ ls -1
bar
baz
foo
$ vim ___

Der letzte Befehl wird auf erweitert vim foo.

Dies hat einige scharfe Kanten! Wenn Sie ___in einen Befehl einschließen , der vorherige Befehl jedoch auch a enthält ___, bleibt die Shell eine Weile in einem seltsamen Zustand hängen. (Mit Ctrl- können Sie diesen Zustand sofort verlassen C.) Sie können auch nicht drücken Tab, um das zu erweitern ___, wie Sie es mit !$und anderen Konstruktionen können. Und wenn Sie etwas mit einer anderen Ausgabezeile als der letzten tun möchten, hilft Ihnen das natürlich nicht weiter.

(Ich habe den Namen ausgewählt, ___weil ich ihn niemals als Wort in eine Befehlszeile aufnehmen wollte, auch nicht als Argument. Sie könnten einen anderen Namen auswählen, aber achten Sie darauf, dass Sie nicht versehentlich etwas auswählen, das für Sie erweitert wird .)

bdesham
quelle