What's the Python analogue of fileinput.input() for output? - python

fileinput.input() allows to simply loop over all lines in either a list of input files provided via sys.argv[1:] or sys.stdin if the former is empty.
Is there a similarly simple way to output to the last argument if given and sys.stdout otherwise?

You can use the argparse module and add a commandline argument like this:
parser.add_argument('outfile', nargs='?', type=argparse.FileType('w'),
default=sys.stdout)

You can check if the final argument is a file, in which case it is an input so set the output to sys.stdout, otherwise open a new file with that name as output and remove it from sys.argv.
Alternatively just use sys.stdout and let your users use > filename to store to a file.

Related

Need to pass argument from sys.argv[1] as input to glob() in python

I have a situation where i need to take argument from command-line and use that string (expression) I need to print files based on that regex.
I want to use glob to parse my string as i can pass regex to filter.
excerpt from python file:
dated = sys.argv[1]
files = glob.glob(dated)
This throws me empty list
> python analysis.py <some_expression>
[]
However, if I give any value manually:
dated = '*.xlsx' # example sake
files = glob.glob(dated)
print(files)
it prints:
[<list of files conforming to the required filter>]
It's obvious that the CLI arguments that it prints above, but I want the CLI argument to work properly.
I tested manually if the arguments are actually testing and it worked, so the sys.argv[1] is working but the results are not getting parsed in the glob.glob()
any ideas if I am missing something somewhere?
The issue her is not in Python, but in the shell that invokes it. Most shells I know (definitely all Linux shells) perform glob expansion before passing arguments to the executable they spawn (your Python script, in this case). This means that, at most, sys.argv[1] would contain the first file matching the glob expression you pass, and anyway, applying glob on it would not do any good.
For example, if your work directory has files a.xlsx, b.xlsx and c.xlsx, and you invoke you code using:
python mycode.py *.xlsx
Then the shell will actually glob the argument you specified, and will pass the results to your script, making the following true:
sys.argv[1:] == [`a.xlsx`, `b.xlsx`, `c.xlsx`]
In fact, instead of explicitly invoking glob, you can simply iterate on sys.argv[1:].

Passing piped data to Python program and also an input file

I want to pass data to a Python file using a pipe and also specifying an input file like:
cat file.txt|python script.py -u configuration.txt
I currently have this:
for line in fileinput.input(mode='rU'):
print(line)
I know there can be something with sys.argv but maybe using fileinput there is a clean way to do it?
Thanks.
From the documentation:
If a filename is '-', it is also replaced by sys.stdin. To specify an alternative list of filenames, pass it as the first argument to input().
So you can create a list containing '-' as well as the contents of sys.argv[1:] (the default), and pass that to input(). Or alternatively just put - in the list of arguments of your Python program:
cat file.txt|python script.py -u - configuration.txt
or
cat file.txt|python script.py -u configuration.txt -
depending on whether you want data provided on standard input to be processed before or after the contents of configuration.txt.
If you want to do anything more complicated than just processing the contents of standard input as if it were an input file, you probably should not be using the fileinput module.

Read from stdin or input file with argparse

I'd like to use argparse to read from either stdin or an input file. In other words:
If an input file is given, read that.
If not, read from stdin only if it's not the terminal. (i.e. a file is being piped in)
If neither of these criteria are satisfied, signal to argparse that the inputs aren't correct.
I'm asking for behavior similar to what's described in this question, but I want argparse to recognize no file as a failed input.
Using the information from the question you linked to, what about using sys.stdin.isatty() to check if the instance your program is being run is part of a pipeline, if not, read from input file, otherwise read from stdin. If the input file does not exist or stdin is empty throw an error.
Hope that helped.
I would recommend just settings nargs='?' and then handling the case of a Nonetype separately. According to the official documentation, "FileType objects understand the pseudo-argument '-' and automatically convert this into sys.stdin for readable FileType objects and sys.stdout for writable FileType objects". So just give it a dash if you want stdin.
Example
import argparse
import sys
parser = argparse.ArgumentParser()
parser.add_argument('inputfile', nargs='?', type=argparse.FileType('r'))
if not inputfile:
sys.exit("Please provide an input file, or pipe it via stdin")

Passing arguments into os.system

I need to execute the following command through python. rtl2gds is a tool which reads in 2 parameters: Path to a file and a module name
rtl2gds -rtl=/home/users/name/file.v -rtl_top=module_name -syn
I am reading in the path to the file and module name from the user through argparse as shown below:
parser = argparse.ArgumentParser(description='Read in a file..')
parser.add_argument('fileread', type=argparse.FileType('r'), help='Enter the file path')
parser.add_argument('-e', help='Enter the module name', dest='module_name')
args = parser.parse_args()
os.system("rtl2gds -rtl=args.fileread -rtl_top=args.module_name -syn")
But the file path that is read into args.fileread does not get in to the os.system when I call -rtl=args.fileread. Instead, args.fileread itself is being assumed as the file name and the tool flags an error.
I am sure there is a way to read in command line arguments into os.system or some other function (may be subprocess?- but couldnt figure out how). Any help is appreciated.
Don't use os.system(); subprocess is definitely the way to go.
Your problem though is that you expect Python to understand that you want to interpolate args.fileread into a string. As great as Python is, it is not able to read your mind like that!
Use string formatting instead:
os.system("rtl2gds -rtl={args.fileread} -rtl_top={args.module_name} -syn".format(args=args)
If you want to pass a filename to another command, you should not use the FileType type option! You want a filename, not an open file object:
parser.add_argument('fileread', help='Enter the file path')
But do use subprocess.call() instead of os.system():
import subprocess
subprocess.call(['rtl2gds', '-rtl=' + args.fileread, '-rtl_top=' + args.module_name, '-syn'])
If rtl2gds implements command line parsing properly, the = is optional and you can use the following call instead, avoiding string concatenation altogether:
subprocess.call(['rtl2gds', '-rtl', args.fileread, '-rtl_top', args.module_name, '-syn'])

Remove parsed options and their values from sys.argv

I am trying to use optparse (to parse command line options to my script) and fileinput (to have the flexibility to provide data input via pipe or file).
import optparse, fileinput
parser = OptionParser()
parser.add_option("-v", action="store_true", dest="verbose")
(options, args) = parser.parse_args()
for line in fileinput.input:
process(line)
However fileinput tries to use the '-v' option as well as file name resulting in 'No such file or directory error'. So either I need to make fileinput args or remove the parsed options from sys.argv, however I don't know a elegant way of doing it. Any pointers?
From the documentation:
To specify an alternative list of filenames, pass it as the first argument to input(). A single file name is also allowed.
So you can just pass in the leftover args you get from optparse.

Categories

Resources