Comment diriger la sortie d'un processus vers un autre mais ne l'exécuter que si le premier a une sortie?

23

Comment puis-je réécrire cette commande dans un e-mail uniquement s'il y a une sortie du mailq | grep?

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

Est-ce même possible sur une seule ligne?

Voir Vérifier si le canal est vide et exécuter une commande sur les données si ce n'est pas pour un cas plus général que d'envoyer un e-mail.

cosmin
la source
Très similaire: rendre une pipe conditionnelle au retour non vide (sur Super User )
G-Man dit 'Reinstate Monica'

Réponses:

19

Je pense que vous devrez utiliser un fichier temporaire pour cette opération afin que vous puissiez utiliser l' &&opérateur pour exécuter la commande mail uniquement si le grep a renvoyé un état de sortie qui indique qu'il avait des correspondances comme ceci:

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

Si cela ne vous dérange pas que le fichier temporaire reste quelque part et puisse utiliser un nom statique, vous pouvez ignorer les éléments spéciaux de dénomination et de suppression:

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

Edit: Après avoir vu la réponse de glenn, j'ai encore joué avec cela et apparemment, assigner une variable en utilisant la $()syntaxe renvoie le code de sortie de la commande, vous pouvez donc ignorer le test qu'il a utilisé pour la longueur de la chaîne et l'utiliser à la place. Ici, tout est dans une seule commande:

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

Edit 2: Après avoir vu la réponse de Simon, j'ai vérifié mon mailprogramme. Il ne se comporte pas de la manière qu'il décrit par défaut, mais a une option pour cela. Depuis la page de manuel:

-ESi un message sortant ne contient aucun texte dans sa première ou sa seule partie de message, ne l'envoyez pas, mais jetez-le en silence, définissant efficacement la variable skipemptybody au démarrage du programme. Ceci est utile pour envoyer des messages à partir de scripts démarrés par cron (8).

Rendre cela possible:

mailq | egrep 'rejected|refused' -A 5 -B 5 | mail -E -s 'dd' email@email
Caleb
la source
1
le pouvoir de la collaboration !!
glenn jackman
3
Je dirais que cette réponse pourrait être améliorée en déplaçant la meilleure solution vers le haut.
Mark Booth
@Wildcard Cette modification n'était pas nécessaire, étant donné que les paramètres d'entrée mktempne retourneront pas quelque chose qui doit être cité.
Caleb
2
Je sais. Vous êtes l'un des rares à vouloir comprendre que les citations sont parfois nécessaires (et les omet délibérément). Cependant, la valeur par défaut doit être entre guillemets, sauf si vous souhaitez explicitement appeler glob(split(var)). Vous n'avez pas besoin de fractionnement de mots ou d'extension de glob de fichiers ici, alors ne les demandez pas. De plus, nous devons montrer la bonne voie sur ce site .
Wildcard
@Wildcard Assez juste. En général, je cours zshet je n'obtiens pas de globulation ou de fractionnement de mots dans des cas comme celui-ci, sauf si je les demande, mais pour le bien des réponses ici et ne pas connaître l'environnement cible citant par principe, c'est bien.
Caleb
11

Le programme utilitaire ifne existe expressément à cet effet: pour exécuter une commande si l'entrée standard n'est pas vide.

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

La commande ifne fait partie du paquet moreutils , présenté comme "une collection croissante d'outils Unix que personne ne pensait écrire il y a longtemps, quand Unix était jeune".

Guy Hillyer
la source
8

Vous pouvez enregistrer la sortie dans une variable, puis appeler la commande mail si la longueur de la chaîne n'est pas nulle.

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

Pour une ligne, joignez les deux commandes avec un point-virgule.

glenn jackman
la source
1
Accessoires pour utiliser une variable au lieu d'un fichier temporaire. Cela m'a fait penser à où va le code de sortie de la commande avec laquelle vous avez fait l'affectation des variables, et apparemment il est transmis! Voir ma réponse mise à jour .
Caleb
7

À utiliser mailavec l' -Eoption. Sur mon Mac, MAIL (1) indique que le -Edrapeau n'enverra pas d'e-mail avec un corps vide.

-E N'envoyez pas de messages avec un corps vide. Ceci est utile pour les erreurs de tuyauterie des scripts cron (8).

Sur mon Mac, j'ai exécuté le test suivant. Notez que le fichier file_does_existexiste, mais le fichier file_does__not_existn'existe pas.

Cela m'envoie un e-mail:

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

Ce n'est pas le cas. Notez que la commande ls file_does_not_exist | egrep ...ne produit aucune sortie.

$ 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
la source
Caleb vous a battu.
Gilles 'SO- arrête d'être méchant'
1
Il ne m'a battu que de 4,75 heures :). J'ai raté ce détail dans sa longue réponse.
Stefan Lasiewski
5

Dans ce cas particulier, si votre mailprend en charge l' -Eoption , utilisez-la simplement. Dans le cas général, vous pouvez essayer de lire un caractère; s'il y en a un, lancez la commande de post-traitement et donnez-lui ce caractère à partir du reste du fichier.

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

Notez l'utilisation utile de cat. L'ajout de echo .fait que cette fonction fonctionne même si le premier caractère dans l'entrée est une nouvelle ligne (rappelez-vous que la $(…)construction supprime les nouvelles lignes de terminal).

Avec la plupart des shells (autre chose que zsh, pour autant que je sache), si le fichier commence par un caractère nul, ce code pensera qu'il est vide. Correction qui est laissée comme un exercice pour le lecteur. (Astuce: utilisez oddans le premier sous-shell et printfpour imprimer le premier octet.) (Solution: comment vérifier si le canal est vide ) Vous pouvez rencontrer le même problème si le fichier commence par un octet qui n'est pas un caractère valide dans le courant lieu; c'est plus facile à corriger en exécutant ce code avec LC_ALL=C.

Gilles 'SO- arrête d'être méchant'
la source
+1 pour ridicule mais peut-être utile dans un autre scénario :) Par curiosité, pourquoi injecter et tester le point au lieu de tester une chaîne vide? Cela ne pose-t-il pas un problème si l'entrée commence par un point? L'utilisation de l' readaide pourrait-elle simplifier cela?
Caleb
@Caleb: Oui, j'aurais dû mentionner que je répondais à la question dans le titre et non à la situation particulière. Voir mon montage pour une explication du point. Je ne pense pas que vous puissiez distinguer une première ligne vide d'un fichier vide avec readin portable sh (cela peut être possible en bash, ksh ou zsh).
Gilles 'SO- arrête d'être méchant'
3

IIRC la mailcommande BSD s'interrompt si un message vide est donné.

Simon Richter
la source
1
Le programme de messagerie sur mon système GNU Linux (Heirloom mailx 12.4) ne se comporte pas de cette façon par défaut, mais a une -Eoption pour l'activer .
Caleb
1

Récemment, j'ai appris le nom de la commande ifnequi fait partie du moreutilspackage. Où vous pouvez l'utiliser pour résoudre ce problème spécifique.

ifne - Exécute la commande si l'entrée standard n'est pas vide

exemple de la page de manuel,

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

Vous pouvez réécrire votre commande en utilisant test -s /dev/stdinpour vérifier s'il y a une sortie de la mailq | greppièce.

- 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
la source