如何读取/处理命令行参数?

我原本是一名C程序员。我见过许多技巧和“黑客”来阅读许多不同的论点。

Python程序员可以通过哪些方式做到这一点?

相关

745879 次浏览

标准库中的标准解是argparse文档):

下面是一个例子:

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获取,例如:

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这是更好的这些原因…本质上:

(从链接复制)

  • artparse模块可以处理位置 和可选参数,而 optparse只能处理可选的 参数

  • Argparse不是教条的 你的命令行界面 应该看起来像-选项像-file 或者 /file得到支持 必需的选项。Optparse拒绝 支持这些功能,更喜欢 纯度高于实用性

  • Argparse产生更多 信息使用消息,包括 命令行用法从 您的论点和帮助消息 位置和可选 参数。optparse模块 要求您编写自己的用法 字符串,并且无法显示 位置参数的帮助。

  • artparse支持以下操作 消耗可变数量的 命令行参数,而optparse 要求的确切数量 已知参数(例如1、2或3) 提前

  • artparse支持以下解析器 分派到子命令,而 optparse需要设置 allow_interspersed_args和做 手动解析器调度

我个人最喜欢的:

  • Argparse允许类型和 操作参数add_argument() 用简单的 可调用对象,而optparse需要 黑客类属性,如 STORE_ACTIONSCHECK_METHODS获取 正确的参数检查

您可能对我编写的一个小Python模块感兴趣,该模块可以更轻松地处理命令行参数(开源且免费使用)-突击队

如您所见optparse“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库非常巧妙。它从您的应用程序的使用字符串构建参数字典。

来自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视为这些其他人的简单替代品。

docop是一个新项目,它通过解析您的--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()

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

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中,您可能会发现使用扩展可迭代解压缩来处理可选的位置参数而无需额外的依赖项很方便:

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解包使arg2arg3成为“可选”-如果在argv中未指定它们,它们将为无,而如果未指定第一个,则会考虑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)

该脚本可以在终端shell中的macOS计算机上执行,如下所示,结果将打印到标准输出(确保当前目录包含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")

新答案的原因:

  1. 现有答案指定了多个选项。
  2. 标准选项是使用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键指定参数的预期类型

    parser.add_argument("age", type=int, help="age of the person")

  • 您可以通过指定default键为参数指定默认值

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