在Python中,我们如何找到为脚本提供的命令行参数,并处理它们?


有关更具体的示例,请参见实现“[命令][动作][参数]”风格的命令行界面?以及如何使用Python的optparse格式化位置参数帮助?


标准库中的规范解决方案是argparse (docs):

这里有一个例子:

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支持(除其他外):

任意顺序的多个选项。 选择做空和做多。 默认值。 生成使用帮助消息。


import sys

print("\n".join(sys.argv))

sys。Argv是一个列表,它包含在命令行上传递给脚本的所有参数。sys。Argv[0]为脚本名称。

基本上,

import sys
print(sys.argv[1:])

一种方法是使用sys.argv。这将打印脚本名称作为第一个参数以及传递给它的所有其他参数。

import sys

for arg in sys.argv:
    print arg

我喜欢从stdlib中获取getopt,例如:

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...")

最近,我一直在包装类似的东西,使事情更少的啰嗦(例如;使“-h”隐式)。


还有argparse stdlib模块(对stdlib的optparse模块的“改进”)。示例来自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))

用法:

$ script.py 1 2 3 4
4

$ script.py --sum 1 2 3 4
10

我自己也使用optparse,但是非常喜欢Simon Willison最近引入的optfunc库。它的工作原理是:

“自省一个函数 定义(包括其参数) 以及它们的默认值)和使用 构造一个命令行 参数解析器。”

例如,这个函数定义:

def geocode(s, api_key='', geocoder='google', list_geocoders=False):

被转换为以下optparse帮助文本:

    Options:
      -h, --help            show this help message and exit
      -l, --list-geocoders
      -a API_KEY, --api-key=API_KEY
      -g GEOCODER, --geocoder=GEOCODER

只是四处宣传argparse,因为这些原因它更好。从本质上讲:

(从链接复制)

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

我个人最喜欢的是:

Argparse允许类型和 add_argument()的操作参数 用simple指定 调用对象,而optparse需要 破解类属性,比如 获取STORE_ACTIONS或CHECK_METHODS 正确的参数检查


你可能会对我写的一个小Python模块感兴趣,它使命令行参数的处理更容易(开源且免费使用)- Commando


正如你所看到的,optparse模块已被弃用,不会再进一步开发;argparse模块将继续开发。”


我的解是入口点2。例子:

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

帮助文本:

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

#set default args as -h , if no args:
if len(sys.argv) == 1: sys.argv[1:] = ["-h"]

docopt库非常漂亮。它为你的应用程序从usage字符串中构建一个参数dict。

来自docopt自述文件:

"""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)

我建议使用docopt作为其他选项的简单替代。

Docopt是一个新的项目,它通过解析您的——help使用消息来工作,而不是要求您自己实现所有内容。您只需将您的使用消息放入POSIX格式。


Pocoo的点击更直观,需要的样板文件更少,而且至少和argparse一样强大。

到目前为止,我遇到的唯一缺点是您不能对帮助页面进行很多自定义,但这通常不是必需的,而docopt似乎是明确的选择。


如果你需要快速而不灵活的东西

main.py:

import sys

first_name = sys.argv[1]
last_name = sys.argv[2]
print("Hello " + first_name + " " + last_name)

然后运行python main.py James Smith

产生以下输出:

你好,詹姆斯·史密斯


还有一个选项是argh。它建立在argparse的基础上,让你可以写这样的东西:

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()

它将自动生成帮助等等,您可以使用装饰器来提供关于参数解析应该如何工作的额外指导。


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

同样在python3中,你可能会发现使用Extended Iterable Unpacking来处理可选的位置参数很方便,没有额外的依赖关系:

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)

上面的argv解包使arg2和arg3成为“可选的”——如果在argv中没有指定它们,它们将是None,而如果没有指定第一个,ValueError将被解析:

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)

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)

让我们把这个文件命名为command_line.py,然后运行它:

C:\Users\simone> python command_line.py arg1 arg2 arg3 ecc
File name: command_line.py
Arguments:
arg1
arg2
arg3
ecc

现在让我们写一个简单的程序sum.py:

import sys

try:
    print(sum(map(float, sys.argv[1:])))
except:
    print("An error has occurred")

结果:

C:\Users\simone> python sum.py 10 4 6 3
23

我们的一些生物技术客户最近提出了这两个问题:

如何将Python脚本作为命令执行? 当Python脚本作为命令执行时,我们如何将输入值传递给它?

我在下面附上了一个Python脚本,我相信它可以回答这两个问题。让我们假设下面的Python脚本保存在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)

该脚本可以在macOS计算机上的Terminal shell中执行,如下所示,结果将被打印到标准输出(确保当前目录包括test.py文件):

$ 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

该脚本也可以在Windows计算机上的命令提示符中执行,如下所示,结果将被打印到标准输出(确保当前目录包括test.py文件):

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

这个脚本回答了上面提出的两个问题,并且是开发将作为带有输入值的命令执行的脚本的一个很好的起点。


它处理简单的开关,带有可选替代标志的值开关。

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")

新答案的原因:

现有的答案指定了多个选项。 标准选项是使用argparse,一些答案从文档中提供了示例,其中一个答案提出了它的优点。但是所有OP都不能充分/清楚地解释实际问题的答案,至少对新手来说是这样。

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)

上面的程序需要一个配置文件作为参数。如果您提供了它,它将愉快地执行。如果没有,它将打印以下内容

usage: test.py [-h] conf_file
test.py: error: the following arguments are required: conf_file

可以指定参数是否为可选参数。 可以使用type key为参数指定预期的类型 解析器。Add_argument("年龄",type=int, help="人的年龄") 可以通过指定default key来指定参数的默认值

本文档将在一定程度上帮助您理解它。