Machen Sie eine Rohrleitung von einer nicht leeren Rückgabe abhängig


16

Gibt es eine Möglichkeit die Ausgabe von einer Funktion zur anderen zu Rohr, aber nur , wenn es ist eine Ausgabe?


Antworten:


9

Die folgende ifnotemptyFunktion 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 --fooin sink --bardurch 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:

  • Ich würde erwarten, dass diese Implementierung auf allen POSIX / Unix-Plattformen funktioniert, obwohl sie streng genommen nicht standardkonform ist: Sie basiert darauf, dass nicht ddmehr als das Byte gelesen wird, das über die Standardeingabe gelesen werden soll.
  • Ich denke, head -c 1wäre ein geeigneter Ersatz für dd bs=1 count=1 2>/dev/nullLinux.
  • Auf der anderen Seite head -n 1wäre dies nicht geeignet, da headnormalerweise 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 headund sind auch read -r -n 1 headhier nicht geeignet, da, wenn das erste Zeichen eine neue Zeile ist, headauf die leere Zeichenkette gesetzt würde, was es unmöglich macht, zwischen leerer Eingabe und Eingabe, die mit einer leeren Zeile beginnt, zu unterscheiden.
  • Wir können nicht einfach schreiben, 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.
  • In bash, ksh oder zsh können Sie catdurch </dev/stdineine 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:

  • Die von der Quelle erzeugten Daten gelten nur dann als leer, wenn sie ausschließlich aus Zeilenumbrüchen bestehen. (Dies kann in der Tat wünschenswert sein.)
  • Die in die Senke eingegebenen Daten enden mit genau einem Zeilenumbruchzeichen, unabhängig davon, mit wie vielen Zeilenumbrüchen die von der Quelle erzeugten Daten enden. (Dies könnte ein Problem sein.)

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
}

17

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.


4

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
$

Diese Funktion schlägt bei mir beim Laufen fehl echo -en "\nX" | pipe_if_not_empty mail -s "Subject line here" foo@bar.com. Es denkt, dass lineund herebeide Empfänger der E-Mail sind, keine Token im Betreff. Ich muss dem entkommen" dem Thema , damit es funktioniert. Die pipe_if_not_emptyFunktion aus der akzeptierten Antwort funktioniert jedoch auch für mich, ohne etwas zu umgehen.
Kuzzooroo

2

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 :)


yourothercommandsieht nie die Ausgabe von yourcommand.
Bis auf weiteres angehalten.

2

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
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.