PYTHON 带有可选和强制参数的命令的所有可能性

PYTHON all possibilities of command with optional and mandatory parameters

我正在与一些在 python 中列出带有可选和强制参数的命令的所有可能性进行斗争。我需要它根据某些脚本的帮助输出在 bash 中生成一些自动完成脚本。

例如虚构命令:

add disk -pool <name> { -diskid <diskid> | -diskid auto [-fx | -tdr] } [-fx] [-status { enable | disable } ]

其中:{} 必填,[] 可选,|或

上述命令的所有 (24) 种可能性的预期结果:

add disk -pool <name> -diskid <diskid>
add disk -pool <name> -diskid <diskid> -capacity_saving
add disk -pool <name> -diskid <diskid> -capacity_saving -status enable
add disk -pool <name> -diskid <diskid> -capacity_saving -status disable
add disk -pool <name> -diskid <diskid> -status enable
add disk -pool <name> -diskid <diskid> -status disable
add disk -pool <name> -diskid auto
add disk -pool <name> -diskid auto -capacity_saving
add disk -pool <name> -diskid auto -capacity_saving -status enable
add disk -pool <name> -diskid auto -capacity_saving -status disable
add disk -pool <name> -diskid auto -status enable
add disk -pool <name> -diskid auto -status disable
add disk -pool <name> -diskid auto -fx 
add disk -pool <name> -diskid auto -fx -capacity_saving
add disk -pool <name> -diskid auto -fx -capacity_saving -status enable
add disk -pool <name> -diskid auto -fx -capacity_saving -status disable
add disk -pool <name> -diskid auto -fx -status enable
add disk -pool <name> -diskid auto -fx -status disable
add disk -pool <name> -diskid auto -tdr 
add disk -pool <name> -diskid auto -tdr -capacity_saving
add disk -pool <name> -diskid auto -tdr -capacity_saving -status enable
add disk -pool <name> -diskid auto -tdr -capacity_saving -status disable
add disk -pool <name> -diskid auto -tdr -status enable
add disk -pool <name> -diskid auto -tdr -status disable

我试过 import intertool + product() 但它只适用于不太复杂的命令,如 { -diskid <diskid> | -diskid auto } 所以如果括号中没有更多的括号,如下面的命令输出:

# add disk -pool <name> { -diskid <diskid> | -diskid auto } [-fx]

command = [ ['add'], ['disk'], ['-pool <name>'], ['-diskid <diskid>', '-diskid auto'], ['', '-fx']]   
print(list(itertools.product(*command)))
print(len(list(itertools.product(*command))))

输出:

[('add', 'disk', '-pool <name>', '-diskid <diskid>', ''), 
('add', 'disk', '-pool <name>', '-diskid <diskid>', '-fx'), 
('add', 'disk', '-pool <name>', '-diskid auto', ''), 
('add', 'disk', '-pool <name>', '-diskid auto', '-fx')]
4

怎样才能得到预期的结果? :c

您为虚构命令提供的语法与预期输出不匹配(例如 -capacity_saving)。

尽管如此 :

import itertools


# OP syntax :
# add disk -pool <name> { -diskid <diskid> | -diskid auto [-fx | -tdr] } [-fx] [-status { enable | disable } ]
command = (
    # one mandatory diskid
    ("-diskid <diskid>", "-diskid auto", "-diskid auto -fx", "-diskid auto -tdr"),
    # one optional fx
    (None, "-fx"),
    # one optional status
    (None, "-status enable", "-status disable")
)

prefix = "add disk -pool <name> "

all_usages = tuple(itertools.product(*command))
print("\n".join(str(prefix + tuple(filter(lambda arg: arg is not None, usage))) for usage in all_usages))
print("total:", len(all_usages))

给我

