Wie kann ich die Ausgabe von einem Prozess zu einem anderen leiten, aber nur ausführen, wenn der erste eine Ausgabe hat?

23

Wie kann ich diesen Befehl so umschreiben, dass nur E-Mails gesendet werden, die von der ausgegeben werden mailq | grep?

mailq | egrep 'rejected|refused' -A 5 -B 5 | mail -s 'dd' email@email

Ist das überhaupt in einer Zeile möglich?

Siehe Überprüfen Sie, ob Rohr leer ist , und einen Befehl auf dem Daten ausgeführt werden, wenn dies nicht der Fall für einen allgemeineren Fall als E - Mail zu senden.

cosmin
quelle

Antworten:

19

Ich denke, Sie müssen eine temporäre Datei für diesen Vorgang verwenden, damit Sie die verwenden können && Operator verwenden können, um den E-Mail-Befehl nur auszuführen, wenn das grep einen Beendigungsstatus zurückgegeben hat, der besagt, dass Übereinstimmungen wie diese vorliegen:

TMPFILE=`mktemp /tmp/mailqgrep.XXXXXX`; mailq | egrep 'rejected|refused' -A5 -B5 > "$TMPFILE" && mail -s 'dd' email@email < "$TMPFILE"; rm "$TMPFILE"

Wenn es Ihnen nichts ausmacht, wenn die temporäre Datei irgendwo bleibt und Sie einen statischen Namen dafür verwenden können, können Sie das spezielle Benennungs- und Lösch-Zeug überspringen:

 mailq | egrep 'rejected|refused' -A5 -B5 > /tmp/mailqgrep && mail -s 'dd' email@email < /tmp/mailqgrep

Bearbeiten: Nachdem ich Glenns Antwort gesehen hatte, spielte ich noch ein bisschen damit und anscheinend gab die Zuweisung einer Variablen mithilfe der $()Syntax den Exit-Code des Befehls zurück, sodass Sie den Test, den er für die String-Länge verwendet hat, überspringen und stattdessen verwenden können. Hier ist alles in einem Befehl:

data=$(mailq | egrep 'rejected|refused' -A 5 -B 5) && mail -s 'dd' email@email <<< "$data"

Edit 2: Nachdem ich Simons Antwort gesehen hatte, habe ich mein mailProgramm ausgecheckt. Es verhält sich nicht so, wie er es standardmäßig beschreibt, hat aber eine Option dafür. Von der Manpage:

-EWenn eine ausgehende Nachricht im ersten oder einzigen Nachrichtenteil keinen Text enthält, senden Sie sie nicht, sondern verwerfen Sie sie unbeaufsichtigt. Dadurch wird die Variable "skipemptybody" beim Programmstart wirksam gesetzt. Dies ist nützlich, um Nachrichten von Skripten zu senden, die von cron (8) gestartet wurden.

Möglich machen:

mailq | egrep 'rejected|refused' -A 5 -B 5 | mail -E -s 'dd' email@email
Caleb
quelle
1
die macht der zusammenarbeit !!
Glenn Jackman
3
Ich würde vorschlagen, dass diese Antwort verbessert werden könnte, indem die beste Lösung nach oben verschoben wird.
Mark Booth
@Wildcard Diese Bearbeitung war nicht erforderlich, da die Eingabeparameter mktempnichts zurückgeben, was zitiert werden muss.
Caleb
2
Ich kenne. Sie sind einer der wenigen , die verstehen stört , dass Zitate sind manchmal notwendig (und auslässt sie absichtlich). Die Standardeinstellung sollte jedoch Anführungszeichen sein, es sei denn, Sie möchten ausdrücklich anrufen glob(split(var)). Sie brauchen hier keine Wortteilung oder Dateiglob-Erweiterung, fragen Sie also nicht danach. Außerdem müssen wir auf dieser Website den richtigen Weg zeigen .
Wildcard
@Wildcard Fair genug. Normalerweise laufe ich zshund bekomme in solchen Fällen kein Globbing oder keine Wortspaltung, es sei denn, ich frage nach ihnen, aber um hier Antworten zu finden und die Zielumgebung nicht zu kennen, ist es in Ordnung, aus Prinzip zu zitieren.
Caleb
11

Das Hilfsprogramm ifne existiert ausdrücklich zu diesem Zweck: um einen Befehl auszuführen, wenn die Standardeingabe nicht leer ist.

mailq | egrep 'rejected|refused' -A 5 -B 5 | ifne mail -s 'dd' email@email

Der Befehl ifne ist Teil des moreutils- Pakets, das als "wachsende Sammlung von Unix-Werkzeugen" bezeichnet wird, die vor langer Zeit, als Unix noch jung war, noch niemand geschrieben hatte.

Guy Hillyer
quelle
8

Sie können die Ausgabe in einer Variablen speichern und dann den Befehl mail aufrufen, wenn die Länge der Zeichenfolge nicht Null ist.

data=$(mailq | egrep 'rejected|refused' -A 5 -B 5)
[[ -n "$data" ]] && mail -s 'dd' email@email <<< "$data"

Verbinden Sie für eine Zeile die beiden Befehle mit einem Semikolon.

