Gibt es eine Möglichkeit die Ausgabe von einer Funktion zur anderen zu Rohr, aber nur , wenn es ist eine Ausgabe?
Gibt es eine Möglichkeit die Ausgabe von einer Funktion zur anderen zu Rohr, aber nur , wenn es ist eine Ausgabe?
Antworten:
Die folgende ifnotempty
Funktion leitet ihre Eingabe an den als Argument übergebenen Befehl weiter, außer dass sie nichts tut, wenn die Eingabe leer ist. Verwenden , um es Rohr source --foo
in sink --bar
durch Schreiben source --foo | pipe_if_not_empty sink --bar
.
pipe_if_not_empty () {
head=$(dd bs=1 count=1 2>/dev/null; echo a)
head=${head%a}
if [ "x$head" != x"" ]; then
{ printf %s "$head"; cat; } | "$@"
fi
}
Konstruktionshinweise:
dd
mehr als das Byte gelesen wird, das über die Standardeingabe gelesen werden soll.head -c 1
wäre ein geeigneter Ersatz für dd bs=1 count=1 2>/dev/null
Linux.head -n 1
wäre dies nicht geeignet, da head
normalerweise die Eingabe zwischengespeichert wird und möglicherweise mehr als die Zeile gelesen wird, die ausgegeben wird. Da die Daten aus einer Pipe gelesen werden, gehen nur die zusätzlichen Bytes verloren.read -r head
und sind auch read -r -n 1 head
hier nicht geeignet, da, wenn das erste Zeichen eine neue Zeile ist, head
auf die leere Zeichenkette gesetzt würde, was es unmöglich macht, zwischen leerer Eingabe und Eingabe, die mit einer leeren Zeile beginnt, zu unterscheiden.head=$(head -c 1)
denn wenn das erste Zeichen eine neue Zeile ist, wird durch die Befehlsersetzung die letzte neue Zeile entfernt, wodurch es unmöglich wird, zwischen leerer Eingabe und Eingabe zu unterscheiden, die mit einer leeren Zeile beginnt.cat
durch </dev/stdin
eine mikroskopische Leistungssteigerung ersetzen .Wenn es Ihnen nichts ausmacht, die gesamten Zwischendaten im Speicher abzulegen, ist hier eine etwas einfachere Implementierung von pipe_if_not_empty
.
pipe_if_not_empty () {
input=$(cat; echo a);
if [ "x$input" != x"a" ]; then
{ printf %s "${input%a}"; } | "$@"
fi
}
Hier ist eine etwas einfachere Implementierung mit den folgenden Einschränkungen:
Auch hier werden die gesamten Daten im Speicher gespeichert.
pipe_if_not_empty () {
input=$(cat);
if [ "x$input" != x"" ]; then
{ printf '%s\n' "${input}"; } | "$@"
fi
}
Dies sollte für Sie arbeiten
$ --a function-- | [ xargs -r ] --another function--
Ein Beispiel
$ echo -e "\n\n" | xargs -r ls
$ # No output. ls did not run.
$ echo -e "\n\n1" | xargs -r ls
ls: cannot access 1: No such file or directory
Es ist einfach, aber es sollte für Sie funktionieren. Wenn Ihre "a-Funktion" eine leere Zeichenfolge oder sogar eine neue Zeile in der Pipeline sendet, verhindert xargs -r den Durchgang zu "einer anderen Funktion".
Referenz für xargs: http://www.oreillynet.com/linux/cmd/cmd.csp?path=x/xargs
-r, --no-run-if-empty
Do not run command if standard input contains only blanks.
Genau das macht ifne (1) von moreutils . Moreutils ist zumindest in Debian und Ubuntu als Paket verfügbar, wahrscheinlich auch in anderen Distributionen.
Die folgende Funktion versucht, das erste Byte zu lesen, und bei Erfolg wird dieses Byte wiedergegeben, und der Rest wird übertragen. Sollte effizient und 100% portabel sein.
if_read() {
IFS="" read -rN 1 BYTE && { echo -nE "$BYTE"; cat; } | "$@";
}
Testfälle:
$ echo -n | if_read wc -c
$ echo | if_read wc -c
1
$ echo -en "\nX" | if_read wc -c
2
$
echo -en "\nX" | pipe_if_not_empty mail -s "Subject line here" foo@bar.com
. Es denkt, dass line
und here
beide Empfänger der E-Mail sind, keine Token im Betreff. Ich muss dem entkommen"
dem Thema , damit es funktioniert. Die pipe_if_not_empty
Funktion aus der akzeptierten Antwort funktioniert jedoch auch für mich, ohne etwas zu umgehen.
Zumindest funktioniert so etwas:
yourcommand | if [ $(wc -c) -gt "0" ]; then yourothercommand; fi
Beachten Sie, dass bei den obigen Anweisungen Zeilenumbrüche und andere Sonderzeichen als Ausgabe betrachtet werden. Daher wird eine leere Zeile an die if-Anweisung übergeben und als Ausgabe betrachtet. Erhöhen Sie einfach das -gt-Limit, wenn Ihre Ausgabe normalerweise höher als 1 Byte sein soll :)
yourothercommand
sieht nie die Ausgabe von yourcommand
.
Anstelle von sender | receiver
:
tester () { local a=$(</dev/stdin); if [[ $a ]]; then printf '%s\n' "$a" | receiver; fi; }
sender | tester
Oder Sie könnten es allgemeiner machen, indem Sie es ändern, um das empfangende Programm als Argument zu akzeptieren, wie in Gilles 'Antwort:
tester () { local a=$(</dev/stdin); if [[ $a ]]; then printf '%s\n' "$a" | "$@"; fi; }
sender | tester receiver