Wie kann ein Bash-Skript seine Standardausgabe innerhalb einer Pipeline schreiben?

Wie kann ein Bash-Skript seine Standardausgabe innerhalb einer Pipeline schreiben?

Ich habe ein Bash-Skript, das eine Funktion aufruft. Die Funktion führt unter anderem eine Pipeline aus, die ihre Ausgabe verarbeitet. Um es zu vereinfachen, hier ein konstruiertes Beispiel:

#!/bin/bash
func() {
  ls "$@" | sort | rev > /tmp/output
}
func "$@"

Sie würden dann etwas wie das Folgende tun, um es auszuführen. Es würde seine Arbeit erledigen und seine Nutzlast in eine Datei übertragen. Es erfolgt keine Ausgabe auf dem Bildschirm.

$ ./myscript .

Angenommen, ich möchte die Ausgabe sortauf der Standardausgabe. Wie würde ich das tun?

Ich kann es so erreichen:

ls "$@" | sort | tee /dev/tty | rev > /tmp/output

Die Verwendung ist jedoch /dev/ttyfalsch, da dies nicht funktioniert:

$ ./myscript > myfile

Gibt es eine korrektere Möglichkeit, innerhalb einer Pipeline innerhalb einer Funktion auf die Standardausgabe eines Bash-Skripts zu verweisen?

(Ich verwende Bash 4.3.0 unter Arch Linux)

Antwort1

Der einfachste Weg, der mir hierfür einfällt, ist:

ls "$@" | sort | tee >(rev > /tmp/output)

Das teesendet eine Kopie an STDOUT, und da danach kein mehr folgt |, wird dies übernommen, d. h. es geht an das TTY, wenn es nicht umgeleitet wird, und an Ihr, myfilewenn es umgeleitet wird.
Die andere Kopie wird an rev > /tmp/outputan dessen STDIN gesendet. In Bash >(...)führt es alles aus, was zwischen den Klammern steht, und ersetzt dann das >(...)durch den Pfad zu einer Pipe, die mit seinem STDIN verbunden ist.

Antwort2

Da die andere Antwort diesbezüglich nicht klar ist, ist der andere (andere) Weg

exec 3>&1
ls | sort | tee /dev/fd/3 | rev > /tmp/output

Derexec 3>&1 dupkompiliert Dateideskriptor 1 (stdout) als Dateideskriptor 3. Dann tee /dev/fd/3schreibt es eine Kopie der sortAusgabe von in diesen Dateideskriptor. Dies sollte in jeder Shell funktionieren, kann aber vom Betriebssystem abhängen. Eine Variante, die betriebssystemunabhängig, aber bash-spezifisch sein sollte, ist:

exec 3>&1
ls | sort | tee >( cat >&3 ) | rev > /tmp/output

In erhalten Sie einen Datei-Handle zu einer Pipe zu einem laufenden Prozess bash.>(command)command. In jeder (?) Shell läuftcommand >&fdcommand wobei seine Standardausgabe zum angegebenen Dateideskriptor umgeleitet wird, der zuvor eingerichtet worden sein muss.

Eine kompliziertere Version, die auf jedem *nix (Posix)-System funktionieren sollte, ist

pipe_to_stdout=$(mktemp -u)
mkfifo "$pipe_to_stdout"
cat "$pipe_to_stdout" &
ls | sort | tee "$pipe_to_stdout" | rev > /tmp/output
rm "$pipe_to_stdout"

Das ist so ziemlich dasselbe wie das zweite Beispiel (das catLesen aus dem FIFO und das Schreiben in die Standardausgabe), aber mit weniger Blendwerk auf der Shell-Ebene.

Beachten Sie, dass diese Antworten flexibel sind, d. h. Sie können die Ausgabe lseinfach durch Neuanordnen der Befehle in die Standardausgabe schreiben.

verwandte Informationen