Search code examples
pythonstdoutstdinoptparse

Consistent way to redirect both stdin & stdout to files in python using optparse


I've got a dozen programs that can accept input via stdin or an option, and I'd like to implement the same features in a similar way for the output.

The optparse code looks like this:

parser.add_option('-f', '--file',
       default='-',
       help='Specifies the input file.  The default is stdin.')
parser.add_option('-o', '--output',
       default='-',
       help='Specifies the output file.  The default is stdout.')

The rest of the applicable code looks like this:

if opts.filename == '-':
    infile = sys.stdin
else:
    infile = open(opts.filename, "r")

if opts.output == '-':
    outfile = sys.stdout
else:
    outfile = open(opts.output, "w")

This code works fine and I like its simplicity - but I haven't been able to find a reference to anyone using a default value of '-' for output to indicate stdout. Is this a good consistent solution or am I overlooking something better or more expected?


Solution

  • For input files you could use fileinput module. It follows common convention for input files: if no files given or filename is '-' it reads stdin, otherwise it reads from files given at a command-line.

    There is no need in -f and --file options. If your program always requires an input file then it is not an option.

    -o and --output is used to specify the output file name in various programs.

    optparse

    #!/usr/bin/env python
    import fileinput
    import sys
    from optparse import OptionParser
    
    parser = OptionParser()
    parser.add_option('-o', '--output',
        help='Specifies the output file.  The default is stdout.')
    options, files = parser.parse_args()
    if options.output and options.output != '-':
       sys.stdout = open(options.output, 'w')
    
    for line in fileinput.input(files):
        process(line)
    

    argparse

    argparse module allows you to specify explicitly files as arguments:

    #!/usr/bin/env python
    import fileinput
    import sys
    from argparse import ArgumentParser
    
    parser = ArgumentParser()
    parser.add_argument('files', nargs='*', help='specify input files')
    group = parser.add_mutually_exclusive_group()
    group.add_argument('-o', '--output', 
        help='specify the output file.  The default is stdout')
    group.add_argument('-i', '--inplace', action='store_true',
        help='modify files inplace')
    args = parser.parse_args()
    
    if args.output and args.output != '-':
       sys.stdout = open(args.output, 'w')
    
    for line in fileinput.input(args.files, inplace=args.inplace):
        process(line)
    

    Note: I've added --inplace option in the second example:

    $ python util-argparse.py --help
    usage: util-argparse.py [-h] [-o OUTPUT | -i] [files [files ...]]
    
    positional arguments:
      files                 specify input files
    
    optional arguments:
      -h, --help            show this help message and exit
      -o OUTPUT, --output OUTPUT
                            specify the output file. The default is stdout
      -i, --inplace         modify files inplace