Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Find out if there is input from a pipe or not in Python?

(I think) I know how to read from a pipe. I mean calls on the bash like this

echo Bähm | ./script.py

This Python3 script work with that.

#!/usr/bin/env python3
import sys

x = sys.stdin.read()

if x:
    print(x)
else:
    print('no pipe')

sys.exit()

But when I simply do this on the bash

./script.py

nothing happens because it waits for input.

So I want to check (when starting the script) if there is input from a pipe or not. But I don't know why size or len didn't worked on sys.stdin or sys.stdin.buffer.

Maybe there is a difference in handling that between version 2 and 3 of Python, too?

like image 215
buhtz Avatar asked Nov 23 '15 13:11

buhtz


4 Answers

Alternatively to @Cyrbil's answer if you really need to know if the input was piped in, you can use isatty:

from sys import stdin
from os import isatty

is_pipe = not isatty(stdin.fileno())
like image 157
kay Avatar answered Oct 06 '22 17:10

kay


You could use select, but it doesn't work on Windows:

import sys
import select

# Input: zero or more file-like objects
# Returns: list of objects that can be read 
def can_read_files(*args):
    return select.select(args, [], [], 0.0)[0]

if can_read_files(sys.stdin):
    print(sys.stdin.read())
else:
    print('No input')

To us it inline:

if select.select([sys.stdin], [], [], 0.0)[0]:
    ...
like image 39
Harvey Avatar answered Oct 06 '22 17:10

Harvey


nothing happens because it waits for input

And that's completely fine. You want to fix something that doesn't need any fixing, and as a by-product you're actually creating problems.

Imagine a piping process that does some heavy calculations, and because of that, needs some time to produce the output. You cannot check for data availability in your python script. You don't know when the data is going to be available. You have to be patient.

So I want to check (when starting the script) if there is input from a pipe or not.

Don't. If, for some reason, your application hasn't meant to be run with a tty (waiting for user input), check for that.

sys.stdin.isatty()

Only check for data availability if there's some hard-time constraint: e.g.: the user has to respond within 2 seconds.

like image 6
Karoly Horvath Avatar answered Oct 06 '22 17:10

Karoly Horvath


You can set the input entry file to non-blocking, and catching the IOError: [Errno 11] Resource temporarily unavailable when nothing is piped.

import fcntl
import os
import sys

# make stdin a non-blocking file
fd = sys.stdin.fileno()
fl = fcntl.fcntl(fd, fcntl.F_GETFL)
fcntl.fcntl(fd, fcntl.F_SETFL, fl | os.O_NONBLOCK)

try:
    print(sys.stdin.read())
except:
    print('No input')
like image 5
Cyrbil Avatar answered Oct 06 '22 17:10

Cyrbil