Est - il un moyen de canaliser la sortie d'une fonction à une autre, mais seulement s'il est une sortie?
Est - il un moyen de canaliser la sortie d'une fonction à une autre, mais seulement s'il est une sortie?
Réponses:
La ifnotemptyfonction suivante redirige son entrée vers la commande passée en argument, sauf qu'elle ne fait rien si l'entrée est vide. Il permet au tuyau source --fooen sink --barpar écrit 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
}
Notes de conception:
ddqu'à proprement parler elle ne soit pas conforme aux normes: elle repose sur le fait de ne pas lire plus que le seul octet qu'on lui a dit de lire sur son entrée standard.head -c 1serait un remplacement approprié pour dd bs=1 count=1 2>/dev/nullLinux.head -n 1ne conviendrait pas, car il met headgénéralement en mémoire tampon son entrée et peut lire plus que la seule ligne qu'il sort - et comme il lit à partir d'un canal, les octets supplémentaires sont simplement perdus.read -r headet même read -r -n 1 headne conviennent pas ici parce que si le premier caractère est une nouvelle ligne, headserait défini sur la chaîne vide, ce qui rend impossible la distinction entre une entrée vide et une entrée commençant par une ligne vierge.head=$(head -c 1)parce que si le premier caractère est une nouvelle ligne, la substitution de commande supprimerait la nouvelle ligne finale, ce qui rendrait impossible la distinction entre une entrée vide et une entrée commençant par une ligne vierge.catpar </dev/stdinpour un gain de performance microscopique.Si cela ne vous dérange pas de stocker la totalité des données intermédiaires en mémoire, voici une implémentation très légèrement plus simple de pipe_if_not_empty.
pipe_if_not_empty () {
input=$(cat; echo a);
if [ "x$input" != x"a" ]; then
{ printf %s "${input%a}"; } | "$@"
fi
}
Voici une implémentation légèrement plus simple avec les mises en garde suivantes:
Encore une fois, toutes les données sont stockées en mémoire.
pipe_if_not_empty () {
input=$(cat);
if [ "x$input" != x"" ]; then
{ printf '%s\n' "${input}"; } | "$@"
fi
}
Cela devrait fonctionner pour vous
$ --a function-- | [ xargs -r ] --another function--
Un exemple
$ 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
C'est simple mais ça devrait marcher pour vous. Si votre "une fonction" envoie une chaîne vide ou même une nouvelle ligne dans le pipeline, xargs -r, empêchera le passage à "une autre fonction".
Référence pour 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.
La fonction ci-dessous essaie de lire le 1er octet, et si elle réussit, elle fait écho à cet octet et renvoie le reste. Doit être efficace et 100% portable.
if_read() {
IFS="" read -rN 1 BYTE && { echo -nE "$BYTE"; cat; } | "$@";
}
Cas de test:
$ 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. Il pense que lineet heresont tous deux destinataires de l'e-mail, pas des jetons dans le sujet. Je dois échapper au "sujet environnant pour le faire fonctionner. Cependant, la pipe_if_not_emptyfonction de la réponse acceptée fonctionne pour moi même sans rien échapper.
Au moins quelque chose comme ça fonctionne:
yourcommand | if [ $(wc -c) -gt "0" ]; then yourothercommand; fi
Veuillez noter que ce qui précède considérera les sauts de ligne et autres caractères spéciaux comme sortie, donc une ligne vide passée à cette instruction if sera considérée comme une sortie. Augmentez simplement la limite -gt si votre sortie doit généralement être supérieure à 1 octet :)
yourothercommandne voit jamais la sortie de yourcommand.
Au lieu de sender | receiver:
tester () { local a=$(</dev/stdin); if [[ $a ]]; then printf '%s\n' "$a" | receiver; fi; }
sender | tester
Ou vous pouvez le rendre plus général en le modifiant pour accepter le programme récepteur comme argument comme dans la réponse de Gilles:
tester () { local a=$(</dev/stdin); if [[ $a ]]; then printf '%s\n' "$a" | "$@"; fi; }
sender | tester receiver