如何使用 python argparse 解决命令行问题?

Posted

技术标签:

【中文标题】如何使用 python argparse 解决命令行问题?【英文标题】:How to solve command line problem using python argparse? 【发布时间】:2021-03-27 10:30:48 【问题描述】:

我正在处理一个命令行项目,在处理 add 子命令时遇到了一些问题,如下面的 cmets 所示:

import argparse
import sys
def todo(args):
    if args.o =='add':
        print("Added Todo: "+args.x)
        f=open("todo.txt", "a+")
        c= str(count+1)
        p= '\n'+'. '+args.x
        f.write(p)
        f.close()
    elif args.o =='report':
        return 
    elif args.o =='del NUMBER':
        return 
    elif args.o =='done NUMBER':
        return 
    elif args.o =='help':
        print ("Usage :-"+
               "\n"+'$ ./todo add "todo item"           # add a new todo'+
               "\n"+'$ ./todo ls                        # Show remaining todos'+
               "\n"+'$ ./todo del NUMBER                # delete a todo'+
               "\n"+'$ ./todo done NUMBER               # complete a todo'+
               "\n"+'$ ./todo help                      # Show Usage'+
               "\n"+'$ ./todo report                    # Statistics')
    elif args.o =='ls':
        f=open("todo.txt", "r")
        print(f.read())
        f.close()
if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    parser.add_argument('./todo', type=str, default="./todo")
    parser.add_argument('o', type=str, default="add")
    args = parser.parse_args()
    if args.o =='add':                     # <- starting here
        parser.add_argument('x', type=str, default=None)
        args = parser.parse_args()
        sys.stdout.write(str(todo(args)))  # <- ending here
    else:
        a = parser.parse_args()
        sys.stdout.write(str(todo(args)))

当我在 Powershell 中运行此脚本时,我收到一些错误,例如

PS E:\python projects\fellowship challenge\python> python todo.py ./todo add " I am soham Das Biswas"
usage: todo.py [-h] ./todo o
todo.py: error: unrecognized arguments:  I am soham Das Biswas

我该如何解决这个问题?

【问题讨论】:

为什么要指定todo两次?如果文件被称为todo 而不是todo.py,则./todo 将是命令名称,并且它被放置在当前目录中并标记为可执行文件; python todo.py 是使用 python 解释器显式运行文件 todo.py 中的脚本的等效命令。 【参考方案1】:

您可能误解了如何定义参数。您可能假设您可以在示例中使用参数作为函数调用中的位置参数,但这不是参数解析器的工作方式。这是您的示例中发生的情况

python todo.py ./todo add " I am soham Das Biswas"

您定义了两个参数:“./todo”和“o”。 ./todo add 部分将add 的值分配给./todo 参数。

“I am soham Das Biswas”不会分配给“o”,因为您在通话中没有提到“o”。如果你想给“o”赋值,你需要像o value_i_want_to_assign这样的东西。

你想让你的代码做什么?

【讨论】:

它给我 x 是强制性的,我不想给它强制性 所以当我询问 x 位置时,它会给出位置,而当我不想要它时,它不会给出强制性的【参考方案2】:

import argparse

parser = argparse.ArgumentParser()
parser.add_argument('./todo', type=str, default="./todo")
parser.add_argument('o', type=str, default="add")
args = parser.parse_args()
print(args)

一些示例运行:

1301:~/mypy$ python3 stack65328753.py -h
usage: stack65328753.py [-h] ./todo o

positional arguments:
  ./todo
  o

optional arguments:
  -h, --help  show this help message and exit

有 2 个字符串:

1303:~/mypy$ python3 stack65328753.py todo add
Namespace(o='add', **'./todo': 'todo')

有一个额外的:

1304:~/mypy$ python3 stack65328753.py todo add "extra string"
usage: stack65328753.py [-h] ./todo o
stack65328753.py: error: unrecognized arguments: extra string

这定义了 2 个参数,都是 positionals。它们由位置标识,而不是任何类型的“标志”字符串。第一个字符串分配给args 的“./todo”属性,第二个字符串分配给“o”。没有什么可以拿第三个字符串的。

您可以使用args.o 访问第二个值,但args../todo 不起作用。相反,您必须getattr(args, "./todo")。因此,使用这样的“花哨”名称通常不是一个好主意。

由于这些是必需的位置,因此没有必要指定 default

将参数更改为optionals

parser = argparse.ArgumentParser()
parser.add_argument("--dir", default="./todo")
parser.add_argument('-o', type=str, default="add")
args = parser.parse_args()
print(args)
print(args.dir, args.o)

然后运行:

1315:~/mypy$ python3 stack65328753.py -h
usage: stack65328753.py [-h] [--dir DIR] [-o O]

optional arguments:
  -h, --help  show this help message and exit
  --dir DIR
  -o O

1316:~/mypy$ python3 stack65328753.py
Namespace(dir='./todo', o='add')
./todo add

1316:~/mypy$ python3 stack65328753.py --dir ./mydir 
Namespace(dir='./mydir', o='add')
./mydir add

1316:~/mypy$ python3 stack65328753.py --dir ./mydir -o subtract
Namespace(dir='./mydir', o='subtract')
./mydir subtract

1316:~/mypy$ python3 stack65328753.py --dir ./mydir -o "an extra string"
Namespace(dir='./mydir', o='an extra string')
./mydir an extra string

您尝试根据args.o 值添加“x”参数

args = parser.parse_args()
if args.o =='add':                     # <- starting here
    parser.add_argument('x', type=str, default=None)
    args = parser.parse_args()

但是第一个parse_args() 是引发无法识别的错误并退出的那个。所以你永远不会继续这个添加。

parser = argparse.ArgumentParser()
parser.add_argument("--dir", default="./todo")
parser.add_argument('-o', type=str, default="add")
args, extras = parser.parse_known_args()
print(args, extras)
print(args.dir, args.o)

if args.o == "add":
    parser.add_argument('x')
    args = parser.parse_args()
    print(args)

帮助没有改变,因为它是第一个起作用的parse

1318:~/mypy$ python3 stack65328753.py -h
usage: stack65328753.py [-h] [--dir DIR] [-o O]

optional arguments:
  -h, --help  show this help message and exit
  --dir DIR
  -o O


1323:~/mypy$ python3 stack65328753.py --dir ./mydir -o "an extra string"
Namespace(dir='./mydir', o='an extra string') []
./mydir an extra string

parse_known_args 将多余的字符串放入extras。现在继续添加x 参数:

1323:~/mypy$ python3 stack65328753.py --dir ./mydir -o add "an extra string"
Namespace(dir='./mydir', o='add') ['an extra string']
./mydir add
Namespace(dir='./mydir', o='add', x='an extra string')

另一种选择是

args.x = extras

这将是(可能是空的)列表。

对于这样的问题,我强烈建议使用print(args) 来查看解析器的作用。并在将解析器嵌入更大的脚本之前对其进行调试。首先不要试图太花哨。将optionals 用于可选、非必需的内容,将positionals 用于必需的内容。有一些方法可以改变这种情况,但它会让你和你的用户更难理解输入。

【讨论】:

以上是关于如何使用 python argparse 解决命令行问题?的主要内容,如果未能解决你的问题,请参考以下文章

python argh/argparse:如何将列表作为命令行参数传递?

python使用argparse解析命令行参数

Python argparse 如何从命令行传递 False?

python 解析命令行选项

如何使用 Python 中的 argparse 和 csv 库编写文件?

Python Argparse Moudle