7

I am trying to read stdin in a Python script, while receiving from pipe.

I used these lines:

for line in sys.stdin:
    print line

And run the script: echo "test" | script.py

So far it works fine. However if I don't use a pipe the program sticks at the for command. Meaning that calling: ./script.py will make the script not work. How can I fix this?

smci
  • 32,567
  • 20
  • 113
  • 146
Ohad
  • 183
  • 3
  • 12
  • 4
    What _should_ the program do if you call `./script.py`? Just terminate without entering the loop? – Kevin Nov 29 '18 at 14:28
  • No, it should return from the function and continue running the rest of the script – Ohad Nov 29 '18 at 14:30
  • 2
    Sorry, I was under the impression that `for line in sys.stdin: print line` was your entire program. Is there a function? Please share your entire code. – Kevin Nov 29 '18 at 14:33
  • Well, basically that is the entire code for now as i am testing the pipe for personal use. Lets say that after the 'for' loop there is another command' 'print "xxx"' and that is all there is. I need the script to reach the second print line at any case running the script (with or without pipe redirection) – Ohad Nov 29 '18 at 14:35
  • 1
    What if you run `python script.py` and type in a few lines that should be echo'ed. And then press `CTRL+D` ? – Tezirg Nov 29 '18 at 14:36
  • 2
    A better title would be *"Read Python stdin without blocking on empty input"* – smci Nov 29 '18 at 14:43

3 Answers3

8

Edit: you apparently don't even want it to read lines in this case or handle KeyboardInterrupt, so what you need to do is check for empty stdin. Check if you are a terminal and pass as shown in the edited example.


sys.stdin is file-like. Try:

if not sys.stdin.isatty():
    data = sys.stdin.readlines()
    # Do something with data

If you run this with no stdin I don't think it will hang like it did for you before... because sys.stdin will be empty. The function should then continue.

Charles Landau
  • 4,187
  • 1
  • 8
  • 24
1

You can check if the program has been piped by calling isatty on the file descriptor derived from sys.stdin.

Like so :

import os
import sys

if not os.isatty(sys.stdin.fileno()):
    print (sys.stdin.readlines())
else:
    print ("Skip, so it doesn't hang")

Example 1 :

 echo "test" | python ./script.py
 ['test\n']

Example 2 :

 python ./script.py 
 Skip, so it doesn't hang
Rhubbarb
  • 4,248
  • 6
  • 36
  • 40
Tezirg
  • 1,629
  • 1
  • 10
  • 20
  • The problem is not about interactive vs piped. `sys.stdin.readlines()` may well block if the other end of the pipe sends data too slowly, so checking for a pipe or tty is not a viable solution. – Eric Aug 27 '22 at 18:04
0

Your script works, it just seems your expectations are not correct.

script.py (adjusted for Python 3, which is what I have installed):

import sys
for line in sys.stdin:
    print(line)

$ python script.py
abcd
abcd

xyz
xyz

Where the first occurrences of "abcd" and "xyz" were my inputs from the keyboard and the second ones were the program's outputs. I terminated execution at that point via ctrl-d.

The issue is that reading from stdin without input from a pipe isn't giving you what you expect. When you just run the script (without piped input), it just sits there waiting for you to enter something from the keyboard. You may want to investigate raw_input for taking input from the keyboard.

Update:

After getting more clarification in comments about what you want, the following script may give you the results you desire:

import sys
if not sys.stdin.isatty():
    for line in sys.stdin:
        print(line)

print("More stuff!")
Community
  • 1
  • 1
GreenMatt
  • 18,244
  • 7
  • 53
  • 79
  • I am not interested in taking input from keyboard. I want to respend to either of these cases: there is a former pipe input or there isn't – Ohad Nov 29 '18 at 14:45
  • @OhadVano: That wasn't clear from your original question. – GreenMatt Nov 29 '18 at 14:48
  • Case 1: there is a pipe redirection, meaning script called in form of: echo "text" | script.py. Case 2: script was called with no pipe, meaning: script.py – Ohad Nov 29 '18 at 14:48
  • @OhadVano: see https://stackoverflow.com/questions/3762881/how-do-i-check-if-stdin-has-some-data – GreenMatt Nov 29 '18 at 14:49
  • @OhadVano: Yes, that was clear from your question. What is desired for case 2 wasn't clear - at least not to me. It seems you want to be able to handle situations where there is no data on stdin. Correct? If that is the case, what do you want the program to do? – GreenMatt Nov 29 '18 at 14:50
  • In that case i want to continue running the rest of the script. – Ohad Nov 29 '18 at 14:52
  • The problem is not about interactive vs piped. sys.stdin.readlines() may well block if the other end of the pipe sends data too slowly, so checking for a pipe or tty is not a viable solution. – Eric Aug 27 '22 at 18:05
  • @Eric: Perhaps you failed to notice that the question was edited after I answered. In that edit the nature of the question was changed a bit. Given the question is almost 4 years old and the OP marked an answer selected, I'm not inclined to put in the effort needed to update this answer, especially when the OP never explained their needs well (note the latest edit was by someone else who seems to be making assumptions about the OP's needs based on comments) or in response to a comment. that was copy & pasted under all the answers. – GreenMatt Aug 29 '22 at 01:50