I've written a command line utility that uses getopt for parsing arguments given on the command line. I would also like to have a filename be an optional argument, such as it is in other utilities like grep, cut etc. So, I would like it to have the following usage
tool -d character -f integer [filename]
How can I implement the following?
input() can read data from the specified file name in the command line argument or from the standard input, while sys. stdin can only read data from the standard input.
Short for standard input, stdin is an input stream where data is sent to and read by a program. It is a file descriptor in Unix-like operating systems, and programming languages, such as C, Perl, and Java. Below, is an example of how STDIN could be used in Perl.
The stdin is the short form of the “standard input”, in C programming the term “stdin” is used for the inputs which are taken from the keyboard either by the user or from a file. The “stdin” is also known as the pointer because the developers access the data from the users or files and can perform an action on them.
The fileinput module may do what you want - assuming the non-option arguments are in args
then:
import fileinput for line in fileinput.input(args): print line
If args
is empty then fileinput.input()
will read from stdin; otherwise it reads from each file in turn, in a similar manner to Perl's while(<>)
.
In the simplest terms:
import sys # parse command line if file_name_given: inf = open(file_name_given) else: inf = sys.stdin
At this point you would use inf
to read from the file. Depending on whether a filename was given, this would read from the given file or from stdin.
When you need to close the file, you can do this:
if inf is not sys.stdin: inf.close()
However, in most cases it will be harmless to close sys.stdin
if you're done with it.
I like the general idiom of using a context manager, but the (too) trivial solution ends up closing sys.stdin
when you are out of the with
statement, which I want to avoid.
Borrowing from this answer, here is a workaround:
import sys
import contextlib
@contextlib.contextmanager
def _smart_open(filename, mode='Ur'):
if filename == '-':
if mode is None or mode == '' or 'r' in mode:
fh = sys.stdin
else:
fh = sys.stdout
else:
fh = open(filename, mode)
try:
yield fh
finally:
if filename != '-':
fh.close()
if __name__ == '__main__':
args = sys.argv[1:]
if args == []:
args = ['-']
for filearg in args:
with _smart_open(filearg) as handle:
do_stuff(handle)
I suppose you could achieve something similar with os.dup()
but the code I cooked up to do that turned out to be more complex and more magical, whereas the above is somewhat clunky but very straightforward.
I prefer to use "-" as an indicator that you should read from stdin, it's more explicit:
import sys
with open(sys.argv[1], 'r') if sys.argv[1] is not "-" else sys.stdin as f:
pass # do something here
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With