J'ai pipé une ligne dans le script bash et je veux vérifier si le tuyau contient des données avant de l'envoyer à un programme.
En cherchant j'ai trouvé environ test -t 0
mais ça ne marche pas ici. Retourne toujours faux. Alors, comment être sûr que le tuyau a des données?
Exemple:
echo "string" | [ -t 0 ] && echo "empty" || echo "fill"
Sortie: fill
echo "string" | tail -n+2 | [ -t 0 ] && echo "empty" || echo "fill"
Sortie: fill
Contrairement à la manière standard / canonique de tester si le pipeline précédent produit une sortie? l'entrée doit être préservée pour la transmettre au programme. Ceci généralise Comment diriger la sortie d'un processus vers un autre mais ne s'exécuter que si le premier a une sortie? qui se concentre sur l'envoi d'email.
Réponses:
Il n’ya aucun moyen de jeter un coup d’œil sur le contenu d’un tube à l’aide des utilitaires shell courants, ni de lire un caractère sur le tube, puis de le remettre en place. Le seul moyen de savoir qu'un canal contient des données est de lire un octet, puis de le renvoyer à sa destination.
Alors faites juste ça: lisez un octet; si vous détectez une fin de fichier, faites ce que vous voulez quand l'entrée est vide; si vous lisez un octet, indiquez ce que vous voulez faire lorsque l'entrée n'est pas vide, transmettez-lui cet octet et transmettez le reste des données.
L'
ifne
utilitaire de moreutils de Joey Hess exécute une commande si son entrée n'est pas vide. Il n'est généralement pas installé par défaut, mais il devrait être disponible ou facile à utiliser avec la plupart des variantes d'Unix. Si l'entrée est vide,ifne
ne fait rien et renvoie le statut 0, qui ne peut pas être distingué de l'exécution de la commande. Si vous voulez faire quelque chose si l'entrée est vide, vous devez vous assurer que la commande ne renvoie pas 0, ce qui peut être fait en renvoyant un état d'erreur distinct au cas de réussite:test -t 0
n'a rien à voir avec cela; il teste si l'entrée standard est un terminal. Il ne dit rien d'une manière ou d'une autre quant à savoir si une entrée est disponible.la source
peek
qui pourrait renvoyer les données réelles d'un pipe, pas seulement la quantité qu'il contient. (Dans 4.4, BSD, 386BSD, etc., les tuyaux étaient implémentés sous forme de paires de sockets , mais cela a été vidé dans les versions ultérieures de * BSD - bien qu'ils les aient gardés bidirectionnels).read -t 0
(t dans ce cas-ci signifie timeout, si vous vous le demandez).Une solution simple consiste à utiliser la
ifne
commande (si l'entrée n'est pas vide). Dans certaines distributions, il n'est pas installé par défaut. C'est une partie du paquetmoreutils
dans la plupart des distributions.ifne
exécute une commande donnée si et seulement si l'entrée standard n'est pas videNotez que si l'entrée standard n'est pas vide, elle est transmise
ifne
à la commande donnée.la source
Ancienne question, mais au cas où quelqu'un l'aurait trouvée comme moi: ma solution est de lire avec un délai d'attente.
Si
stdin
est vide, cela reviendra après 5 secondes. Sinon, toutes les entrées sont lues et vous pouvez les traiter au besoin.la source
-t
vérifie si le descripteur de fichier de stdin (0) est ouvert ou fermé:
la source
[ -t 0 ]
vérifie si le fd 0 est ouvert sur un terminal , pas s'il est fermé ou ouvert../that_script </dev/null
=> "stdin a data". Ou./that_script <&-
d'avoir le stdin vraiment fermé .Vous pouvez également utiliser
test -s /dev/stdin
(dans un sous-shell explicite).la source
En bash:
Détecte si une entrée contient des données (sans rien lire). Ensuite, vous pouvez lire l'entrée (si l'entrée est disponible au moment de l'exécution de la lecture):
Remarque: Comprenez que cela dépend du moment choisi. Ceci détecte si l'entrée contient déjà des données uniquement au moment de l'
read -t
exécution.Par exemple, avec
la sortie est
1
(échec de lecture, c.-à-d. entrée vide). L'écho écrit des données mais n'est pas très rapide à démarrer et écrit son premier octet, ainsi,read -t 0
signalera que son entrée est vide, puisque le programme n'a encore rien écrit.la source
bash
fera l'unselect()
ou l'ioctl(FIONREAD)
autre, mais pas les deux, comme il se doit pour que cela fonctionne.read -t0
est cassé. Ne pas utiliserAvec
FIONREAD
ioctl, vous pouvez facilement vérifier si des données sont lisibles sous Unix .Je ne peux pas penser à un utilitaire standard faisant exactement cela, alors voici un programme trivial le faisant (meilleur que celui
ifne
de moreutils IMHO ;-)).S'il n'y a pas de données disponibles sur stdin, il sortira avec le statut 1. S'il y a des données, elles seront exécutées
prog
. Si nonprog
, il sortira avec le statut 0.Vous pouvez supprimer l'
poll
appel si vous n'êtes intéressé que par les données immédiatement disponibles. Cela devrait fonctionner avec la plupart des types de fds, pas seulement les pipes.fionread.c
la source
POLLHUP
événement pour gérer le cas vide? Cela fonctionne-t-il s'il y a plusieurs descriptions de fichiers à l'autre bout du canal?Si vous aimez les one-liners cryptés et courts:
J'ai utilisé les exemples de la question initiale. Si vous ne voulez pas utiliser les données canalisées
-q
avec grepla source
Cela semble être une implémentation raisonnable de ifne dans bash si vous êtes d'accord pour lire toute la première ligne.
la source
read
retournera également false si l'entrée n'est pas vide, mais ne contient aucun caractère de nouvelle ligne,read
effectue certains traitements sur son entrée et peut lire plusieurs lignes à moins que vous ne l'appeliez commeIFS= read -r line
.echo
ne peut pas être utilisé pour des données arbitraires.Cela fonctionne pour moi en utilisant
read -rt 0
exemple de question originale, sans données:
la source
{ sleep .1; echo yes; } | { read -rt0 || echo NO; cat; }
(faux négatif) ettrue | { sleep .1; read -rt0 && echo YES; }
(faux positif). En fait, ce bashread
sera dupé même par fds ouvert en écriture seule le mode:{ read -rt0 && echo YES; cat; } 0>/tmp/foo
. La seule chose qu'il semble faire est unselect(2)
fd.select
retournera un fd en tant que "prêt" si unread(2)
sur il ne bloquerait pas, peu importe s'il retourneraEOF
ou une erreur. Conclusion:read -t0
est cassé dansbash
. Ne l'utilisez pas.{ sleep .1; echo yes; } | { read -rt0 || echo NO; cat; }
n'est pas un faux négatif car (au moment de l'exécution de la lecture), il n'y a pas d'entrée. Plus tard (sommeil .1), cette entrée est disponible (pour le chat).echo "" | { read -t0 && echo YES; }
imprime OUI maisecho "" | { read -rt0 && echo YES; }
ne le fait pas.