我正在使用optparse-applicative,我希望能够解析命令行参数,例如:
$ ./program -a file1 file2 -b filea fileb
Run Code Online (Sandbox Code Playgroud)
即,两个开关,两者都可以采用多个参数.
所以我的选项有一个数据类型,如下所示:
data MyOptions = MyOptions {
aFiles :: [String]
, bFiles :: [String] }
Run Code Online (Sandbox Code Playgroud)
然后Parser像这样:
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
Run Code Online (Sandbox Code Playgroud)
这种方法失败的原因是,当为每个开关提供一个参数时,它将给出预期的结果,但是如果你提供第二个参数,则为第二个参数得到"无效的参数".
我想知道我是否可以通过假装我想要四个选项来克服它:一个布尔开关(即-a); 字符串列表; 另一个布尔开关(即-b); 和另一个字符串列表.所以我改变了我的数据类型:
data MyOptions = MyOptions {
isA :: Bool
, aFiles :: [String]
, isB :: Bool
, bFiles :: [String] }
Run Code Online (Sandbox Code Playgroud)
然后像这样修改解析器:
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"))
Run Code Online (Sandbox Code Playgroud)
这次使用many和argument组合器而不是字符串列表的显式解析器.
但现在第一个many (argument str (metavar "FILE"))消耗所有参数,包括-b切换后的参数.
那么如何编写这个参数解析器呢?
除了命令之外,optparse-applicative遵循getopts约定:命令行上的单个参数对应于单个选项参数.它甚至更严格一些,因为getopts它将允许使用相同开关的多个选项:
./program-with-getopts -i input1 -i input2 -i input3
Run Code Online (Sandbox Code Playgroud)
所以没有"神奇"可以帮助你立即使用你的程序
./program-with-magic -a 1 2 3 -b foo bar crux
Run Code Online (Sandbox Code Playgroud)
因为Options.Applicative.Parser没有记住这一点; 它也与POSIX约定相矛盾,其中options选择一个参数或不参数.
但是,您可以从两个方面解决此问题:要么-a像您一样多次使用getopts,要么告诉用户使用引号:
./program-as-above -a "1 2 3" -b "foo bar crux"
# works already with your program!
Run Code Online (Sandbox Code Playgroud)
要允许多次使用您必须使用的选项many(如果它们是可选的)或some(如果它们不是).您甚至可以组合两种变体:
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")
Run Code Online (Sandbox Code Playgroud)
这使您可以使用
./program-with-posix-style -a 1 -a "2 3" -b foo -b "foo bar"
Run Code Online (Sandbox Code Playgroud)
但是我所知道的任何解析库都不支持你提出的样式,因为自由参数的位置是不明确的.如果你真的想使用-a 1 2 3 -b foo bar crux,你必须自己解析参数.