2016-01-20 14 views
5

Ich verwende optparse-applicative und ich möchte in der Lage sein, um Kommandozeilenparameter wie zu analysieren:optparse-applicative Option mit mehreren Werten

$ ./program -a file1 file2 -b filea fileb 

dh zwei Schalter, von denen beide mehrere Argumente annehmen kann .

So habe ich einen Datentyp für meine Optionen, die wie folgt aussieht:

data MyOptions = MyOptions { 
    aFiles :: [String] 
    , bFiles :: [String] } 

Und dann ein Parser wie folgt aus:

config :: Parser MyOptions 
config = MyOptions 
     <$> option (str >>= parseStringList) 
      (short 'a' <> long "aFiles") 
     <*> option (str >>= parseStringList) 
      (short 'b' <> long "bFiles") 

parseStringList :: Monad m => String -> m [String] 
parseStringList = return . words 

Dieser Ansatz scheitert, dass es das erwartete Ergebnis geben Wenn nur ein Argument für jeden Schalter bereitgestellt wird, aber wenn Sie ein zweites Argument angeben, erhalten Sie "Ungültiges Argument" für dieses zweite Argument.

Ich fragte mich, ob ich könnte kludge es durch vorgeben, dass ich vier Optionen wollte: ein boolescher Schalter (d. H.); eine Liste von Strings; ein anderer boolescher Schalter (d. h. -b); und eine andere Liste von Strings. Also änderte ich meine Datentyp:

data MyOptions = MyOptions { 
    isA :: Bool 
    , aFiles :: [String] 
    , isB :: Bool 
    , bFiles :: [String] } 

und modifiziert dann den Parser wie folgt aus:

config :: Parser MyOptions 
config = MyOptions 
     <$> switch 
      (short 'a' <> long "aFiles") 
     <*> many (argument str (metavar "FILE")) 
     <*> switch 
      (short 'b' <> long "bFiles") 
     <*> many (argument str (metavar "FILE")) 

Dieses Mal mit den many und argument combinators anstelle eines expliziten Parser für eine String-Liste.

Aber jetzt die erste many (argument str (metavar "FILE")) verbraucht alle der Argumente, einschließlich der folgenden die -b Schalter.

Also, wie kann ich diesen Argumenten Parser schreiben?

Antwort

4

Abgesehen von Befehlen folgt optparse-applicative der Konvention getopts: Ein einzelnes Argument in der Befehlszeile entspricht einem einzelnen Optionsargument. Es ist sogar ein wenig strenger, da getopts mehrere Optionen mit dem gleichen Switch ermöglicht:

./program-with-getopts -i input1 -i input2 -i input3 

So gibt es keine „Magie“, die Sie sofort Ihr Programm kann helfen, verwenden, wie

./program-with-magic -a 1 2 3 -b foo bar crux 

seit Options.Applicative.Parser wurde nicht in diesem Sinne geschrieben; Es widerspricht auch der POSIX conventions, wo Optionen entweder ein Argument oder keine.

Sie können jedoch dieses Problem von zwei Seiten angehen: entweder -a mehrmals verwenden, wie Sie es in getopts oder dem Benutzer sagen Anführungszeichen zu verwenden:

./program-as-above -a "1 2 3" -b "foo bar crux" 
# works already with your program! 

Um die Mehrfachnutzung einer Option zu aktivieren Sie müssen many (wenn sie optional sind) oder some (wenn sie nicht sind) verwenden.Sie können sogar beide Varianten kombinieren:

multiString desc = concat <$> some single 
    where single = option (str >>= parseStringList) desc 

config :: Parser MyOptions 
config = MyOptions 
    <$> multiString (short 'a' <> long "aFiles" <> help "Use quotes/multiple") 
    <*> multiString (short 'b' <> long "bFiles" <> help "Use quotes/multiple") 

, die Sie

./program-with-posix-style -a 1 -a "2 3" -b foo -b "foo bar" 

Aber Ihre vorgeschlagenen Stil unterstützt wird nicht durch eine Parsing-Bibliothek Ich weiß, da die Position der freien Argumente verwenden, ermöglicht es wäre mehrdeutig . Wenn Sie wirklich -a 1 2 3 -b foo bar crux verwenden möchten, müssen Sie die Argumente selbst analysieren.

+0

Die freien Argumente wären nicht mehrdeutig, wenn die Argumente für "-a" so eingeschränkt wären, dass sie nicht mit "-" beginnen würden. – rampion