('add', 'disk', '-pool', '<name>', '-diskid <diskid>')
('add', 'disk', '-pool', '<name>', '-diskid <diskid>', '-status enable')
('add', 'disk', '-pool', '<name>', '-diskid <diskid>', '-status disable')
('add', 'disk', '-pool', '<name>', '-diskid <diskid>', '-fx')
('add', 'disk', '-pool', '<name>', '-diskid <diskid>', '-fx', '-status enable')
('add', 'disk', '-pool', '<name>', '-diskid <diskid>', '-fx', '-status disable')
('add', 'disk', '-pool', '<name>', '-diskid auto')
('add', 'disk', '-pool', '<name>', '-diskid auto', '-status enable')
('add', 'disk', '-pool', '<name>', '-diskid auto', '-status disable')
('add', 'disk', '-pool', '<name>', '-diskid auto', '-fx')
('add', 'disk', '-pool', '<name>', '-diskid auto', '-fx', '-status enable')
('add', 'disk', '-pool', '<name>', '-diskid auto', '-fx', '-status disable')
('add', 'disk', '-pool', '<name>', '-diskid auto -fx')
('add', 'disk', '-pool', '<name>', '-diskid auto -fx', '-status enable')
('add', 'disk', '-pool', '<name>', '-diskid auto -fx', '-status disable')
('add', 'disk', '-pool', '<name>', '-diskid auto -fx', '-fx')
('add', 'disk', '-pool', '<name>', '-diskid auto -fx', '-fx', '-status enable')
('add', 'disk', '-pool', '<name>', '-diskid auto -fx', '-fx', '-status disable')
('add', 'disk', '-pool', '<name>', '-diskid auto -tdr')
('add', 'disk', '-pool', '<name>', '-diskid auto -tdr', '-status enable')
('add', 'disk', '-pool', '<name>', '-diskid auto -tdr', '-status disable')
('add', 'disk', '-pool', '<name>', '-diskid auto -tdr', '-fx')
('add', 'disk', '-pool', '<name>', '-diskid auto -tdr', '-fx', '-status enable')
('add', 'disk', '-pool', '<name>', '-diskid auto -tdr', '-fx', '-status disable')
total: 24

下面的解决方案采用了更通用的方法。首先,parse_commands 将字符串命令转换为列表和字典的嵌套对象,然后 command_combosparse_commands:

生成的对象中生成所有可能的命令组合
import re, collections itertools
s = 'add disk -pool <name> { -diskid <diskid> | -diskid auto [-fx | -tdr] } [-fx] [-status { enable | disable } ]'
symbols = {'|':None, '{':'}', '[':']'}
def parse_command(s, f = False, t = None):
   #take in a string command and parse it
   b = []
   while s:
      n = s.popleft()
      if f:
         if t is None and n in [*symbols, *symbols.values()]:
            s.appendleft(n)
            break
         if t is not None and n == symbols[t]:
            break
      if n not in [*symbols, *symbols.values()]:
         b.append(n)
      elif n == '|':
         b = [{'action':'or', 'blocks':(b, list(parse_command(s, f = True, t = None)))}]
      else:
         b.append({'action':{'{':'mandatory', '[':'optional'}[n], 'blocks':list(parse_command(s, f = True, t = n))})
   yield from b

def command_combos(d, c = []):
   #recursively traverse parsed command and produce combinations
   if not d:
      yield c
   else:
      if isinstance(d[0], str):
         yield from command_combos(d[1:], c+[d[0]])
      else:
         if d[0]['action'] == 'optional':
            yield from command_combos(d[1:], c)
         for b in (d[0]['blocks'] if d[0]['action'] == 'or' else [d[0]['blocks']]):
            for i in command_combos(b, []):
                yield from command_combos(d[1:], c+i)

综合起来:

new_s = list(parse_command(collections.deque(re.findall('[\{\[\}\]\|]|\-*\<*\w+\>*', s))))
combos = list(command_combos(new_s))  

输出:

