コマンドラインオプションの解析(ちょー簡易版getopts)そのに
コマンドラインオプションの解析(ちょー簡易版getopts) - 趣味的にっきで書いたちょー簡易版getoptsですが、もちっと実用になるように実装してみました。(opts, oargs, rests) <- parseOpts . getOpts "hvab:c" =<< getArgsのように、main関数の一行でコマンドラインオプションの解析ができてきれいです。
特徴は以下の通りです。
- 受け付け可能なオプションの指定ができるようにしました。引数が必要なオプションも指定できます(-o fileみたいなの)。指定方法はよくあるgetoptsを同じ。
- 「--」で区切るとそれ以降は、オプション以外の引数として扱います。
- getOptsの返り値は、「指定されたオプションのリスト」、「引数を指定されたオプションの連想リスト」、「オプション以外の引数のリスト」です。
- 受付不可能なオプションが渡されるとerrorで終了します。←この辺、乱暴。
- オプション関係の関数をUtilモジュールにまとめています。
ファイル: Util.hs
module Util (getOpts, OptionResult, usage, version) where import Data.List (isPrefixOf) import System.Environment (getProgName) import System.Exit (exitWith, ExitCode(ExitFailure)) import System.IO (stderr, hPutStrLn) type OptionFormat = ([String], [String]) type OptionResult = ([String], [(String, String)], [String]) getOpts :: String -> [String] -> OptionResult getOpts fmt args = let (os, rs) = spanDM args (os', oas, rs') = spanFmt (parseFmt $ reverse fmt) "" os in (os', oas, rs' ++ rs) where spanDM :: [String] -> ([String], [String]) spanDM [] = ([], []) spanDM (x : xs) | x /= "--" = let (ys, zs) = spanDM xs in (x : ys, zs) | otherwise = ([], xs) parseFmt :: String -> OptionFormat parseFmt [] = ([], []) parseFmt (':' : x : xs) = let (ys, zs) = parseFmt xs in (['-', x] : ys, zs) parseFmt (x : xs) = let (ys, zs) = parseFmt xs in (ys, ['-', x] : zs) spanFmt :: OptionFormat -> String -> [String] -> OptionResult spanFmt _ "" [] = ([], [], []) spanFmt fs "" (x : xs) | elem x $ fst fs = let (os, oas, rs) = spanFmt fs x xs in (x : os, oas, rs) | elem x $ snd fs = let (os, oas, rs) = spanFmt fs "" xs in (x : os, oas, rs) | "-" `isPrefixOf` x = error $ "invalid option: " ++ x | otherwise = ([], [], x : xs) spanFmt fs b [] = error $ "invalid option: " ++ b spanFmt fs b (x : xs) = let (os, oas, rs) = spanFmt fs "" xs in (os, (b, x) : oas, rs) usage :: String -> IO () usage m = do prog <- getProgName hPutStrLn stderr $ "usage: " ++ prog ++ " " ++ m exitWith (ExitFailure 1) version :: String -> IO () version m = do prog <- getProgName hPutStrLn stderr $ prog ++ " - " ++ m exitWith (ExitFailure 1)
使い方: ファイル: a.hs
module Main (main) where import Control.Monad (when) import System.Environment (getArgs) import Util (getOpts, OptionResult, usage, version) parseOpts :: OptionResult -> IO OptionResult parseOpts (os, oas, rs) = do when ("-h" `elem` os) $ usage "[opts] [files]" when ("-v" `elem` os) $ version "0.1a" return (os, oas, rs) main :: IO () main = do (opts, oargs, rests) <- parseOpts . getOpts "hvab:c" =<< getArgs putStrLn $ "opts: " ++ show opts -- 指定したオプション。 putStrLn $ "oargs: " ++ show oargs -- オプションの引数。 putStrLn $ "rests: " ++ show rests -- オプション以外。 -- Local Variables: -- compile-command: "ghc -W -fno-warn-unused-matches --make -o a a" -- End:
実行例:
$ ./a -a -b foo -c bar baz opts: ["-a","-b","-c"] oargs: [("-b","foo")] rests: ["bar","baz"] $ ./a -a -b foo -- -c bar baz opts: ["-a","-b"] oargs: [("-b","foo")] rests: ["-c","bar","baz"]
BUGS: よくあるgetoptsとの微妙な仕様差異は調べてません。