I am trying to write a python script which accepts optional input parameters plus an input file:
./script --lines 1 file.txt
should take 1 for the number of lines (--lines) and then take file.txt as an input file. However, getopt does not even see "file.txt" since it does not have a parameter name in front of it.
How can I get the filename? I already considered using sys.arv[-1], but this means when I run:
./script --lines 1
then 1 will be taken as the input filename. My script will then throw an error (if no file named 1 exists):
error: file '1' no found
This works, but is not a great solution. Is there a way around this?
Definitely use argparse -- It's included in python2.7+ and can easily be installed for older versions:
parser = argparse.ArgumentParser()
parser.add_argument('--lines', type=int, default=0, help='number of lines')
parser.add_argument('file', help='name of file')
namespace = parser.parse_args()
print namespace.lines
print namespace.file
In a call to getopts:
opts, args = getopts(...)
the second element is a list of arguments not recognized by getopts. In your example, it will be a list containing the single item 'file.txt'.
Related
Currently, I'm using argparse to output the following file:
import argparse
....
parser = argparse.ArgumentParser(description='do cool stuff')
parser.add_argument('--output_text_file',
default="outputs/output_file1.txt",
help='file path for the output text file')
This is already an optional argument. However, I would like the default option to be do not output anything. If the user would like a file output, they can call the argument with the pathname above, e.g. `--output_text_file "outputs/my_text.txt"
What is normally the correct way to do this?
I suspect that if I use:
default=""
there will be an error above.
Quite simply, just omit the default:
parser.add_argument('--output-text-file')
In the args, check whether the output_text_file is None. The name will always be present.
You could set your default to os.devnull, it's a cross-platform value which corresponds to the file path of a null device
parser.add_argument('--output_text_file',
default=os.devnull,
help='file path for the output text file')
I have a script that uses some arguments and some stdin data.
For checking arguments I use argparse.ArgumentParser
Is it possible to check if any stdin data is given? Something like that:
parser.add_argument('infile', nargs='?', type=argparse.FileType('r'), default=sys.stdin, required=True)
but this example gives this error:
TypeError: 'required' is an invalid argument for positionals
No. It wont't read from whatever file you pass it, be it given on the command line, or stdin. You will get an open file handle, with not even a single byte/char consumed.
Simply read the data yourself, for instance with data = args.infile.read() (assuming args is the result of parsing`).
You can then test if it is empty, with a simple if not data:...
But usually, if you expect data in a specific format, the best is to simply try to parse it, and raise an error if you fail. Either empty data is invalid (json for instance), or it is valid but then it should be an acceptable input.
(as for the error, required only tells whether some option must be given on the command line or not, for --options and -o options. Positionals are always required unless you change their numbers with nargs).
The error is just because of the required=True parameter; and the message tells you what is wrong. It should be:
parser.add_argument('infile', nargs='?', type=argparse.FileType('r'), default=sys.stdin)
By 'calling' this infile, as opposed to '--infile', you've created a positional argument. argparse itself determines whether it is required or not. With nargs='?' it can't be required. It's by definition optional (but not an optionals argument :) ).
The FileType type lets you name a file (or '-') in the commandline. It will open it (stdin is already open) and assign it to the args.infile attribute. It does nothing more.
So after parsing, using args.infile gives you access to this open file, which you can read as needed (and optionally close if not stdin).
So this is a convenient way of letting your users specify which file should be opened for use in your code. It was intended for simple scripts that read one file, do something, and write to another.
But if all you are looking at is stdin, there isn't any point in using this type. sys.stdin is always available for reading. And there isn't any way of making the parser read stdin. It parses sys.argv which comes from the commandline.
There is an # prefix file feature that tells the parser to read commandline strings from a file. It parses the file and splices the values into sys.argv. See the argparse docs.
From the docs The add_argument() method
required - Whether or not the command-line option may be omitted (optionals only).
The required keyword is only used for options (e.g., -f or --foo) not for positional arguments. Just take it out.
parser.add_argument('infile', nargs='?', type=argparse.FileType('r'),
default=sys.stdin)
When parsed infile will either be a string or the sys.stdin file object. You would need to read that file to see if there is anything in it. Reading can be risky... you may block forever. But that just means that the user didn't follow instructions.
Trying to make an argument in argparse where one can input several file names that can be read.
In this example, i'm just trying to print each of the file objects to make sure it's working correctly but I get the error:
error: unrecognized arguments: f2.txt f3.txt
. How can I get it to recognize all of them?
my command in the terminal to run a program and read multiple files
python program.py f1.txt f2.txt f3.txt
Python script
import argparse
def main():
parser = argparse.ArgumentParser()
parser.add_argument('file', nargs='?', type=file)
args = parser.parse_args()
for f in args.file:
print f
if __name__ == '__main__':
main()
I used nargs='?' b/c I want it to be any number of files that can be used . If I change add_argument to:
parser.add_argument('file', nargs=3)
then I can print them as strings but I can't get it to work with '?'
If your goal is to read one or more readable files, you can try this:
parser.add_argument('file', type=argparse.FileType('r'), nargs='+')
nargs='+' gathers all command line arguments into a list. There must also be one or more arguments or an error message will be generated.
type=argparse.FileType('r') tries to open each argument as a file for reading. It will generate an error message if argparse cannot open the file. You can use this for checking whether the argument is a valid and readable file.
Alternatively, if your goal is to read zero or more readable files, you can simply replace nargs='+' with nargs='*'. This will give you an empty list if no command line arguments are supplied. Maybe you might want to open stdin if you're not given any files - if so just add default=[sys.stdin] as a parameter to add_argument.
And then to process the files in the list:
args = parser.parse_args()
for f in args.file:
for line in f:
# process file...
More about nargs:
https://docs.python.org/2/library/argparse.html#nargs
More about type: https://docs.python.org/2/library/argparse.html#type
Just had to make sure there was at least one argument
parser.add_argument('file',nargs='*')
I need to create a program called extractGenes.py
The command line parameters need to take 2 OR 3 parameters:
-s is an optional parameter, or switch, indicating that the user wwants the spliced gene sequence(introns removed). The user does not have to provide this (meaning he wants the entire gene sequence), but it he does provide it then it must be the first parameter
input file (with the genes)
output file (where the program will create to store the fasta file
The file contains lines like this:
NM_001003443 chr11 + 5925152 592608098 2 5925152,5925652, 5925404,5926898,
However, I am not sure how to include the -s optional parameter into the starting function.
So I started with:
getGenes(-s, input, output):
fp = open(input, 'r')
wp = open(output, "w")
but am unsure as to how to include the -s.
This case is simple enough to use sys.argv directly:
import sys
spliced = False
if '-s' in sys.argv:
spliced = True
sys.argv.remove('-s')
infile, outfile = sys.argv[1:]
Alternatively, you can also use the more powerful tools like argparse and optparse to generate a command-line parser:
import argparse
parser = argparse.ArgumentParser(description='Tool for extracting genes')
parser.add_argument('infile', help='source file with the genes')
parser.add_argument('outfile', help='outfile file in a FASTA format')
parser.add_argument('-s', '--spliced', action='store_true', help='remove introns')
if __name__ == '__main__':
result = parser.parse_args('-s myin myout'.split())
print vars(result)
Argparse is a python library that willl take care of optional paremeters for you. http://docs.python.org/library/argparse.html#module-argparse
Try something like this:
def getGenes(input, output, s=False):
if s:
...
else:
...
If you input 2 parameters, s will be False;
getGenes(input, output)
If you call getGenes() with 3 parameters, s will be the 3rd parameter, so in this case calling it with any non False value will yield the else clause.
I have this code:
#! /usr/bin/python
import sys, string
def findAll(search, fh):
count = 0
for line in fh:
count += 1
if line.find(search) != -1:
print "%3d: %s"%(count, line.rstrip())
return count
search = raw_input("Enter string to be found: ")
filename = raw_input("Enter filename: ")
fh = open(filename, "rU")
findAll(search, fh)
My professor recommended I write this code and incorporate "improved usage".
I'm confused as to how but she recommended that
I modify the program by commenting out the raw_input() statements, then adding statements to check if the program is invoked with fewer than 2 arguments and if so to print 'Usage: findstring.py string filename. The code takes strings and locates them in a file.
I use the filename command line argument from sys.argv to open the file and prepare for an input/output error (IOError) to occur. Then to use a try-except block to encode what to do if opening the file works or not.
If the opening fails, I print 'Error: cannot open findstring.py where findstring.py is also the considered the text file.
To be honest... I was so busy writing down her suggestions that I had no idea how to do many of the things she recommended. Can someone help improve this code? I'm confused and I don't know how to do this. My prof did say that the code would run, but I don't know how to modify it.
For improved usage, try using the argparse module. It makes taking command-line options easier.
http://docs.python.org/library/argparse.html#module-argparse
A code sample from the above link reads:
import argparse
parser = argparse.ArgumentParser(description='Process some integers.')
parser.add_argument('integers', metavar='N', type=int, nargs='+',
help='an integer for the accumulator')
parser.add_argument('--sum', dest='accumulate', action='store_const',
const=sum, default=max,
help='sum the integers (default: find the max)')
args = parser.parse_args()
print args.accumulate(args.integers)
Now think about how you might modify this sample for your assignment. You need to take strings (search term, filename) instead of integers.
For the try/except block, remember that the code to handle an error goes in the except portion of the block. That is, you might consider showing an error message in the except block.