Glenn Jackman
quelle
1
Requisiten für die Verwendung einer Variablen anstelle einer temporären Datei. Dadurch habe ich darüber nachgedacht, wohin der Exit-Code von dem Befehl stammt, mit dem Sie die Variablenzuweisung durchgeführt haben, und anscheinend wird er weitergeleitet! Siehe meine aktualisierte Antwort .
Caleb
7

Verwenden Sie mailmit der -EOption. Auf meinem Mac sagt MAIL (1), dass die -EFlagge keine E-Mails mit einem leeren Körper sendet.

-E Senden Sie keine Nachrichten mit leerem Text. Dies ist nützlich, um Fehler aus cron (8) -Skripten weiterzuleiten.

Auf meinem Mac habe ich den folgenden Test ausgeführt. Beachten Sie, dass die Datei file_does_existvorhanden ist, aber die Datei file_does__not_existnicht vorhanden ist.

Dies schickt mir eine E-Mail:

$ ls file_does_exist | egrep 'file' -A5 -B5 | mailx -E -s 'test' stefan@example.org

Das tut nicht. Beachten Sie, dass der Befehl ls file_does_not_exist | egrep ...keine Ausgabe erzeugt.

$ ls file_does_not_exist | egrep 'file' -A5 -B5 | mailx -E -s 'test' stefan@example.org
ls: file_does_not_exist: No such file or directory
Stefan Lasiewski
quelle
Caleb hat dich geschlagen.
Gilles 'SO- hör auf böse zu sein'
1
Er hat mich nur um 4,75 Stunden geschlagen :). Ich habe dieses Detail in seiner langen Antwort verpasst.
Stefan Lasiewski
5

In diesem speziellen Fall verwenden Sie diese Option einfach , wenn sie von Ihnen mailunterstützt wird-E . Im allgemeinen Fall können Sie versuchen, ein Zeichen zu lesen. Wenn es einen gibt, starten Sie den Nachverarbeitungsbefehl und geben Sie das Zeichen aus dem Rest der Datei ein.

pipe_if_not_empty () {
  a=$(dd bs=1 count=1 2>/dev/null; echo .)  # read at most one character
  if [ "$a" != "." ]; then                  # if there were two characters,
    { printf %s "${a%.}";                   # then output the first character
      cat; } |                              # and the rest of the input   
    "$@"                                    # into the specified program
  fi
}

mailq | egrep 'rejected|refused' -A 5 -B 5 |
pipe_if_not_empty mail -s 'dd' email@email

Beachten Sie die nützliche Verwendung von cat. Das Hinzufügen von echo .bewirkt, dass diese Funktion auch dann funktioniert, wenn das erste Zeichen in der Eingabe eine Newline ist (denken Sie daran, dass das $(…)Konstrukt terminale Newlines entfernt).

Wenn die Datei bei den meisten Shells (soweit ich weiß, nicht zsh) mit einem Nullzeichen beginnt, wird dieser Code glauben, dass sie leer ist. Fixierung, die als Übung für den Leser bleibt. (Hinweis: Verwenden Sie diese odOption in der ersten Unterschale printf, um das erste Byte zu drucken.) (Lösung: Überprüfen, ob die Pipe leer ist. ) Das gleiche Problem tritt möglicherweise auf, wenn die Datei mit einem Byte beginnt, das im aktuellen Zeichen nicht gültig ist locale; Das ist einfacher zu beheben, indem Sie diesen Code mit ausführen LC_ALL=C.

Gilles 'SO - hör auf böse zu sein'
quelle
+1 für lächerlich, aber möglicherweise nützlich in einem anderen Szenario :) Warum aus Neugier auf den Punkt spritzen und testen, anstatt auf eine leere Zeichenfolge zu testen? Führt das nicht zu einem Problem, wenn die Eingabe mit einem Punkt beginnt? Könnte die Verwendung von readHilfe dies vereinfachen?
Caleb
@Caleb: Ja, ich hätte erwähnen sollen, dass ich auf die Frage im Titel antworte und nicht auf die besondere Situation. Eine Erklärung des Punkts finden Sie in meiner Bearbeitung. Ich denke nicht, dass Sie eine leere erste Zeile von einer leeren Datei mit readin portable sh unterscheiden können (es kann in bash, ksh oder zsh möglich sein).
Gilles 'SO - hör auf böse zu sein'
3

IIRC Der BSD- mailBefehl wird abgebrochen, wenn eine leere Nachricht ausgegeben wird.

Simon Richter
quelle
1
Das Mail-Programm auf meinem GNU Linux-System (Heirloom mailx 12.4) verhält sich nicht standardmäßig so, hat aber eine -EOption, um es zu aktivieren .
Caleb
1

Kürzlich habe ich über den Befehlsnamen erfahren, der ifneTeil des moreutilsPakets ist. Wo können Sie es verwenden, um dieses spezielle Problem zu lösen.

ifne - Befehl ausführen, wenn die Standardeingabe nicht leer ist

Beispiel aus der Manpage,

EXAMPLE
       find . -name core | ifne mail -s "Core files found" root
Rabin
quelle
0

Sie können Ihren Befehl mit umschreiben, um test -s /dev/stdinzu überprüfen, ob das mailq | grepTeil eine Ausgabe enthält .

- mailq | egrep 'rejected|refused' -A 5 -B 5 | mail -s 'dd' email@email
+ mailq | egrep 'rejected|refused' -A 5 -B 5 | (test -s /dev/stdin && cat) | mail -s 'dd' email@email
trent55
quelle