2009-03-30 2 views
31

Je veux un programme pour faire une chose si elle est exécutée comme ceci:Quelle est la meilleure façon de savoir si un programme Python a quelque chose à lire à partir de stdin?

cat something | my_program.py 

et faire autre chose si courir comme ce

my_program.py 

Mais si je lis de stdin, il attendra l'utilisateur entrée, donc je veux voir s'il y a quelque chose à lire avant d'essayer de lire à partir de stdin.

+0

Que signifie « ne devrait pas être aussi difficile que cela semble » signifie? Pourriez-vous mettre à jour la question avec une description de votre objectif, de tout code que vous avez essayé jusqu'à présent et qui n'a pas fonctionné, et de ce que vous vous attendiez à ce qu'il soit arrivé? –

+0

merci Josh ... beaucoup apprécié. –

Répondre

59

Si vous voulez détecter si quelqu'un est des données de tuyauterie dans votre programme, ou l'exécuter, vous pouvez utiliser de manière interactive isatty pour voir si stdin est un terminal:

$ python -c 'import sys; print sys.stdin.isatty()' 
True 
$ echo | python -c 'import sys; print sys.stdin.isatty()' 
False 
+0

Excellente solution, merci! –

+0

Si le script pouvait être exécuté à distance, cette approche isatty() fonctionne-t-elle toujours? Par exemple 'ssh somehost.com 'bash -c" python -c \ "import sys; print sys.stdin.isatty() \ "" ''renvoie' False' –

+0

En utilisant la réponse de @Trey Stout, le script se comporte comme prévu sur ssh. 'ssh somehost.com 'bash -c" python -c \ "import sys; importer sélectionner; r, w, x = select.select ([sys.stdin], [], [], 0); print (r) \ "" ''Imprime' [] '. La même commande avec pipe est: 'ssh somehost.com 'bash -c" echo quelque chose | python -c \ "import sys; importer sélectionner; r, w, x = select.select ([sys.stdin], [], [], 0); print (r) \ "" ''et qui imprime' [ ', le mode' r 'à 0x7ff2aad250c0>] ' –

-2

Je ne connais pas les commandes Python du haut de ma tête, mais vous devriez être capable de faire quelque chose avec poll ou de sélectionner des données prêtes à lire sur une entrée standard.

Cela peut être spécifique à un système d'exploitation Unix et différent sur Windows Python.

+0

Quelque chose comme: select.select ([sys.stdin], [], [], 0) == ([sys.stdin], [], []) Et, oui, il est spécifique à unix. –

2

Les mauvaises nouvelles. Du point de vue de la ligne de commande Unix, ces deux invocations de votre programme sont identiques.

Unix ne peut pas les distinguer facilement. Ce que vous demandez n'est pas vraiment raisonnable, et vous devez penser à une autre façon d'utiliser votre programme.

Dans le cas où ce n'est pas dans un pipeline, que doit-il lire s'il ne lit pas stdin?

Est-il censé lancer une interface graphique? Si c'est le cas, vous pouvez avoir une option "-i" (--interactive) pour indiquer que vous voulez une interface graphique, pas de lecture de stdin.

Vous pouvez parfois distinguer les tubes de la console car le périphérique de console est "/ dev/tty", mais ce n'est pas portable.

+0

+1: Quiconque a déjà combattu une application qui a essayé des trucs mignons pour contourner cette volonté appréciez tout effort supplémentaire dépensé pour éviter ces astuces mignonnes (ou au moins les rendre explicites, comme le suggère S.Lott). –

+0

Ces invocations ne sont pas identiques, comme "unknown (google)" a souligné en utilisant sys.stdin.isatty(). "isatty" est portable sous Unix, et utilisé par exemple dans 'ls' (http://git.savannah.gnu.org/cgit/coreutils.git/tree/src/ls.c). Mais oui, c'est un truc à se méfier de l'utilisation. –

+0

Les invocations sont identiques - indiscernables. Les périphériques connectés à stdin sont légèrement différents. Selon/dev/tty est réalisable, mais une option est triviale et évidente. –

7

Vous voulez le module de sélection (man select sur unix) Il vous permettra de tester s'il y a quelque chose de lisible sur stdin. Notez que select ne fonctionnera pas sur Window avec des objets fichier. Mais à partir de votre pipe chargée question que je suppose que vous êtes sur un os unix :)

http://docs.python.org/library/select.html

root::2832 jobs:0 [~] # cat stdin_test.py 
#!/usr/bin/env python 
import sys 
import select 

r, w, x = select.select([sys.stdin], [], [], 0) 
if r: 
    print "READABLES:", r 
else: 
    print "no pipe" 

root::2832 jobs:0 [~] # ./stdin_test.py 
no pipe 

root::2832 jobs:0 [~] # echo "foo" | ./stdin_test.py 
READABLES: [<open file '<stdin>', mode 'r' at 0xb7d79020>] 
+2

Cela ne vous indique pas si l'entrée est un canal, uniquement si l'entrée est disponible. Considérez: (sleep 1 && echo "foo") | ./stdin_test.py Sur ma machine cela signale "pas de tuyau". –

+1

Note intéressante. Je pense que c'est juste parce que le select ne bloque pas. Quand je change le 0 à un 5 votre exemple montre correctement le fichier ouvert. Il devrait vraiment être mis dans une boucle qui pourrait ensuite cycle jusqu'à ce que les données étaient disponibles. Bon point :) –

Questions connexes