In Python, how can we find out the command line arguments that were provided for a script, and process them?
For some more specific examples, see Implementing a "[command] [action] [parameter]" style command-line interfaces? and How do I format positional argument help using Python's optparse?.
import sys
print("\n".join(sys.argv))
sys.argv is a list that contains all the arguments passed to the script on the command line. sys.argv[0] is the script name.
Basically,
import sys
print(sys.argv[1:])
The canonical solution in the standard library is argparse (docs):
Here is an example:
from argparse import ArgumentParser
parser = ArgumentParser()
parser.add_argument("-f", "--file", dest="filename",
help="write report to FILE", metavar="FILE")
parser.add_argument("-q", "--quiet",
action="store_false", dest="verbose", default=True,
help="don't print status messages to stdout")
args = parser.parse_args()
argparse supports (among other things):
Multiple options in any order.
Short and long options.
Default values.
Generation of a usage help message.
Just going around evangelizing for argparse which is better for these reasons.. essentially:
(copied from the link)
argparse module can handle positional
and optional arguments, while
optparse can handle only optional
arguments
argparse isn’t dogmatic about
what your command line interface
should look like - options like -file
or /file are supported, as are
required options. Optparse refuses to
support these features, preferring
purity over practicality
argparse produces more
informative usage messages, including
command-line usage determined from
your arguments, and help messages for
both positional and optional
arguments. The optparse module
requires you to write your own usage
string, and has no way to display
help for positional arguments.
argparse supports action that
consume a variable number of
command-line args, while optparse
requires that the exact number of
arguments (e.g. 1, 2, or 3) be known
in advance
argparse supports parsers that
dispatch to sub-commands, while
optparse requires setting
allow_interspersed_args and doing the
parser dispatch manually
And my personal favorite:
argparse allows the type and
action parameters to add_argument()
to be specified with simple
callables, while optparse requires
hacking class attributes like
STORE_ACTIONS or CHECK_METHODS to get
proper argument checking
There is also argparse stdlib module (an "impovement" on stdlib's optparse module). Example from the introduction to argparse:
# script.py
import argparse
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument(
'integers', metavar='int', type=int, choices=range(10),
nargs='+', help='an integer in the range 0..9')
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))
Usage:
$ script.py 1 2 3 4
4
$ script.py --sum 1 2 3 4
10
If you need something fast and not very flexible
main.py:
import sys
first_name = sys.argv[1]
last_name = sys.argv[2]
print("Hello " + first_name + " " + last_name)
Then run python main.py James Smith
to produce the following output:
Hello James Smith
The docopt library is really slick. It builds an argument dict from the usage string for your app.
Eg from the docopt readme:
"""Naval Fate.
Usage:
naval_fate.py ship new <name>...
naval_fate.py ship <name> move <x> <y> [--speed=<kn>]
naval_fate.py ship shoot <x> <y>
naval_fate.py mine (set|remove) <x> <y> [--moored | --drifting]
naval_fate.py (-h | --help)
naval_fate.py --version
Options:
-h --help Show this screen.
--version Show version.
--speed=<kn> Speed in knots [default: 10].
--moored Moored (anchored) mine.
--drifting Drifting mine.
"""
from docopt import docopt
if __name__ == '__main__':
arguments = docopt(__doc__, version='Naval Fate 2.0')
print(arguments)
One way to do it is using sys.argv. This will print the script name as the first argument and all the other parameters that you pass to it.
import sys
for arg in sys.argv:
print arg
#set default args as -h , if no args:
if len(sys.argv) == 1: sys.argv[1:] = ["-h"]
I use optparse myself, but really like the direction Simon Willison is taking with his recently introduced optfunc library. It works by:
"introspecting a function
definition (including its arguments
and their default values) and using
that to construct a command line
argument parser."
So, for example, this function definition:
def geocode(s, api_key='', geocoder='google', list_geocoders=False):
is turned into this optparse help text:
Options:
-h, --help show this help message and exit
-l, --list-geocoders
-a API_KEY, --api-key=API_KEY
-g GEOCODER, --geocoder=GEOCODER
I like getopt from stdlib, eg:
try:
opts, args = getopt.getopt(sys.argv[1:], 'h', ['help'])
except getopt.GetoptError, err:
usage(err)
for opt, arg in opts:
if opt in ('-h', '--help'):
usage()
if len(args) != 1:
usage("specify thing...")
Lately I have been wrapping something similiar to this to make things less verbose (eg; making "-h" implicit).
As you can see optparse "The optparse module is deprecated with and will not be developed further; development will continue with the argparse module."
Pocoo's click is more intuitive, requires less boilerplate, and is at least as powerful as argparse.
The only weakness I've encountered so far is that you can't do much customization to help pages, but that usually isn't a requirement and docopt seems like the clear choice when it is.
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))
Assuming the Python code above is saved into a file called prog.py
$ python prog.py -h
Ref-link: https://docs.python.org/3.3/library/argparse.html
You may be interested in a little Python module I wrote to make handling of command line arguments even easier (open source and free to use) - Commando
Yet another option is argh. It builds on argparse, and lets you write things like:
import argh
# declaring:
def echo(text):
"Returns given word as is."
return text
def greet(name, greeting='Hello'):
"Greets the user with given name. The greeting is customizable."
return greeting + ', ' + name
# assembling:
parser = argh.ArghParser()
parser.add_commands([echo, greet])
# dispatching:
if __name__ == '__main__':
parser.dispatch()
It will automatically generate help and so on, and you can use decorators to provide extra guidance on how the arg-parsing should work.
I recommend looking at docopt as a simple alternative to these others.
docopt is a new project that works by parsing your --help usage message rather than requiring you to implement everything yourself. You just have to put your usage message in the POSIX format.
Also with python3 you might find convenient to use Extended Iterable Unpacking to handle optional positional arguments without additional dependencies:
try:
_, arg1, arg2, arg3, *_ = sys.argv + [None] * 2
except ValueError:
print("Not enough arguments", file=sys.stderr) # unhandled exception traceback is meaningful enough also
exit(-1)
The above argv unpack makes arg2 and arg3 "optional" - if they are not specified in argv, they will be None, while if the first is not specified, ValueError will be thouwn:
Traceback (most recent call last):
File "test.py", line 3, in <module>
_, arg1, arg2, arg3, *_ = sys.argv + [None] * 2
ValueError: not enough values to unpack (expected at least 4, got 3)
My solution is entrypoint2. Example:
from entrypoint2 import entrypoint
#entrypoint
def add(file, quiet=True):
''' This function writes report.
:param file: write report to FILE
:param quiet: don't print status messages to stdout
'''
print file,quiet
help text:
usage: report.py [-h] [-q] [--debug] file
This function writes report.
positional arguments:
file write report to FILE
optional arguments:
-h, --help show this help message and exit
-q, --quiet don't print status messages to stdout
--debug set logging level to DEBUG
import sys
# Command line arguments are stored into sys.argv
# print(sys.argv[1:])
# I used the slice [1:] to print all the elements except the first
# This because the first element of sys.argv is the program name
# So the first argument is sys.argv[1], the second is sys.argv[2] ecc
print("File name: " + sys.argv[0])
print("Arguments:")
for i in sys.argv[1:]:
print(i)
Let's name this file command_line.py and let's run it:
C:\Users\simone> python command_line.py arg1 arg2 arg3 ecc
File name: command_line.py
Arguments:
arg1
arg2
arg3
ecc
Now let's write a simple program, sum.py:
import sys
try:
print(sum(map(float, sys.argv[1:])))
except:
print("An error has occurred")
Result:
C:\Users\simone> python sum.py 10 4 6 3
23
This handles simple switches, value switches with optional alternative flags.
import sys
# [IN] argv - array of args
# [IN] switch - switch to seek
# [IN] val - expecting value
# [IN] alt - switch alternative
# returns value or True if val not expected
def parse_cmd(argv,switch,val=None,alt=None):
for idx, x in enumerate(argv):
if x == switch or x == alt:
if val:
if len(argv) > (idx+1):
if not argv[idx+1].startswith('-'):
return argv[idx+1]
else:
return True
//expecting a value for -i
i = parse_cmd(sys.argv[1:],"-i", True, "--input")
//no value needed for -p
p = parse_cmd(sys.argv[1:],"-p")
Several of our biotechnology clients have posed these two questions recently:
How can we execute a Python script as a command?
How can we pass input values to a Python script when it is executed as a command?
I have included a Python script below which I believe answers both questions. Let's assume the following Python script is saved in the file test.py:
#
#----------------------------------------------------------------------
#
# file name: test.py
#
# input values: data - location of data to be processed
# date - date data were delivered for processing
# study - name of the study where data originated
# logs - location where log files should be written
#
# macOS usage:
#
# python3 test.py "/Users/lawrence/data" "20220518" "XYZ123" "/Users/lawrence/logs"
#
# Windows usage:
#
# python test.py "D:\data" "20220518" "XYZ123" "D:\logs"
#
#----------------------------------------------------------------------
#
# import needed modules...
#
import sys
import datetime
def main(argv):
#
# print message that process is starting...
#
print("test process starting at", datetime.datetime.now().strftime("%Y%m%d %H:%M"))
#
# set local values from input values...
#
data = sys.argv[1]
date = sys.argv[2]
study = sys.argv[3]
logs = sys.argv[4]
#
# print input arguments...
#
print("data value is", data)
print("date value is", date)
print("study value is", study)
print("logs value is", logs)
#
# print message that process is ending...
#
print("test process ending at", datetime.datetime.now().strftime("%Y%m%d %H:%M"))
#
# call main() to begin processing...
#
if __name__ == '__main__':
main(sys.argv)
The script can be executed on a macOS computer in a Terminal shell as shown below and the results will be printed to standard output (be sure the current directory includes the test.py file):
$ python3 test.py "/Users/lawrence/data" "20220518" "XYZ123" "/Users/lawrence/logs"
test process starting at 20220518 16:51
data value is /Users/lawrence/data
date value is 20220518
study value is XYZ123
logs value is /Users/lawrence/logs
test process ending at 20220518 16:51
The script can also be executed on a Windows computer in a Command Prompt as shown below and the results will be printed to standard output (be sure the current directory includes the test.py file):
D:\scripts>python test.py "D:\data" "20220518" "XYZ123" "D:\logs"
test process starting at 20220518 17:20
data value is D:\data
date value is 20220518
study value is XYZ123
logs value is D:\logs
test process ending at 20220518 17:20
This script answers both questions posed above and is a good starting point for developing scripts that will be executed as commands with input values.
Reason for the new answer:
Existing answers specify multiple options.
Standard option is to use argparse, a few answers provided examples from the documentation, and one answer suggested the advantage of it. But all fail to explain the answer adequately/clearly to the actual question by OP, at least for newbies.
An example of argparse:
import argparse
def load_config(conf_file):
pass
if __name__ == '__main__':
parser = argparse.ArgumentParser()
//Specifies one argument from the command line
//You can have any number of arguments like this
parser.add_argument("conf_file", help="configuration file for the application")
args = parser.parse_args()
config = load_config(args.conf_file)
Above program expects a config file as an argument. If you provide it, it will execute happily. If not, it will print the following
usage: test.py [-h] conf_file
test.py: error: the following arguments are required: conf_file
You can have the option to specify if the argument is optional.
You can specify the expected type for the argument using type key
parser.add_argument("age", type=int, help="age of the person")
You can specify default value for the arguments by specifying default key
This document will help you to understand it to an extent.
Related
In Python, how can we find out the command line arguments that were provided for a script, and process them?
For some more specific examples, see Implementing a "[command] [action] [parameter]" style command-line interfaces? and How do I format positional argument help using Python's optparse?.
import sys
print("\n".join(sys.argv))
sys.argv is a list that contains all the arguments passed to the script on the command line. sys.argv[0] is the script name.
Basically,
import sys
print(sys.argv[1:])
The canonical solution in the standard library is argparse (docs):
Here is an example:
from argparse import ArgumentParser
parser = ArgumentParser()
parser.add_argument("-f", "--file", dest="filename",
help="write report to FILE", metavar="FILE")
parser.add_argument("-q", "--quiet",
action="store_false", dest="verbose", default=True,
help="don't print status messages to stdout")
args = parser.parse_args()
argparse supports (among other things):
Multiple options in any order.
Short and long options.
Default values.
Generation of a usage help message.
Just going around evangelizing for argparse which is better for these reasons.. essentially:
(copied from the link)
argparse module can handle positional
and optional arguments, while
optparse can handle only optional
arguments
argparse isn’t dogmatic about
what your command line interface
should look like - options like -file
or /file are supported, as are
required options. Optparse refuses to
support these features, preferring
purity over practicality
argparse produces more
informative usage messages, including
command-line usage determined from
your arguments, and help messages for
both positional and optional
arguments. The optparse module
requires you to write your own usage
string, and has no way to display
help for positional arguments.
argparse supports action that
consume a variable number of
command-line args, while optparse
requires that the exact number of
arguments (e.g. 1, 2, or 3) be known
in advance
argparse supports parsers that
dispatch to sub-commands, while
optparse requires setting
allow_interspersed_args and doing the
parser dispatch manually
And my personal favorite:
argparse allows the type and
action parameters to add_argument()
to be specified with simple
callables, while optparse requires
hacking class attributes like
STORE_ACTIONS or CHECK_METHODS to get
proper argument checking
There is also argparse stdlib module (an "impovement" on stdlib's optparse module). Example from the introduction to argparse:
# script.py
import argparse
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument(
'integers', metavar='int', type=int, choices=range(10),
nargs='+', help='an integer in the range 0..9')
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))
Usage:
$ script.py 1 2 3 4
4
$ script.py --sum 1 2 3 4
10
If you need something fast and not very flexible
main.py:
import sys
first_name = sys.argv[1]
last_name = sys.argv[2]
print("Hello " + first_name + " " + last_name)
Then run python main.py James Smith
to produce the following output:
Hello James Smith
The docopt library is really slick. It builds an argument dict from the usage string for your app.
Eg from the docopt readme:
"""Naval Fate.
Usage:
naval_fate.py ship new <name>...
naval_fate.py ship <name> move <x> <y> [--speed=<kn>]
naval_fate.py ship shoot <x> <y>
naval_fate.py mine (set|remove) <x> <y> [--moored | --drifting]
naval_fate.py (-h | --help)
naval_fate.py --version
Options:
-h --help Show this screen.
--version Show version.
--speed=<kn> Speed in knots [default: 10].
--moored Moored (anchored) mine.
--drifting Drifting mine.
"""
from docopt import docopt
if __name__ == '__main__':
arguments = docopt(__doc__, version='Naval Fate 2.0')
print(arguments)
One way to do it is using sys.argv. This will print the script name as the first argument and all the other parameters that you pass to it.
import sys
for arg in sys.argv:
print arg
#set default args as -h , if no args:
if len(sys.argv) == 1: sys.argv[1:] = ["-h"]
I use optparse myself, but really like the direction Simon Willison is taking with his recently introduced optfunc library. It works by:
"introspecting a function
definition (including its arguments
and their default values) and using
that to construct a command line
argument parser."
So, for example, this function definition:
def geocode(s, api_key='', geocoder='google', list_geocoders=False):
is turned into this optparse help text:
Options:
-h, --help show this help message and exit
-l, --list-geocoders
-a API_KEY, --api-key=API_KEY
-g GEOCODER, --geocoder=GEOCODER
I like getopt from stdlib, eg:
try:
opts, args = getopt.getopt(sys.argv[1:], 'h', ['help'])
except getopt.GetoptError, err:
usage(err)
for opt, arg in opts:
if opt in ('-h', '--help'):
usage()
if len(args) != 1:
usage("specify thing...")
Lately I have been wrapping something similiar to this to make things less verbose (eg; making "-h" implicit).
As you can see optparse "The optparse module is deprecated with and will not be developed further; development will continue with the argparse module."
Pocoo's click is more intuitive, requires less boilerplate, and is at least as powerful as argparse.
The only weakness I've encountered so far is that you can't do much customization to help pages, but that usually isn't a requirement and docopt seems like the clear choice when it is.
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))
Assuming the Python code above is saved into a file called prog.py
$ python prog.py -h
Ref-link: https://docs.python.org/3.3/library/argparse.html
You may be interested in a little Python module I wrote to make handling of command line arguments even easier (open source and free to use) - Commando
Yet another option is argh. It builds on argparse, and lets you write things like:
import argh
# declaring:
def echo(text):
"Returns given word as is."
return text
def greet(name, greeting='Hello'):
"Greets the user with given name. The greeting is customizable."
return greeting + ', ' + name
# assembling:
parser = argh.ArghParser()
parser.add_commands([echo, greet])
# dispatching:
if __name__ == '__main__':
parser.dispatch()
It will automatically generate help and so on, and you can use decorators to provide extra guidance on how the arg-parsing should work.
I recommend looking at docopt as a simple alternative to these others.
docopt is a new project that works by parsing your --help usage message rather than requiring you to implement everything yourself. You just have to put your usage message in the POSIX format.
Also with python3 you might find convenient to use Extended Iterable Unpacking to handle optional positional arguments without additional dependencies:
try:
_, arg1, arg2, arg3, *_ = sys.argv + [None] * 2
except ValueError:
print("Not enough arguments", file=sys.stderr) # unhandled exception traceback is meaningful enough also
exit(-1)
The above argv unpack makes arg2 and arg3 "optional" - if they are not specified in argv, they will be None, while if the first is not specified, ValueError will be thouwn:
Traceback (most recent call last):
File "test.py", line 3, in <module>
_, arg1, arg2, arg3, *_ = sys.argv + [None] * 2
ValueError: not enough values to unpack (expected at least 4, got 3)
My solution is entrypoint2. Example:
from entrypoint2 import entrypoint
#entrypoint
def add(file, quiet=True):
''' This function writes report.
:param file: write report to FILE
:param quiet: don't print status messages to stdout
'''
print file,quiet
help text:
usage: report.py [-h] [-q] [--debug] file
This function writes report.
positional arguments:
file write report to FILE
optional arguments:
-h, --help show this help message and exit
-q, --quiet don't print status messages to stdout
--debug set logging level to DEBUG
import sys
# Command line arguments are stored into sys.argv
# print(sys.argv[1:])
# I used the slice [1:] to print all the elements except the first
# This because the first element of sys.argv is the program name
# So the first argument is sys.argv[1], the second is sys.argv[2] ecc
print("File name: " + sys.argv[0])
print("Arguments:")
for i in sys.argv[1:]:
print(i)
Let's name this file command_line.py and let's run it:
C:\Users\simone> python command_line.py arg1 arg2 arg3 ecc
File name: command_line.py
Arguments:
arg1
arg2
arg3
ecc
Now let's write a simple program, sum.py:
import sys
try:
print(sum(map(float, sys.argv[1:])))
except:
print("An error has occurred")
Result:
C:\Users\simone> python sum.py 10 4 6 3
23
This handles simple switches, value switches with optional alternative flags.
import sys
# [IN] argv - array of args
# [IN] switch - switch to seek
# [IN] val - expecting value
# [IN] alt - switch alternative
# returns value or True if val not expected
def parse_cmd(argv,switch,val=None,alt=None):
for idx, x in enumerate(argv):
if x == switch or x == alt:
if val:
if len(argv) > (idx+1):
if not argv[idx+1].startswith('-'):
return argv[idx+1]
else:
return True
//expecting a value for -i
i = parse_cmd(sys.argv[1:],"-i", True, "--input")
//no value needed for -p
p = parse_cmd(sys.argv[1:],"-p")
Several of our biotechnology clients have posed these two questions recently:
How can we execute a Python script as a command?
How can we pass input values to a Python script when it is executed as a command?
I have included a Python script below which I believe answers both questions. Let's assume the following Python script is saved in the file test.py:
#
#----------------------------------------------------------------------
#
# file name: test.py
#
# input values: data - location of data to be processed
# date - date data were delivered for processing
# study - name of the study where data originated
# logs - location where log files should be written
#
# macOS usage:
#
# python3 test.py "/Users/lawrence/data" "20220518" "XYZ123" "/Users/lawrence/logs"
#
# Windows usage:
#
# python test.py "D:\data" "20220518" "XYZ123" "D:\logs"
#
#----------------------------------------------------------------------
#
# import needed modules...
#
import sys
import datetime
def main(argv):
#
# print message that process is starting...
#
print("test process starting at", datetime.datetime.now().strftime("%Y%m%d %H:%M"))
#
# set local values from input values...
#
data = sys.argv[1]
date = sys.argv[2]
study = sys.argv[3]
logs = sys.argv[4]
#
# print input arguments...
#
print("data value is", data)
print("date value is", date)
print("study value is", study)
print("logs value is", logs)
#
# print message that process is ending...
#
print("test process ending at", datetime.datetime.now().strftime("%Y%m%d %H:%M"))
#
# call main() to begin processing...
#
if __name__ == '__main__':
main(sys.argv)
The script can be executed on a macOS computer in a Terminal shell as shown below and the results will be printed to standard output (be sure the current directory includes the test.py file):
$ python3 test.py "/Users/lawrence/data" "20220518" "XYZ123" "/Users/lawrence/logs"
test process starting at 20220518 16:51
data value is /Users/lawrence/data
date value is 20220518
study value is XYZ123
logs value is /Users/lawrence/logs
test process ending at 20220518 16:51
The script can also be executed on a Windows computer in a Command Prompt as shown below and the results will be printed to standard output (be sure the current directory includes the test.py file):
D:\scripts>python test.py "D:\data" "20220518" "XYZ123" "D:\logs"
test process starting at 20220518 17:20
data value is D:\data
date value is 20220518
study value is XYZ123
logs value is D:\logs
test process ending at 20220518 17:20
This script answers both questions posed above and is a good starting point for developing scripts that will be executed as commands with input values.
Reason for the new answer:
Existing answers specify multiple options.
Standard option is to use argparse, a few answers provided examples from the documentation, and one answer suggested the advantage of it. But all fail to explain the answer adequately/clearly to the actual question by OP, at least for newbies.
An example of argparse:
import argparse
def load_config(conf_file):
pass
if __name__ == '__main__':
parser = argparse.ArgumentParser()
//Specifies one argument from the command line
//You can have any number of arguments like this
parser.add_argument("conf_file", help="configuration file for the application")
args = parser.parse_args()
config = load_config(args.conf_file)
Above program expects a config file as an argument. If you provide it, it will execute happily. If not, it will print the following
usage: test.py [-h] conf_file
test.py: error: the following arguments are required: conf_file
You can have the option to specify if the argument is optional.
You can specify the expected type for the argument using type key
parser.add_argument("age", type=int, help="age of the person")
You can specify default value for the arguments by specifying default key
This document will help you to understand it to an extent.
In Python, how can we find out the command line arguments that were provided for a script, and process them?
For some more specific examples, see Implementing a "[command] [action] [parameter]" style command-line interfaces? and How do I format positional argument help using Python's optparse?.
import sys
print("\n".join(sys.argv))
sys.argv is a list that contains all the arguments passed to the script on the command line. sys.argv[0] is the script name.
Basically,
import sys
print(sys.argv[1:])
The canonical solution in the standard library is argparse (docs):
Here is an example:
from argparse import ArgumentParser
parser = ArgumentParser()
parser.add_argument("-f", "--file", dest="filename",
help="write report to FILE", metavar="FILE")
parser.add_argument("-q", "--quiet",
action="store_false", dest="verbose", default=True,
help="don't print status messages to stdout")
args = parser.parse_args()
argparse supports (among other things):
Multiple options in any order.
Short and long options.
Default values.
Generation of a usage help message.
Just going around evangelizing for argparse which is better for these reasons.. essentially:
(copied from the link)
argparse module can handle positional
and optional arguments, while
optparse can handle only optional
arguments
argparse isn’t dogmatic about
what your command line interface
should look like - options like -file
or /file are supported, as are
required options. Optparse refuses to
support these features, preferring
purity over practicality
argparse produces more
informative usage messages, including
command-line usage determined from
your arguments, and help messages for
both positional and optional
arguments. The optparse module
requires you to write your own usage
string, and has no way to display
help for positional arguments.
argparse supports action that
consume a variable number of
command-line args, while optparse
requires that the exact number of
arguments (e.g. 1, 2, or 3) be known
in advance
argparse supports parsers that
dispatch to sub-commands, while
optparse requires setting
allow_interspersed_args and doing the
parser dispatch manually
And my personal favorite:
argparse allows the type and
action parameters to add_argument()
to be specified with simple
callables, while optparse requires
hacking class attributes like
STORE_ACTIONS or CHECK_METHODS to get
proper argument checking
There is also argparse stdlib module (an "impovement" on stdlib's optparse module). Example from the introduction to argparse:
# script.py
import argparse
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument(
'integers', metavar='int', type=int, choices=range(10),
nargs='+', help='an integer in the range 0..9')
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))
Usage:
$ script.py 1 2 3 4
4
$ script.py --sum 1 2 3 4
10
If you need something fast and not very flexible
main.py:
import sys
first_name = sys.argv[1]
last_name = sys.argv[2]
print("Hello " + first_name + " " + last_name)
Then run python main.py James Smith
to produce the following output:
Hello James Smith
The docopt library is really slick. It builds an argument dict from the usage string for your app.
Eg from the docopt readme:
"""Naval Fate.
Usage:
naval_fate.py ship new <name>...
naval_fate.py ship <name> move <x> <y> [--speed=<kn>]
naval_fate.py ship shoot <x> <y>
naval_fate.py mine (set|remove) <x> <y> [--moored | --drifting]
naval_fate.py (-h | --help)
naval_fate.py --version
Options:
-h --help Show this screen.
--version Show version.
--speed=<kn> Speed in knots [default: 10].
--moored Moored (anchored) mine.
--drifting Drifting mine.
"""
from docopt import docopt
if __name__ == '__main__':
arguments = docopt(__doc__, version='Naval Fate 2.0')
print(arguments)
One way to do it is using sys.argv. This will print the script name as the first argument and all the other parameters that you pass to it.
import sys
for arg in sys.argv:
print arg
#set default args as -h , if no args:
if len(sys.argv) == 1: sys.argv[1:] = ["-h"]
I use optparse myself, but really like the direction Simon Willison is taking with his recently introduced optfunc library. It works by:
"introspecting a function
definition (including its arguments
and their default values) and using
that to construct a command line
argument parser."
So, for example, this function definition:
def geocode(s, api_key='', geocoder='google', list_geocoders=False):
is turned into this optparse help text:
Options:
-h, --help show this help message and exit
-l, --list-geocoders
-a API_KEY, --api-key=API_KEY
-g GEOCODER, --geocoder=GEOCODER
I like getopt from stdlib, eg:
try:
opts, args = getopt.getopt(sys.argv[1:], 'h', ['help'])
except getopt.GetoptError, err:
usage(err)
for opt, arg in opts:
if opt in ('-h', '--help'):
usage()
if len(args) != 1:
usage("specify thing...")
Lately I have been wrapping something similiar to this to make things less verbose (eg; making "-h" implicit).
As you can see optparse "The optparse module is deprecated with and will not be developed further; development will continue with the argparse module."
Pocoo's click is more intuitive, requires less boilerplate, and is at least as powerful as argparse.
The only weakness I've encountered so far is that you can't do much customization to help pages, but that usually isn't a requirement and docopt seems like the clear choice when it is.
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))
Assuming the Python code above is saved into a file called prog.py
$ python prog.py -h
Ref-link: https://docs.python.org/3.3/library/argparse.html
You may be interested in a little Python module I wrote to make handling of command line arguments even easier (open source and free to use) - Commando
Yet another option is argh. It builds on argparse, and lets you write things like:
import argh
# declaring:
def echo(text):
"Returns given word as is."
return text
def greet(name, greeting='Hello'):
"Greets the user with given name. The greeting is customizable."
return greeting + ', ' + name
# assembling:
parser = argh.ArghParser()
parser.add_commands([echo, greet])
# dispatching:
if __name__ == '__main__':
parser.dispatch()
It will automatically generate help and so on, and you can use decorators to provide extra guidance on how the arg-parsing should work.
I recommend looking at docopt as a simple alternative to these others.
docopt is a new project that works by parsing your --help usage message rather than requiring you to implement everything yourself. You just have to put your usage message in the POSIX format.
Also with python3 you might find convenient to use Extended Iterable Unpacking to handle optional positional arguments without additional dependencies:
try:
_, arg1, arg2, arg3, *_ = sys.argv + [None] * 2
except ValueError:
print("Not enough arguments", file=sys.stderr) # unhandled exception traceback is meaningful enough also
exit(-1)
The above argv unpack makes arg2 and arg3 "optional" - if they are not specified in argv, they will be None, while if the first is not specified, ValueError will be thouwn:
Traceback (most recent call last):
File "test.py", line 3, in <module>
_, arg1, arg2, arg3, *_ = sys.argv + [None] * 2
ValueError: not enough values to unpack (expected at least 4, got 3)
My solution is entrypoint2. Example:
from entrypoint2 import entrypoint
#entrypoint
def add(file, quiet=True):
''' This function writes report.
:param file: write report to FILE
:param quiet: don't print status messages to stdout
'''
print file,quiet
help text:
usage: report.py [-h] [-q] [--debug] file
This function writes report.
positional arguments:
file write report to FILE
optional arguments:
-h, --help show this help message and exit
-q, --quiet don't print status messages to stdout
--debug set logging level to DEBUG
import sys
# Command line arguments are stored into sys.argv
# print(sys.argv[1:])
# I used the slice [1:] to print all the elements except the first
# This because the first element of sys.argv is the program name
# So the first argument is sys.argv[1], the second is sys.argv[2] ecc
print("File name: " + sys.argv[0])
print("Arguments:")
for i in sys.argv[1:]:
print(i)
Let's name this file command_line.py and let's run it:
C:\Users\simone> python command_line.py arg1 arg2 arg3 ecc
File name: command_line.py
Arguments:
arg1
arg2
arg3
ecc
Now let's write a simple program, sum.py:
import sys
try:
print(sum(map(float, sys.argv[1:])))
except:
print("An error has occurred")
Result:
C:\Users\simone> python sum.py 10 4 6 3
23
This handles simple switches, value switches with optional alternative flags.
import sys
# [IN] argv - array of args
# [IN] switch - switch to seek
# [IN] val - expecting value
# [IN] alt - switch alternative
# returns value or True if val not expected
def parse_cmd(argv,switch,val=None,alt=None):
for idx, x in enumerate(argv):
if x == switch or x == alt:
if val:
if len(argv) > (idx+1):
if not argv[idx+1].startswith('-'):
return argv[idx+1]
else:
return True
//expecting a value for -i
i = parse_cmd(sys.argv[1:],"-i", True, "--input")
//no value needed for -p
p = parse_cmd(sys.argv[1:],"-p")
Several of our biotechnology clients have posed these two questions recently:
How can we execute a Python script as a command?
How can we pass input values to a Python script when it is executed as a command?
I have included a Python script below which I believe answers both questions. Let's assume the following Python script is saved in the file test.py:
#
#----------------------------------------------------------------------
#
# file name: test.py
#
# input values: data - location of data to be processed
# date - date data were delivered for processing
# study - name of the study where data originated
# logs - location where log files should be written
#
# macOS usage:
#
# python3 test.py "/Users/lawrence/data" "20220518" "XYZ123" "/Users/lawrence/logs"
#
# Windows usage:
#
# python test.py "D:\data" "20220518" "XYZ123" "D:\logs"
#
#----------------------------------------------------------------------
#
# import needed modules...
#
import sys
import datetime
def main(argv):
#
# print message that process is starting...
#
print("test process starting at", datetime.datetime.now().strftime("%Y%m%d %H:%M"))
#
# set local values from input values...
#
data = sys.argv[1]
date = sys.argv[2]
study = sys.argv[3]
logs = sys.argv[4]
#
# print input arguments...
#
print("data value is", data)
print("date value is", date)
print("study value is", study)
print("logs value is", logs)
#
# print message that process is ending...
#
print("test process ending at", datetime.datetime.now().strftime("%Y%m%d %H:%M"))
#
# call main() to begin processing...
#
if __name__ == '__main__':
main(sys.argv)
The script can be executed on a macOS computer in a Terminal shell as shown below and the results will be printed to standard output (be sure the current directory includes the test.py file):
$ python3 test.py "/Users/lawrence/data" "20220518" "XYZ123" "/Users/lawrence/logs"
test process starting at 20220518 16:51
data value is /Users/lawrence/data
date value is 20220518
study value is XYZ123
logs value is /Users/lawrence/logs
test process ending at 20220518 16:51
The script can also be executed on a Windows computer in a Command Prompt as shown below and the results will be printed to standard output (be sure the current directory includes the test.py file):
D:\scripts>python test.py "D:\data" "20220518" "XYZ123" "D:\logs"
test process starting at 20220518 17:20
data value is D:\data
date value is 20220518
study value is XYZ123
logs value is D:\logs
test process ending at 20220518 17:20
This script answers both questions posed above and is a good starting point for developing scripts that will be executed as commands with input values.
Reason for the new answer:
Existing answers specify multiple options.
Standard option is to use argparse, a few answers provided examples from the documentation, and one answer suggested the advantage of it. But all fail to explain the answer adequately/clearly to the actual question by OP, at least for newbies.
An example of argparse:
import argparse
def load_config(conf_file):
pass
if __name__ == '__main__':
parser = argparse.ArgumentParser()
//Specifies one argument from the command line
//You can have any number of arguments like this
parser.add_argument("conf_file", help="configuration file for the application")
args = parser.parse_args()
config = load_config(args.conf_file)
Above program expects a config file as an argument. If you provide it, it will execute happily. If not, it will print the following
usage: test.py [-h] conf_file
test.py: error: the following arguments are required: conf_file
You can have the option to specify if the argument is optional.
You can specify the expected type for the argument using type key
parser.add_argument("age", type=int, help="age of the person")
You can specify default value for the arguments by specifying default key
This document will help you to understand it to an extent.
The documentation for the argparse python module, while excellent I'm sure, is too much for my tiny beginner brain to grasp right now. I don't need to do math on the command line or meddle with formatting lines on the screen or change option characters. All I want to do is "If arg is A, do this, if B do that, if none of the above show help and quit".
Here's the way I do it with argparse (with multiple args):
parser = argparse.ArgumentParser(description='Description of your program')
parser.add_argument('-f','--foo', help='Description for foo argument', required=True)
parser.add_argument('-b','--bar', help='Description for bar argument', required=True)
args = vars(parser.parse_args())
args will be a dictionary containing the arguments:
if args['foo'] == 'Hello':
# code here
if args['bar'] == 'World':
# code here
In your case simply add only one argument.
My understanding of the original question is two-fold. First, in terms of the simplest possible argparse example, I'm surprised that I haven't seen it here. Of course, to be dead-simple, it's also all overhead with little power, but it might get you started.
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("a")
args = parser.parse_args()
if args.a == 'magic.name':
print 'You nailed it!'
But this positional argument is now required. If you leave it out when invoking this program, you'll get an error about missing arguments. This leads me to the second part of the original question. Matt Wilkie seems to want a single optional argument without a named label (the --option labels). My suggestion would be to modify the code above as follows:
...
parser.add_argument("a", nargs='?', default="check_string_for_empty")
...
if args.a == 'check_string_for_empty':
print 'I can tell that no argument was given and I can deal with that here.'
elif args.a == 'magic.name':
print 'You nailed it!'
else:
print args.a
There may well be a more elegant solution, but this works and is minimalist.
The argparse documentation is reasonably good but leaves out a few useful details which might not be obvious. (#Diego Navarro already mentioned some of this but I'll try to expand on his answer slightly.) Basic usage is as follows:
parser = argparse.ArgumentParser()
parser.add_argument('-f', '--my-foo', default='foobar')
parser.add_argument('-b', '--bar-value', default=3.14)
args = parser.parse_args()
The object you get back from parse_args() is a 'Namespace' object: An object whose member variables are named after your command-line arguments. The Namespace object is how you access your arguments and the values associated with them:
args = parser.parse_args()
print (args.my_foo)
print (args.bar_value)
(Note that argparse replaces '-' in your argument names with underscores when naming the variables.)
In many situations you may wish to use arguments simply as flags which take no value. You can add those in argparse like this:
parser.add_argument('--foo', action='store_true')
parser.add_argument('--no-foo', action='store_false')
The above will create variables named 'foo' with value True, and 'no_foo' with value False, respectively:
if (args.foo):
print ("foo is true")
if (args.no_foo is False):
print ("nofoo is false")
Note also that you can use the "required" option when adding an argument:
parser.add_argument('-o', '--output', required=True)
That way if you omit this argument at the command line argparse will tell you it's missing and stop execution of your script.
Finally, note that it's possible to create a dict structure of your arguments using the vars function, if that makes life easier for you.
args = parser.parse_args()
argsdict = vars(args)
print (argsdict['my_foo'])
print (argsdict['bar_value'])
As you can see, vars returns a dict with your argument names as keys and their values as, er, values.
There are lots of other options and things you can do, but this should cover the most essential, common usage scenarios.
Matt is asking about positional parameters in argparse, and I agree that the Python documentation is lacking on this aspect. There's not a single, complete example in the ~20 odd pages that shows both parsing and using positional parameters.
None of the other answers here show a complete example of positional parameters, either, so here's a complete example:
# tested with python 2.7.1
import argparse
parser = argparse.ArgumentParser(description="An argparse example")
parser.add_argument('action', help='The action to take (e.g. install, remove, etc.)')
parser.add_argument('foo-bar', help='Hyphens are cumbersome in positional arguments')
args = parser.parse_args()
if args.action == "install":
print("You asked for installation")
else:
print("You asked for something other than installation")
# The following do not work:
# print(args.foo-bar)
# print(args.foo_bar)
# But this works:
print(getattr(args, 'foo-bar'))
The thing that threw me off is that argparse will convert the named argument "--foo-bar" into "foo_bar", but a positional parameter named "foo-bar" stays as "foo-bar", making it less obvious how to use it in your program.
Notice the two lines near the end of my example -- neither of those will work to get the value of the foo-bar positional param. The first one is obviously wrong (it's an arithmetic expression args.foo minus bar), but the second one doesn't work either:
AttributeError: 'Namespace' object has no attribute 'foo_bar'
If you want to use the foo-bar attribute, you must use getattr, as seen in the last line of my example. What's crazy is that if you tried to use dest=foo_bar to change the property name to something that's easier to access, you'd get a really bizarre error message:
ValueError: dest supplied twice for positional argument
Here's how the example above runs:
$ python test.py
usage: test.py [-h] action foo-bar
test.py: error: too few arguments
$ python test.py -h
usage: test.py [-h] action foo-bar
An argparse example
positional arguments:
action The action to take (e.g. install, remove, etc.)
foo-bar Hyphens are cumbersome in positional arguments
optional arguments:
-h, --help show this help message and exit
$ python test.py install foo
You asked for installation
foo
Yet another summary introduction, inspired by this post.
import argparse
# define functions, classes, etc.
# executes when your script is called from the command-line
if __name__ == "__main__":
parser = argparse.ArgumentParser()
#
# define each option with: parser.add_argument
#
args = parser.parse_args() # automatically looks at sys.argv
#
# access results with: args.argumentName
#
Arguments are defined with combinations of the following:
parser.add_argument( 'name', options... ) # positional argument
parser.add_argument( '-x', options... ) # single-char flag
parser.add_argument( '-x', '--long-name', options... ) # flag with long name
Common options are:
help: description for this arg when --help is used.
default: default value if the arg is omitted.
type: if you expect a float or int (otherwise is str).
dest: give a different name to a flag (e.g. '-x', '--long-name', dest='longName'). Note: by default --long-name is accessed with args.long_name
action: for special handling of certain arguments
store_true, store_false: for boolean args '--foo', action='store_true' => args.foo == True
store_const: to be used with option const '--foo', action='store_const', const=42 => args.foo == 42
count: for repeated options, as in ./myscript.py -vv '-v', action='count' => args.v == 2
append: for repeated options, as in ./myscript.py --foo 1 --foo 2 '--foo', action='append' => args.foo == ['1', '2']
required: if a flag is required, or a positional argument is not.
nargs: for a flag to capture N args ./myscript.py --foo a b => args.foo = ['a', 'b']
choices: to restrict possible inputs (specify as list of strings, or ints if type=int).
Note the Argparse Tutorial in Python HOWTOs. It starts from most basic examples, like this one:
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("square", type=int,
help="display a square of a given number")
args = parser.parse_args()
print(args.square**2)
and progresses to less basic ones.
There is an example with predefined choice for an option, like what is asked:
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("square", type=int,
help="display a square of a given number")
parser.add_argument("-v", "--verbosity", type=int, choices=[0, 1, 2],
help="increase output verbosity")
args = parser.parse_args()
answer = args.square**2
if args.verbosity == 2:
print("the square of {} equals {}".format(args.square, answer))
elif args.verbosity == 1:
print("{}^2 == {}".format(args.square, answer))
else:
print(answer)
Here's what I came up with in my learning project thanks mainly to #DMH...
Demo code:
import argparse
def main():
parser = argparse.ArgumentParser()
parser.add_argument('-f', '--flag', action='store_true', default=False) # can 'store_false' for no-xxx flags
parser.add_argument('-r', '--reqd', required=True)
parser.add_argument('-o', '--opt', default='fallback')
parser.add_argument('arg', nargs='*') # use '+' for 1 or more args (instead of 0 or more)
parsed = parser.parse_args()
# NOTE: args with '-' have it replaced with '_'
print('Result:', vars(parsed))
print('parsed.reqd:', parsed.reqd)
if __name__ == "__main__":
main()
This may have evolved and is available online: command-line.py
Script to give this code a workout: command-line-demo.sh
code file: argparseDemo.py
Simple: common case
name(abbr, full), with help
import argparse
argParser = argparse.ArgumentParser()
argParser.add_argument("-n", "--name", help="your name")
args = argParser.parse_args()
print("args=%s" % args)
print("args.name=%s" % args.name)
call
python argparseDemo.py -n Crifan
python argparseDemo.py --name Crifan
output: args=Namespace(name='Crifan') and args.name=Crifan
type
argParser.add_argument("-a", "--age", type=int, help="your current age")
print("type(args.age)=%s" % type(args.age))
call: python argparseDemo.py --age 30
output: type(args.age)=<class 'int'> and args.age=30
required
argParser.add_argument("-a", "--age", required=True, type=int, help="your current age")
call: python argparseDemo.py
output: error argparseDemo.py: error: the following arguments are required: -a/--age
default
argParser.add_argument("-a", "--age", type=int, default=20, help="your current age. Default is 20")
call: python argparseDemo.py
output: args.age=20
choices
argParser.add_argument("-f", "--love-fruit", choices=['apple', 'orange', 'banana'], help="your love fruits")
call: python argparseDemo.py -f apple
output: args=Namespace(love_fruit='apple') and args.love_fruit=apple
multi args
argParser.add_argument("-f", "--love-fruit", nargs=2, help="your love fruits")
call: python argparseDemo.py -f apple orange
output: args.love_fruit=['apple', 'orange']
Detail
most simple: -x
code:
import argparse
argParser = argparse.ArgumentParser()
argParser.add_argument("-a") # most simple -> got args.a, type is `str`
args = argParser.parse_args()
print("args.a=%s" % args.a)
usage = run in command line
python argparseDemo.py -a 30
or: ./argparseDemo.py -a 30
makesure argparseDemo.py is executable
if not, add it: chmod +x argparseDemo.py
output
args.a=30
Note
default type is str
argParser.add_argument("-a") == argParser.add_argument("-a", type=str)
print("type(args.a)=%s" % type(args.a)) -> type(args.a)=<class 'str'>
args type is Namespace
print("type(args)=%s" % type(args)) -> type(args)=<class 'argparse.Namespace'>
args value is Namespace(a='30')
print("args=%s" % args) -> args=Namespace(a='30')
so we can call/use args.a
parameter name
full parameter name: --xxx
code
argParser.add_argument("-a", "--age")
usage
python argparseDemo.py -a 30
or: python argparseDemo.py --age 30
get parsed value: args.age
Note: NOT args.a, and NOT exist args.a
full parameter name with multiple words: --xxx-yyy
code
argParser.add_argument("-a", "--current-age")
get parsed value: args.current_age
add help description: help
code
argParser.add_argument("-a", help="your age") # with help
output
use --help can see description
python argparseDemo.py --help
usage: argparseDemo.py [-h] [-a A]
optional arguments:
-h, --help show this help message and exit
-a A your age
designate parameter type: type
code
argParser.add_argument("-a", type=int) # parsed arg is `int`, not default `str`
output
print("type(args.a)=%s" % type(args.a)) -> type(args.a)=<class 'int'>
print("args=%s" % args) -> args=Namespace(a=30)
add default value: default
code
argParser.add_argument("-a", type=int, default=20) # if not pass a, a use default value: 20
effect
usage: python argparseDemo.py
output: print("args.age=%s" % args.age) -> args=Namespace(a=20)
You could also use plac (a wrapper around argparse).
As a bonus it generates neat help instructions - see below.
Example script:
#!/usr/bin/env python3
def main(
arg: ('Argument with two possible values', 'positional', None, None, ['A', 'B'])
):
"""General help for application"""
if arg == 'A':
print("Argument has value A")
elif arg == 'B':
print("Argument has value B")
if __name__ == '__main__':
import plac
plac.call(main)
Example output:
No arguments supplied - example.py:
usage: example.py [-h] {A,B}
example.py: error: the following arguments are required: arg
Unexpected argument supplied - example.py C:
usage: example.py [-h] {A,B}
example.py: error: argument arg: invalid choice: 'C' (choose from 'A', 'B')
Correct argument supplied - example.py A :
Argument has value A
Full help menu (generated automatically) - example.py -h:
usage: example.py [-h] {A,B}
General help for application
positional arguments:
{A,B} Argument with two possible values
optional arguments:
-h, --help show this help message and exit
Short explanation:
The name of the argument usually equals the parameter name (arg).
The tuple annotation after arg parameter has the following meaning:
Description (Argument with two possible values)
Type of argument - one of 'flag', 'option' or 'positional' (positional)
Abbreviation (None)
Type of argument value - eg. float, string (None)
Restricted set of choices (['A', 'B'])
Documentation:
To learn more about using plac check out its great documentation:
Plac: Parsing the Command Line the Easy Way
To add to what others have stated:
I usually like to use the 'dest' parameter to specify a variable name and then use 'globals().update()' to put those variables in the global namespace.
Usage:
$ python script.py -i "Hello, World!"
Code:
...
parser.add_argument('-i', '--input', ..., dest='inputted_variable',...)
globals().update(vars(parser.parse_args()))
...
print(inputted_variable) # Prints "Hello, World!"
New to this, but combining Python with Powershell and using this template, being inspired by an in-depth and great Python Command Line Arguments – Real Python
There is a lot you can do within the init_argparse() and I am covering just the most simple scenario here.
import argparse
use if __name__ == "__main__": main() pattern to execute from terminal
parse arguments within the main() function that has no parameters as all
define a init_argparse() function
create a parser object by calling argparse.ArgumentParser()
declare one or more argumnent with parser.add_argument("--<long_param_name>")
return parser
parse args by creating an args object by calling parser.parse_args()
define a function proper with param1, param2, ...
call function_proper with params being assigned as attributes of an args object
e.g. function_proper(param1=args.param1, param2=args.param2)
within a shell call the module with named arguments:
e.g. python foobar.py --param1="foo" --param2=="bar"
#file: foobar.py
import argparse
def function_proper(param1, param2):
#CODE...
def init_argparse() -> argparse.ArgumentParser:
parser = argparse.ArgumentParser()
parser.add_argument("--param1")
parser.add_argument("--param2")
return parser
def main() -> None:
parser = init_argparse()
args = parser.parse_args()
function_proper(param1=args.param1, param2=args.param2)
if __name__ == "__main__":
main()
>>> python .\foobar.py --param1="foo" --param2=="bar"
I went through all the examples and answers and in a way or another they didn't address my need. So I will list her a scenario that I need more help and I hope this can explain the idea more.
Initial Problem
I need to develop a tool which is getting a file to process it and it needs some optional configuration file to be used to configure the tool.
so what I need is something like the following
mytool.py file.text -config config-file.json
The solution
Here is the solution code
import argparse
def main():
parser = argparse.ArgumentParser(description='This example for a tool to process a file and configure the tool using a config file.')
parser.add_argument('filename', help="Input file either text, image or video")
# parser.add_argument('config_file', help="a JSON file to load the initial configuration ")
# parser.add_argument('-c', '--config_file', help="a JSON file to load the initial configuration ", default='configFile.json', required=False)
parser.add_argument('-c', '--config', default='configFile.json', dest='config_file', help="a JSON file to load the initial configuration " )
parser.add_argument('-d', '--debug', action="store_true", help="Enable the debug mode for logging debug statements." )
args = parser.parse_args()
filename = args.filename
configfile = args.config_file
print("The file to be processed is", filename)
print("The config file is", configfile)
if args.debug:
print("Debug mode enabled")
else:
print("Debug mode disabled")
print("and all arguments are: ", args)
if __name__ == '__main__':
main()
I will show the solution in multiple enhancements to show the idea
First Round: List the arguments
List all input as mandatory inputs so second argument will be
parser.add_argument('config_file', help="a JSON file to load the initial configuration ")
When we get the help command for this tool we find the following outcome
(base) > python .\argparser_example.py -h
usage: argparser_example.py [-h] filename config_file
This example for a tool to process a file and configure the tool using a config file.
positional arguments:
filename Input file either text, image or video
config_file a JSON file to load the initial configuration
optional arguments:
-h, --help show this help message and exit
and when I execute it as the following
(base) > python .\argparser_example.py filename.txt configfile.json
the outcome will be
The file to be processed is filename.txt
The config file is configfile.json
and all arguments are: Namespace(config_file='configfile.json', filename='filename.txt')
But the config file should be optional, I removed it from the arguments
(base) > python .\argparser_example.py filename.txt
The outcome will be is:
usage: argparser_example.py [-h] filename config_file
argparser_example.py: error: the following arguments are required: c
Which means we have a problem in the tool
Second Round : Make it optimal
So to make it optional I modified the program as follows
parser.add_argument('-c', '--config', help="a JSON file to load the initial configuration ", default='configFile.json', required=False)
The help outcome should be
usage: argparser_example.py [-h] [-c CONFIG] filename
This example for a tool to process a file and configure the tool using a config file.
positional arguments:
filename Input file either text, image or video
optional arguments:
-h, --help show this help message and exit
-c CONFIG, --config CONFIG
a JSON file to load the initial configuration
so when I execute the program
(base) > python .\argparser_example.py filename.txt
the outcome will be
The file to be processed is filename.txt
The config file is configFile.json
and all arguments are: Namespace(config_file='configFile.json', filename='filename.txt')
with arguments like
(base) > python .\argparser_example.py filename.txt --config_file anotherConfig.json
The outcome will be
The file to be processed is filename.txt
The config file is anotherConfig.json
and all arguments are: Namespace(config_file='anotherConfig.json', filename='filename.txt')
Round 3: Enhancements
to change the flag name from --config_file to --config while we keep the variable name as is we modify the code to include dest='config_file' as the following:
parser.add_argument('-c', '--config', help="a JSON file to load the initial configuration ", default='configFile.json', dest='config_file')
and the command will be
(base) > python .\argparser_example.py filename.txt --config anotherConfig.json
To add the support for having a debug mode flag, we need to add a flag in the arguments to support a boolean debug flag. To implement it i added the following:
parser.add_argument('-d', '--debug', action="store_true", help="Enable the debug mode for logging debug statements." )
the tool command will be:
(carnd-term1-38) > python .\argparser_example.py image.jpg -c imageConfig,json --debug
the outcome will be
The file to be processed is image.jpg
The config file is imageConfig,json
Debug mode enabled
and all arguments are: Namespace(config_file='imageConfig,json', debug=True, filename='image.jpg')
A really simple way to use argparse and amend the '-h'/ '--help' switches to display your own personal code help instructions is to set the default help to False, you can also add as many additional .add_arguments as you like:
import argparse
parser = argparse.ArgumentParser(add_help=False)
parser.add_argument('-h', '--help', action='help',
help='To run this script please provide two arguments')
parser.parse_args()
Run: python test.py -h
Output:
usage: test.py [-h]
optional arguments:
-h, --help To run this script please provide two arguments
As an addition to existing answers, if you are lazy enough, it is possible to use code generation tool called protoargs. It generates arguments parser from the configuration. For python it uses argparse.
Configuration with optional A and B:
syntax = "proto2";
message protoargs
{
optional string A = 1; // A param description
optional string B = 2; // B param description
}//protoargs
Configuration with required A and B:
syntax = "proto2";
message protoargs
{
required string A = 1; // A param description
required string B = 2; // B param description
}//protoargs
Configuration with positional A and B:
syntax = "proto2";
message protoargs
{
required string A = 1; // A param description
required string B = 2; // B param description
}//protoargs
message protoargs_links
{
}//protoargs_links
Now all you should run is:
python ./protoargs.py -i test.proto -o . --py
And use it (it is possible to take other examples here):
import sys
import test_pa
class ArgsParser:
program = "test"
description = "Simple A and B parser test."
def parse(self, argv):
self.config = test_pa.parse(self.program, self.description, argv)
def usage(self):
return test_pa.usage(self.program, self.description)
if __name__ == "__main__":
parser = ArgsParser()
if len(sys.argv) == 1:
print(parser.usage())
else:
parser.parse(sys.argv[1:])
if parser.config.A:
print(parser.config.A)
if parser.config.B:
print(parser.config.B)
If you want more - change configuration, regenerate parser, use an updated parser.config.
UPD: As mentioned in rules, I must specify that this is my own project
code:
import argparse
parser=argparse.ArgumentParser()
parser.add_argument('-A', default=False, action='store_true')
parser.add_argument('-B', default=False, action='store_true')
args=parser.parse_args()
if args.A:
print('do this')
elif args.B:
print('do that')
else:
print('help')
running result:
$ python3 test.py
help
$ python3 test.py -A
do this
$ python3 test.py -B
do that
$ python3 test.py -C
usage: test.py [-h] [-A] [-B]
test.py: error: unrecognized arguments: -C
As for the original request (if A ....), I would use argv to solve it, not using argparse at all:
import sys
if len(sys.argv)==2:
if sys.argv[1] == 'A':
print('do this')
elif sys.argv[1] == 'B':
print('do that')
else:
print('help')
else:
print('help')
Since you have not clarified wheather the arguments 'A' and 'B' are positional or optional, I'll make a mix of both.
Positional arguments are required by default. If not giving one will throw 'Few arguments given' which is not the case for the optional arguments going by their name. This program will take a number and return its square by default, if the cube option is used it shall return its cube.
import argparse
parser = argparse.ArgumentParser('number-game')
parser.add_argument(
"number",
type=int,
help="enter a number"
)
parser.add_argument(
"-c", "--choice",
choices=['square','cube'],
help="choose what you need to do with the number"
)
# all the results will be parsed by the parser and stored in args
args = parser.parse_args()
# if square is selected return the square, same for cube
if args.c == 'square':
print("{} is the result".format(args.number**2))
elif args.c == 'cube':
print("{} is the result".format(args.number**3))
else:
print("{} is not changed".format(args.number))
usage
$python3 script.py 4 -c square
16
Here the optional arguments are taking value, if you just wanted to use it like a flag you can too. So by using -s for square and -c for cube we change the behaviour, by adding action = "store_true". It is changed to true only when used.
parser.add_argument(
"-s", "--square",
help="returns the square of number",
action="store_true"
)
parser.add_argument(
"-c", "--cube",
help="returns the cube of number",
action="store_true"
)
so the conditional block can be changed to,
if args.s:
print("{} is the result".format(args.number**2))
elif args.c:
print("{} is the result".format(args.number**3))
else:
print("{} is not changed".format(args.number))
usage
$python3 script.py 4 -c
64
The simplest answer!
P.S. the one who wrote the document of argparse is foolish
python code:
import argparse
parser = argparse.ArgumentParser(description='')
parser.add_argument('--o_dct_fname',type=str)
parser.add_argument('--tp',type=str)
parser.add_argument('--new_res_set',type=int)
args = parser.parse_args()
o_dct_fname = args.o_dct_fname
tp = args.tp
new_res_set = args.new_res_set
running code
python produce_result.py --o_dct_fname o_dct --tp father_child --new_res_set 1
I'm writing a wrapper around the ssh command line client. After the first positional argument that's part of command, all further options should also be treated as positional arguments.
Under optparse, I believe this would be done with disable_interspersed_args.
Presently I have something like this:
parser = argparse.ArgumentParser()
parser.add_argument('--parallel', default=False, action='store_true')
# maybe allow no command? this would ssh interactively into each machine...
parser.add_argument('command', nargs='+')
args = parser.parse_args()
But if options are passed as part of the command (such as my_wrapper ls -l), they're instead interpreted by ArgumentParser as unknown options. error: unrecognized arguments: -l
If I use parse_known_args(), the options may be taken out of order.
p = argparse.ArgumentParser()
p.add_argument('-a', action='store_true')
p.add_argument('command', nargs='+')
print(p.parse_known_args())
$ python3 bah.py -b ls -l -a
(Namespace(a=True, command=['ls']), ['-b', '-l'])
Here you can see that -b's position before ls has been lost, and -a has been parsed out from the command, which is not desired.
How can I:
Prevent arguments from being parsed after a certain point?
Disable parsing of interspersed arguments?
Allow arguments with a prefix to be consumed as positional arguments?
I had the same problem. I found the solution on the argparse bug tracker: http://code.google.com/p/argparse/issues/detail?id=52
The solution is simple: replace nargs='+' (or '*') with nargs=argparse.REMAINDER. This special value is not documented, but it does what you want.
I think your best bet to start solving these issues is to try out -- after all your optional args. -- is a pseudo-arg that tells ArgumentParser that everything after is a positional argument. Docs are here
As for prevent arguments from being parsed after a certain point, you can pass part of argv to parse_args. That combined with some introspection can be used to limit what is parsed.
What #dcolish suggested is the universal approach. Here is a sample implementation which also supports the standard -- separator, but its usage is not required for correct parsing.
Result:
# ./parse-pos.py -h
usage: parse-pos.py [-h] [-qa] [-qb] COMMAND [ARGS...]
# ./parse-pos.py -qa ls -q -h aa /bb
try_argv = ['-qa', 'ls']
cmd_rest_argv = ['-q', '-h', 'aa', '/bb']
parsed_args = Namespace(command='ls', qa=True, qb=False)
The code:
#!/usr/bin/python3
import argparse
import sys
from pprint import pprint
class CustomParserError(Exception):
pass
class CustomArgumentParser(argparse.ArgumentParser):
def error(self, message):
raise CustomParserError(message)
def original_error(self, message):
super().error(message)
def parse_argv():
parser = CustomArgumentParser(description='Example')
parser.add_argument('command', metavar='COMMAND [ARGS...]', help='the command to be executed')
parser.add_argument('-qa', action='store_true') # "ambiguous option" if you specify just "-q"
parser.add_argument('-qb', action='store_true') # "ambiguous option" if you specify just "-q"
def parse_until_positional(parser, _sys_argv = None):
if _sys_argv is None:
_sys_argv = sys.argv[1:] # skip the program name
for i in range(0, len(_sys_argv) + 1):
try_argv = _sys_argv[0:i]
try:
parsed_args = parser.parse_args(try_argv)
except CustomParserError as ex:
if len(try_argv) == len(_sys_argv):
# this is our last try and we still couldn't parse anything
parser.original_error(str(ex)) # sys.exit()
continue
# if we are here, we parsed our known optional & dash-prefixed parameters and the COMMAND
cmd_rest_argv = _sys_argv[i:]
break
return (parsed_args, cmd_rest_argv, try_argv)
(parsed_args, cmd_rest_argv, try_argv) = parse_until_positional(parser)
# debug
pprint(try_argv)
pprint(cmd_rest_argv)
pprint(parsed_args)
return (parsed_args, cmd_rest_argv)
def main():
parse_argv()
main()
Another option is to use parse_known_args, which stops parsing when an unknown argument is encountered.
This question already has answers here:
How to read/process command line arguments?
(22 answers)
Closed 8 months ago.
What's the easiest, tersest, and most flexible method or library for parsing Python command line arguments?
argparse is the way to go. Here is a short summary of how to use it:
1) Initialize
import argparse
# Instantiate the parser
parser = argparse.ArgumentParser(description='Optional app description')
2) Add Arguments
# Required positional argument
parser.add_argument('pos_arg', type=int,
help='A required integer positional argument')
# Optional positional argument
parser.add_argument('opt_pos_arg', type=int, nargs='?',
help='An optional integer positional argument')
# Optional argument
parser.add_argument('--opt_arg', type=int,
help='An optional integer argument')
# Switch
parser.add_argument('--switch', action='store_true',
help='A boolean switch')
3) Parse
args = parser.parse_args()
4) Access
print("Argument values:")
print(args.pos_arg)
print(args.opt_pos_arg)
print(args.opt_arg)
print(args.switch)
5) Check Values
if args.pos_arg > 10:
parser.error("pos_arg cannot be larger than 10")
Usage
Correct use:
$ ./app 1 2 --opt_arg 3 --switch
Argument values:
1
2
3
True
Incorrect arguments:
$ ./app foo 2 --opt_arg 3 --switch
usage: convert [-h] [--opt_arg OPT_ARG] [--switch] pos_arg [opt_pos_arg]
app: error: argument pos_arg: invalid int value: 'foo'
$ ./app 11 2 --opt_arg 3
Argument values:
11
2
3
False
usage: app [-h] [--opt_arg OPT_ARG] [--switch] pos_arg [opt_pos_arg]
convert: error: pos_arg cannot be larger than 10
Full help:
$ ./app -h
usage: app [-h] [--opt_arg OPT_ARG] [--switch] pos_arg [opt_pos_arg]
Optional app description
positional arguments:
pos_arg A required integer positional argument
opt_pos_arg An optional integer positional argument
optional arguments:
-h, --help show this help message and exit
--opt_arg OPT_ARG An optional integer argument
--switch A boolean switch
This answer suggests optparse which is appropriate for older Python versions. For Python 2.7 and above, argparse replaces optparse. See this answer for more information.
As other people pointed out, you are better off going with optparse over getopt. getopt is pretty much a one-to-one mapping of the standard getopt(3) C library functions, and not very easy to use.
optparse, while being a bit more verbose, is much better structured and simpler to extend later on.
Here's a typical line to add an option to your parser:
parser.add_option('-q', '--query',
action="store", dest="query",
help="query string", default="spam")
It pretty much speaks for itself; at processing time, it will accept -q or --query as options, store the argument in an attribute called query and has a default value if you don't specify it. It is also self-documenting in that you declare the help argument (which will be used when run with -h/--help) right there with the option.
Usually you parse your arguments with:
options, args = parser.parse_args()
This will, by default, parse the standard arguments passed to the script (sys.argv[1:])
options.query will then be set to the value you passed to the script.
You create a parser simply by doing
parser = optparse.OptionParser()
These are all the basics you need. Here's a complete Python script that shows this:
import optparse
parser = optparse.OptionParser()
parser.add_option('-q', '--query',
action="store", dest="query",
help="query string", default="spam")
options, args = parser.parse_args()
print 'Query string:', options.query
5 lines of python that show you the basics.
Save it in sample.py, and run it once with
python sample.py
and once with
python sample.py --query myquery
Beyond that, you will find that optparse is very easy to extend.
In one of my projects, I created a Command class which allows you to nest subcommands in a command tree easily. It uses optparse heavily to chain commands together. It's not something I can easily explain in a few lines, but feel free to browse around in my repository for the main class, as well as a class that uses it and the option parser
Using docopt
Since 2012 there is a very easy, powerful and really cool module for argument parsing called docopt. Here is an example taken from its documentation:
"""Naval Fate.
Usage:
naval_fate.py ship new <name>...
naval_fate.py ship <name> move <x> <y> [--speed=<kn>]
naval_fate.py ship shoot <x> <y>
naval_fate.py mine (set|remove) <x> <y> [--moored | --drifting]
naval_fate.py (-h | --help)
naval_fate.py --version
Options:
-h --help Show this screen.
--version Show version.
--speed=<kn> Speed in knots [default: 10].
--moored Moored (anchored) mine.
--drifting Drifting mine.
"""
from docopt import docopt
if __name__ == '__main__':
arguments = docopt(__doc__, version='Naval Fate 2.0')
print(arguments)
So this is it: 2 lines of code plus your doc string which is essential and you get your arguments parsed and available in your arguments object.
Using python-fire
Since 2017 there's another cool module called python-fire. It can generate a CLI interface for your code with you doing zero argument parsing. Here's a simple example from the documentation (this small program exposes the function double to the command line):
import fire
class Calculator(object):
def double(self, number):
return 2 * number
if __name__ == '__main__':
fire.Fire(Calculator)
From the command line, you can run:
> calculator.py double 10
20
> calculator.py double --number=15
30
The new hip way is argparse for these reasons. argparse > optparse > getopt
update: As of py2.7 argparse is part of the standard library and optparse is deprecated.
I prefer Click. It abstracts managing options and allows "(...) creating beautiful command line interfaces in a composable way with as little code as necessary".
Here's example usage:
import click
#click.command()
#click.option('--count', default=1, help='Number of greetings.')
#click.option('--name', prompt='Your name',
help='The person to greet.')
def hello(count, name):
"""Simple program that greets NAME for a total of COUNT times."""
for x in range(count):
click.echo('Hello %s!' % name)
if __name__ == '__main__':
hello()
It also automatically generates nicely formatted help pages:
$ python hello.py --help
Usage: hello.py [OPTIONS]
Simple program that greets NAME for a total of COUNT times.
Options:
--count INTEGER Number of greetings.
--name TEXT The person to greet.
--help Show this message and exit.
Pretty much everybody is using getopt
Here is the example code for the doc :
import getopt, sys
def main():
try:
opts, args = getopt.getopt(sys.argv[1:], "ho:v", ["help", "output="])
except getopt.GetoptError:
# print help information and exit:
usage()
sys.exit(2)
output = None
verbose = False
for o, a in opts:
if o == "-v":
verbose = True
if o in ("-h", "--help"):
usage()
sys.exit()
if o in ("-o", "--output"):
output = a
So in a word, here is how it works.
You've got two types of options. Those who are receiving arguments, and those who are
just like switches.
sys.argv is pretty much your char** argv in C. Like in C you skip the first element which is the name of your program and parse only the arguments : sys.argv[1:]
Getopt.getopt will parse it according to the rule you give in argument.
"ho:v" here describes the short arguments : -ONELETTER. The : means that -o accepts one argument.
Finally ["help", "output="] describes long arguments ( --MORETHANONELETTER ).
The = after output once again means that output accepts one arguments.
The result is a list of couple (option,argument)
If an option doesn't accept any argument (like --help here) the arg part is an empty string.
You then usually want to loop on this list and test the option name as in the example.
I hope this helped you.
Use optparse which comes with the standard library. For example:
#!/usr/bin/env python
import optparse
def main():
p = optparse.OptionParser()
p.add_option('--person', '-p', default="world")
options, arguments = p.parse_args()
print 'Hello %s' % options.person
if __name__ == '__main__':
main()
Source: Using Python to create UNIX command line tools
However as of Python 2.7 optparse is deprecated, see: Why use argparse rather than optparse?
Lightweight command line argument defaults
Although argparse is great and is the right answer for fully documented command line switches and advanced features, you can use function argument defaults to handles straightforward positional arguments very simply.
import sys
def get_args(name='default', first='a', second=2):
return first, int(second)
first, second = get_args(*sys.argv)
print first, second
The 'name' argument captures the script name and is not used. Test output looks like this:
> ./test.py
a 2
> ./test.py A
A 2
> ./test.py A 20
A 20
For simple scripts where I just want some default values, I find this quite sufficient. You might also want to include some type coercion in the return values or command line values will all be strings.
Just in case you might need to, this may help if you need to grab unicode arguments on Win32 (2K, XP etc):
from ctypes import *
def wmain(argc, argv):
print argc
for i in argv:
print i
return 0
def startup():
size = c_int()
ptr = windll.shell32.CommandLineToArgvW(windll.kernel32.GetCommandLineW(), byref(size))
ref = c_wchar_p * size.value
raw = ref.from_address(ptr)
args = [arg for arg in raw]
windll.kernel32.LocalFree(ptr)
exit(wmain(len(args), args))
startup()
Argparse code can be longer than actual implementation code!
That's a problem I find with most popular argument parsing options is that if your parameters are only modest, the code to document them becomes disproportionately large to the benefit they provide.
A relative new-comer to the argument parsing scene (I think) is plac.
It makes some acknowledged trade-offs with argparse, but uses inline documentation and wraps simply around main() type function function:
def main(excel_file_path: "Path to input training file.",
excel_sheet_name:"Name of the excel sheet containing training data including columns 'Label' and 'Description'.",
existing_model_path: "Path to an existing model to refine."=None,
batch_size_start: "The smallest size of any minibatch."=10.,
batch_size_stop: "The largest size of any minibatch."=250.,
batch_size_step: "The step for increase in minibatch size."=1.002,
batch_test_steps: "Flag. If True, show minibatch steps."=False):
"Train a Spacy (http://spacy.io/) text classification model with gold document and label data until the model nears convergence (LOSS < 0.5)."
pass # Implementation code goes here!
if __name__ == '__main__':
import plac; plac.call(main)
I prefer optparse to getopt. It's very declarative: you tell it the names of the options and the effects they should have (e.g., setting a boolean field), and it hands you back a dictionary populated according to your specifications.
http://docs.python.org/lib/module-optparse.html
I think the best way for larger projects is optparse, but if you are looking for an easy way, maybe http://werkzeug.pocoo.org/documentation/script is something for you.
from werkzeug import script
# actions go here
def action_foo(name=""):
"""action foo does foo"""
pass
def action_bar(id=0, title="default title"):
"""action bar does bar"""
pass
if __name__ == '__main__':
script.run()
So basically every function action_* is exposed to the command line and a nice
help message is generated for free.
python foo.py
usage: foo.py <action> [<options>]
foo.py --help
actions:
bar:
action bar does bar
--id integer 0
--title string default title
foo:
action foo does foo
--name string
consoleargs deserves to be mentioned here. It is very easy to use. Check it out:
from consoleargs import command
#command
def main(url, name=None):
"""
:param url: Remote URL
:param name: File name
"""
print """Downloading url '%r' into file '%r'""" % (url, name)
if __name__ == '__main__':
main()
Now in console:
% python demo.py --help
Usage: demo.py URL [OPTIONS]
URL: Remote URL
Options:
--name -n File name
% python demo.py http://www.google.com/
Downloading url ''http://www.google.com/'' into file 'None'
% python demo.py http://www.google.com/ --name=index.html
Downloading url ''http://www.google.com/'' into file ''index.html''
Here's a method, not a library, which seems to work for me.
The goals here are to be terse, each argument parsed by a single line, the args line up for readability, the code is simple and doesn't depend on any special modules (only os + sys), warns about missing or unknown arguments gracefully, use a simple for/range() loop, and works across python 2.x and 3.x
Shown are two toggle flags (-d, -v), and two values controlled by arguments (-i xxx and -o xxx).
import os,sys
def HelpAndExit():
print("<<your help output goes here>>")
sys.exit(1)
def Fatal(msg):
sys.stderr.write("%s: %s\n" % (os.path.basename(sys.argv[0]), msg))
sys.exit(1)
def NextArg(i):
'''Return the next command line argument (if there is one)'''
if ((i+1) >= len(sys.argv)):
Fatal("'%s' expected an argument" % sys.argv[i])
return(1, sys.argv[i+1])
### MAIN
if __name__=='__main__':
verbose = 0
debug = 0
infile = "infile"
outfile = "outfile"
# Parse command line
skip = 0
for i in range(1, len(sys.argv)):
if not skip:
if sys.argv[i][:2] == "-d": debug ^= 1
elif sys.argv[i][:2] == "-v": verbose ^= 1
elif sys.argv[i][:2] == "-i": (skip,infile) = NextArg(i)
elif sys.argv[i][:2] == "-o": (skip,outfile) = NextArg(i)
elif sys.argv[i][:2] == "-h": HelpAndExit()
elif sys.argv[i][:1] == "-": Fatal("'%s' unknown argument" % sys.argv[i])
else: Fatal("'%s' unexpected" % sys.argv[i])
else: skip = 0
print("%d,%d,%s,%s" % (debug,verbose,infile,outfile))
The goal of NextArg() is to return the next argument while checking for missing data, and 'skip' skips the loop when NextArg() is used, keeping the flag parsing down to one liners.
I extended Erco's approach to allow for required positional arguments and for optional arguments. These should precede the -d, -v etc. arguments.
Positional and optional arguments can be retrieved with PosArg(i) and OptArg(i, default) respectively.
When an optional argument is found the start position of searching for options (e.g. -i) is moved 1 ahead to avoid causing an 'unexpected' fatal.
import os,sys
def HelpAndExit():
print("<<your help output goes here>>")
sys.exit(1)
def Fatal(msg):
sys.stderr.write("%s: %s\n" % (os.path.basename(sys.argv[0]), msg))
sys.exit(1)
def NextArg(i):
'''Return the next command line argument (if there is one)'''
if ((i+1) >= len(sys.argv)):
Fatal("'%s' expected an argument" % sys.argv[i])
return(1, sys.argv[i+1])
def PosArg(i):
'''Return positional argument'''
if i >= len(sys.argv):
Fatal("'%s' expected an argument" % sys.argv[i])
return sys.argv[i]
def OptArg(i, default):
'''Return optional argument (if there is one)'''
if i >= len(sys.argv):
Fatal("'%s' expected an argument" % sys.argv[i])
if sys.argv[i][:1] != '-':
return True, sys.argv[i]
else:
return False, default
### MAIN
if __name__=='__main__':
verbose = 0
debug = 0
infile = "infile"
outfile = "outfile"
options_start = 3
# --- Parse two positional parameters ---
n1 = int(PosArg(1))
n2 = int(PosArg(2))
# --- Parse an optional parameters ---
present, a3 = OptArg(3,50)
n3 = int(a3)
options_start += int(present)
# --- Parse rest of command line ---
skip = 0
for i in range(options_start, len(sys.argv)):
if not skip:
if sys.argv[i][:2] == "-d": debug ^= 1
elif sys.argv[i][:2] == "-v": verbose ^= 1
elif sys.argv[i][:2] == "-i": (skip,infile) = NextArg(i)
elif sys.argv[i][:2] == "-o": (skip,outfile) = NextArg(i)
elif sys.argv[i][:2] == "-h": HelpAndExit()
elif sys.argv[i][:1] == "-": Fatal("'%s' unknown argument" % sys.argv[i])
else: Fatal("'%s' unexpected" % sys.argv[i])
else: skip = 0
print("Number 1 = %d" % n1)
print("Number 2 = %d" % n2)
print("Number 3 = %d" % n3)
print("Debug = %d" % debug)
print("verbose = %d" % verbose)
print("infile = %s" % infile)
print("outfile = %s" % outfile)