98

次のようなインターフェースを持つコマンドラインプログラムを実装しています:

cmd [GLOBAL_OPTIONS] {command [COMMAND_OPTS]} [{command [COMMAND_OPTS]} ...]

argparse のドキュメントを読みました。inGLOBAL_OPTIONSを使用してオプションの引数として実装できます。そしてサブコマンドの使用。add_argumentargparse{command [COMMAND_OPTS]}

ドキュメントから、サブコマンドは 1 つしか持てないようです。しかし、ご覧のとおり、1 つ以上のサブコマンドを実装する必要があります。を使用してそのようなコマンドライン引数を解析する最良の方法は何argparseですか?

4

12 に答える 12

37

私は同じ質問を思いつきましたが、より良い答えが得られたようです。

解決策は、サブパーサーを別のサブパーサーと単純にネストするのではなく、別のサブパーサーに続くパーサーに続いてサブパーサーを追加できることです。

コードはその方法を教えてくれます:

parent_parser = argparse.ArgumentParser(add_help=False)
parent_parser.add_argument('--user', '-u',
                    default=getpass.getuser(),
                    help='username')
parent_parser.add_argument('--debug', default=False, required=False,
                        action='store_true', dest="debug", help='debug flag')
main_parser = argparse.ArgumentParser()
service_subparsers = main_parser.add_subparsers(title="service",
                    dest="service_command")
service_parser = service_subparsers.add_parser("first", help="first",
                    parents=[parent_parser])
action_subparser = service_parser.add_subparsers(title="action",
                    dest="action_command")
action_parser = action_subparser.add_parser("second", help="second",
                    parents=[parent_parser])

args = main_parser.parse_args()
于 2013-10-20T10:15:50.590 に答える
27

@mgilsonは、この質問に対する良い答えを持っています。しかし、sys.argvを自分で分割する場合の問題は、Argparseがユーザーに対して生成するすばらしいヘルプメッセージをすべて失うことです。だから私はこれをすることになった:

import argparse

## This function takes the 'extra' attribute from global namespace and re-parses it to create separate namespaces for all other chained commands.
def parse_extra (parser, namespace):
  namespaces = []
  extra = namespace.extra
  while extra:
    n = parser.parse_args(extra)
    extra = n.extra
    namespaces.append(n)

  return namespaces

argparser=argparse.ArgumentParser()
subparsers = argparser.add_subparsers(help='sub-command help', dest='subparser_name')

parser_a = subparsers.add_parser('command_a', help = "command_a help")
## Setup options for parser_a

## Add nargs="*" for zero or more other commands
argparser.add_argument('extra', nargs = "*", help = 'Other commands')

## Do similar stuff for other sub-parsers

これで、最初の解析後、チェーンされたすべてのコマンドがに格納されextraます。空でないときに再解析して、チェーンされたすべてのコマンドを取得し、それらに個別の名前空間を作成します。そして、argparseが生成するより良い使用法文字列を取得します。

于 2012-05-14T08:36:57.237 に答える
15

parse_known_args名前空間と不明な文字列のリストを返します。extraこれは、チェックされた回答に似ています。

import argparse
parser = argparse.ArgumentParser()
parser.add_argument('--foo')
sub = parser.add_subparsers()
for i in range(1,4):
    sp = sub.add_parser('cmd%i'%i)
    sp.add_argument('--foo%i'%i) # optionals have to be distinct

rest = '--foo 0 cmd2 --foo2 2 cmd3 --foo3 3 cmd1 --foo1 1'.split() # or sys.argv
args = argparse.Namespace()
while rest:
    args,rest =  parser.parse_known_args(rest,namespace=args)
    print args, rest

生成:

Namespace(foo='0', foo2='2') ['cmd3', '--foo3', '3', 'cmd1', '--foo1', '1']
Namespace(foo='0', foo2='2', foo3='3') ['cmd1', '--foo1', '1']
Namespace(foo='0', foo1='1', foo2='2', foo3='3') []

別のループでは、各サブパーサーに独自の名前空間が与えられます。これにより、位置指定名の重複が許可されます。

argslist = []
while rest:
    args,rest =  parser.parse_known_args(rest)
    argslist.append(args)
于 2013-10-20T18:19:53.273 に答える
5

いつでもコマンドラインを自分で分割 (sys.argvコマンド名で分割) し、特定のコマンドに対応する部分のみを に渡すことができます。必要に応じて、namespace キーワードを parse_args使用して同じものを使用することもできます。Namespace

コマンドラインをグループ化するのは簡単itertools.groupbyです:

import sys
import itertools
import argparse    

mycommands=['cmd1','cmd2','cmd3']

def groupargs(arg,currentarg=[None]):
    if(arg in mycommands):currentarg[0]=arg
    return currentarg[0]

commandlines=[list(args) for cmd,args in intertools.groupby(sys.argv,groupargs)]

#setup parser here...
parser=argparse.ArgumentParser()
#...

namespace=argparse.Namespace()
for cmdline in commandlines:
    parser.parse_args(cmdline,namespace=namespace)

#Now do something with namespace...

テストされていない

于 2012-05-04T13:00:16.373 に答える
5

@mgilson による回答を改善して、argv を部分に分割し、コマンドの引数の値を名前空間の階層に入れる小さな解析メソッドを作成しました。

import sys
import argparse


def parse_args(parser, commands):
    # Divide argv by commands
    split_argv = [[]]
    for c in sys.argv[1:]:
        if c in commands.choices:
            split_argv.append([c])
        else:
            split_argv[-1].append(c)
    # Initialize namespace
    args = argparse.Namespace()
    for c in commands.choices:
        setattr(args, c, None)
    # Parse each command
    parser.parse_args(split_argv[0], namespace=args)  # Without command
    for argv in split_argv[1:]:  # Commands
        n = argparse.Namespace()
        setattr(args, argv[0], n)
        parser.parse_args(argv, namespace=n)
    return args


parser = argparse.ArgumentParser()
commands = parser.add_subparsers(title='sub-commands')

cmd1_parser = commands.add_parser('cmd1')
cmd1_parser.add_argument('--foo')

cmd2_parser = commands.add_parser('cmd2')
cmd2_parser.add_argument('--foo')

cmd2_parser = commands.add_parser('cmd3')
cmd2_parser.add_argument('--foo')


args = parse_args(parser, commands)
print(args)

それは適切に動作し、素敵な argparse ヘルプを提供します:

の場合./test.py --help:

usage: test.py [-h] {cmd1,cmd2,cmd3} ...

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

sub-commands:
  {cmd1,cmd2,cmd3}

の場合./test.py cmd1 --help:

usage: test.py cmd1 [-h] [--foo FOO]

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

そして、引数値を含む名前空間の階層を作成します。

./test.py cmd1 --foo 3 cmd3 --foo 4
Namespace(cmd1=Namespace(foo='3'), cmd2=None, cmd3=Namespace(foo='4'))
于 2017-05-11T23:57:36.373 に答える
0

サブコマンドを解析するために、以下を使用しました (argparse.py コードから参照)。サブパーサー引数を解析し、両方のヘルプを保持します。そこには何も渡されませんでした。

args, _ = parser.parse_known_args()
于 2022-01-10T11:42:20.350 に答える
-4

パッケージoptparseを使用できます

import optparse
parser = optparse.OptionParser()
parser.add_option("-f", dest="filename", help="corpus filename")
parser.add_option("--alpha", dest="alpha", type="float", help="parameter alpha", default=0.5)
(options, args) = parser.parse_args()
fname = options.filename
alpha = options.alpha
于 2012-05-04T13:23:09.743 に答える