Search code examples
pythonpython-3.xargumentsstdin

Supporting sys.stdin.readlines() as well as command line arguments in python?


I'm working on an application that can be launched directly, or via stdin.

Currently if I don't pipe any data to the application an EOF is never received and it hangs waiting for input (such as ctrl+d). That code looks like:

while True:
    line = sys.stdin.readline()
    print("DEBUG: %s" % line) 
    if not line:
       break

I've also tried:

for line in sys.stdin:
    print("DEBUG (stdin): %s" % line)
    return

However in both cases an EOF isn't received if the program is launched directly so it hangs waiting for it.

I've seen some unix applications pass a single - command line flag in cases where stdin input is expected but I'm wondering if there's a better workaround then this? I'd rather the user be able to use the application interchangeably without remembering to add a - flag.


Solution

  • The best you can do is to check whether standard input is a TTY, and, if so, not read it:

    $ cat test.py 
    import sys
    
    for a in sys.argv[1:]:
        print("Command line arg:", a)
    
    if not sys.stdin.isatty():
        for line in sys.stdin:
            print("stdin:", line, end="")
    
    $ python3 test.py a b c
    Command line arg: a
    Command line arg: b
    Command line arg: c
    
    $ { echo 1; echo 2; } | python3 test.py a b c
    Command line arg: a
    Command line arg: b
    Command line arg: c
    stdin: 1
    stdin: 2
    
    $ python3 test.py a b c < test.py 
    Command line arg: a
    Command line arg: b
    Command line arg: c
    stdin: import os, sys
    stdin: 
    stdin: for a in sys.argv[1:]:
    stdin:     print("Command line arg:", a)
    stdin: 
    stdin: if not sys.stdin.isatty():
    stdin:     for line in sys.stdin:
    stdin:         print("stdin:", line, end="")