I was writing a small Python script and wanted it to handle three types of inputs:
- Standard input by user inputing from keyboard
- Standard input by pipeline
- File input
I wanted to make my script can do as follow:
% program # Sometimes, implying using standard input but depends on the purpose of program % program - # Indicates using standard input % program filename % prog2 | program # Pipe prog2's standard output to program's standard input
We should all have no problem with file, you just check the argument to see if we need to open a file as input.
The first two input types are not hard to do, we only need to use file.isatty() to identify current state of standard input:
- file.isatty()
- Return True if the file is connected to a tty(-like) device, else False.
We can decide based on the following:
sys.stdin.isatty()
If the program’s standard input is piped in with another program’s standard output, then it returns False; True if not.
You can quickly test:
python -c "import sys ; print sys.stdin.isatty()" echo blah | python -c "import sys ; print sys.stdin.isatty()"
You should get True then False as results.
So, now, here is a snippet stdinput.py:
#!/usr/bin/env python import sys def main(): args = sys.argv[1:] print 'Usage: %s [-|filename] or using pipe.\n' % sys.argv[0] if sys.stdin.isatty(): if len(args) == 1 and args[0] != '-': print '=== from file ===' text = ['* %s *' % args[0]] else: if sys.platform == 'win32': print '( Press Control+Z, then Return at new line to finish )' else: print '( Press Control+D, then Return at new line to finish )' text = sys.stdin.readlines() print '=== from manual input ===' else: if len(args) == 1 and args[0] != '-': print >> sys.stderr, 'Argument should be -, if you intend to pipe in your text.' sys.exit(1) text = sys.stdin.readlines() print '=== from pipe ===' print ''.join(text) if __name__ == '__main__': main()
This snippet isn’t so special, only two things to note:
- It use different keystroke to close the stream (to signal End-of-file) on Windows Ctrl+Z and Linux Ctrl+D.
- The script load data at once. You need to check empty string if you use read() or readline() for EOF.
It should do what I want, you can test with the followings:
./stdinput.py ./stdinput.py - ./stdinput.py filename echo blah | ./stdinput.py echo blah | ./stdinput.py - echo blah | ./stdinput.py filename
Note that this is the behavior I want, you may need to adjust them for your need.
What about standard output? It’s the same story, just switching the roles.
python -c "import sys ; print sys.stdout.isatty()" python -c "import sys ; print sys.stdout.isatty()" | cat # On Windows python -c "import sys ; print sys.stdout.isatty()" | more # On Windows' PowerShell python -c "import sys ; print sys.stdout.isatty()" | echo
You should get True then False as results.
And what about redirection, prog.py > file or prog.py < file? Still same story.
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.