2012-11-01 14 views
0

如何解析由特殊預定義語法分隔的變長參數列表。舉個例子:如何將argparse參數解析爲字符串列表,在預定義的標誌參數上停止?

./script --arg1 --cmdname otherscript --a1 --a2 --cmdname-- --arg3 

與​​腳本解析後應該有三個參數:arg1cmdnamearg3。參數cmdname應該包含三個值列表otherscript,a1,a2

擁有這樣的配方將有助於將cmdname中的所有內容都傳遞給subprocess.popen(cmdname, ...)調用。

我正在考慮subparsers。但我相信一個分析者不能停下來,並且與其他分析者真的是相互排斥的。任何其他簡單的,已經提供的方式?子類Action的方式來做到這一點?

+3

這會替代語法足夠? './script --arg1 --cmdname「otherscript --a1 --a2」--arg3' – unutbu

+2

@unutbu - 這是一個非常好的建議。與'shlex.split'配對,我想你會做生意。 – mgilson

+0

@unutbu和@mgilson:好點!不知道'shlex.split()'。我提出的語法在shell引用和轉義方面更具吸引力:如果您不必擔心引用,那麼通過子命令傳遞它們的參數會更容易。 – cfi

回答

2

正如你在你的文章中指出的那樣,子類Action可能是這樣做的 - 儘管如果參數otherscript未被argparse所知,那將會非常棘手。你可以用parse_known_args解決這個問題,但你可能不會。老實說,我真的認爲最簡單的方法就是自己預處理sys.argv

import shlex 
s = shlex.split("./script --arg1 --cmdname otherscript --a1 --a2 --cmdname-- --arg3") 
def preprocess(lst): 
    """ 
    process an iterable into 2 lists. 
    The second list contains the portion bracketed by '--cmdname' and '--cmdname--' 
    whereas the first portion contains the rest of it. 
    """ 
    argv1,argv2 = [],[] 
    current = argv1 
    for i in lst: 
     if i == '--cmdname': 
      current = argv2 
     elif i == '--cmdname--': 
      current = argv1 
     else: 
      current.append(i) 
    return argv1,argv2 

l1,l2 = preprocess(s) 
print l1 
print l2 

而且替代實施preprocess它適用於有一個.index方法可切片的對象 - sys.argv會工作得很好:

def preprocess(lst): 
    """ 
    process an iterable into 2 lists. 
    The second list contains the portion bracketed by '--cmdname' and '--cmdname--' 
    whereas the first portion contains the rest of it. 
    """ 
    try: 
     i1 = lst.index('--cmdname') 
     i2 = lst.index('--cmdname--') 
     argv1 = lst[i1+1:i2] 
     argv2 = lst[:i1]+lst[i2+1:] 
    except ValueError: 
     argv1 = lst 
     argv2 = [] 

    return argv1,argv2 

另一種選擇(在一個優越的評論指出, by @unutbu)是將命令行語法改爲更簡單一點的標準,這大大簡化了問題:

./script --arg1 --cmd "otherscript --a1 --a2" --arg3 

然後,您可以像平常使用​​那樣解析cmd(指定type=shlex.split將此參數從字符串轉換爲參數列表)。

+0

這是一個很好的實用方法。這可以與'l1'傳遞給'argparse.parse_args()'。儘管如此,我更喜歡集成的argparse解決方案。我並不關注確切的' - cmdname - '停止標誌。如果這有助於保持在argparse領域,我想'++ cmdname'或甚至'--cmdname'都可以。 – cfi

+0

@cfi - 是的,我假設你會將'l1'傳遞給'parser.parse_args()'。我真的不明白,如果你想完全置身於argparse之中,你會如何比unutbu的建議做得更好(至少,如果你不想深入內部並且依賴一些實現細節) 。問題是,argparse會將'--a1'解釋爲一個參數,它會使用相關的動作,或者抱怨它不知道如何處理這個參數。 – mgilson

+0

@cfi - 我站好了。通過unubu查看答案。 – mgilson

2

如果我們將以--開頭的參數與也以--開頭的命令的部分區分開來,將會有所幫助。

所以,如果./script ++arg1 ++cmdname otherscript --a1 --a2 ++arg3 是可以接受的,則:

import argparse 
import shlex 

parser = argparse.ArgumentParser(prefix_chars = '+') 
parser.add_argument('++arg1', action = 'store_true') 
parser.add_argument('++arg3', action = 'store_true') 
parser.add_argument('++cmdname', nargs = '*') 
args = parser.parse_args(shlex.split("++arg1 ++cmdname otherscript --a1 --a2 ++arg3")) 
print(args) 

產生

Namespace(arg1=True, arg3=True, cmdname=['otherscript', '--a1', '--a2']) 
+0

聰明。 +1這個答案和(更重要的是你以前的評論,我仍然認爲是最好的方式來做到這一點) – mgilson

+0

@mgilson:是的,這是其他命令像'bash -c「...」'工作的方式,所以它可能是程序員/用戶最熟悉的。 – unutbu

+0

我想這種方法的缺點是,如果'otherScript'使用'+'作爲它的「prefix_chars」。這很少見,但它應該很安全。 – mgilson

相關問題