[['add', 'disk', '-pool', '<name>', '-diskid', '<diskid>'], ['add', 'disk', '-pool', '<name>', '-diskid', '<diskid>', '-status', 'enable'], ['add', 'disk', '-pool', '<name>', '-diskid', '<diskid>', '-status', 'disable'], ['add', 'disk', '-pool', '<name>', '-diskid', '<diskid>', '-fx'], ['add', 'disk', '-pool', '<name>', '-diskid', '<diskid>', '-fx', '-status', 'enable'], ['add', 'disk', '-pool', '<name>', '-diskid', '<diskid>', '-fx', '-status', 'disable'], ['add', 'disk', '-pool', '<name>', '-diskid', '<diskid>', '-fx'], ['add', 'disk', '-pool', '<name>', '-diskid', '<diskid>', '-fx', '-status', 'enable'], ['add', 'disk', '-pool', '<name>', '-diskid', '<diskid>', '-fx', '-status', 'disable'], ['add', 'disk', '-pool', '<name>', '-diskid', '<diskid>', '-fx', '-fx'], ['add', 'disk', '-pool', '<name>', '-diskid', '<diskid>', '-fx', '-fx', '-status', 'enable'], ['add', 'disk', '-pool', '<name>', '-diskid', '<diskid>', '-fx', '-fx', '-status', 'disable'], ['add', 'disk', '-pool', '<name>', '-diskid', '<diskid>', '-tdr'], ['add', 'disk', '-pool', '<name>', '-diskid', '<diskid>', '-tdr', '-status', 'enable'], ['add', 'disk', '-pool', '<name>', '-diskid', '<diskid>', '-tdr', '-status', 'disable'], ['add', 'disk', '-pool', '<name>', '-diskid', '<diskid>', '-tdr', '-fx'], ['add', 'disk', '-pool', '<name>', '-diskid', '<diskid>', '-tdr', '-fx', '-status', 'enable'], ['add', 'disk', '-pool', '<name>', '-diskid', '<diskid>', '-tdr', '-fx', '-status', 'disable'], ['add', 'disk', '-pool', '<name>', '-diskid', 'auto'], ['add', 'disk', '-pool', '<name>', '-diskid', 'auto', '-status', 'enable'], ['add', 'disk', '-pool', '<name>', '-diskid', 'auto', '-status', 'disable'], ['add', 'disk', '-pool', '<name>', '-diskid', 'auto', '-fx'], ['add', 'disk', '-pool', '<name>', '-diskid', 'auto', '-fx', '-status', 'enable'], ['add', 'disk', '-pool', '<name>', '-diskid', 'auto', '-fx', '-status', 'disable'], ['add', 'disk', '-pool', '<name>', '-diskid', 'auto', '-fx'], ['add', 'disk', '-pool', '<name>', '-diskid', 'auto', '-fx', '-status', 'enable'], ['add', 'disk', '-pool', '<name>', '-diskid', 'auto', '-fx', '-status', 'disable'], ['add', 'disk', '-pool', '<name>', '-diskid', 'auto', '-fx', '-fx'], ['add', 'disk', '-pool', '<name>', '-diskid', 'auto', '-fx', '-fx', '-status', 'enable'], ['add', 'disk', '-pool', '<name>', '-diskid', 'auto', '-fx', '-fx', '-status', 'disable'], ['add', 'disk', '-pool', '<name>', '-diskid', 'auto', '-tdr'], ['add', 'disk', '-pool', '<name>', '-diskid', 'auto', '-tdr', '-status', 'enable'], ['add', 'disk', '-pool', '<name>', '-diskid', 'auto', '-tdr', '-status', 'disable'], ['add', 'disk', '-pool', '<name>', '-diskid', 'auto', '-tdr', '-fx'], ['add', 'disk', '-pool', '<name>', '-diskid', 'auto', '-tdr', '-fx', '-status', 'enable'], ['add', 'disk', '-pool', '<name>', '-diskid', 'auto', '-tdr', '-fx', '-status', 'disable']]