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 ifnotempty
fonction 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 --foo
en sink --bar
par é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:
dd
qu'à 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 1
serait un remplacement approprié pour dd bs=1 count=1 2>/dev/null
Linux.head -n 1
ne conviendrait pas, car il met head
gé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 head
et même read -r -n 1 head
ne conviennent pas ici parce que si le premier caractère est une nouvelle ligne, head
serait 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.cat
par </dev/stdin
pour 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 line
et here
sont 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_empty
fonction 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 :)
yourothercommand
ne 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