ジュリア。 スクリプトとコマンドライン引数の解析



プログラミング言語Juliaを引き続き扱います。 データの分析と処理に焦点を当てた言語ではバッチモードの操作が必要なだけなので、Julia言語でスクリプトを実装し、コマンドラインから引数を渡すことの特殊性を考慮します。 このトピックは誰かにとってはささいなことかもしれませんが、この言語の斬新さを考えると、Juliaで紹介されているコマンドライン引数とライブラリの解析方法の簡単な概要がまだ役に立つことを願っています。







まず、スクリプトがどのように作成されるかについてのいくつかの言葉。 スクリプトは、インタープリターを示す特別な形式の行で始まります。 行は、シバンと呼ばれるシーケンスで始まります。 ジュリアの場合、この行は次のとおりです。







#!/usr/bin/env julia
      
      





もちろん、これを行うことはできませんが、次のコマンドでスクリプトを実行する必要があります。







 julia .jl
      
      





また、スクリプトは改行文字で終了する必要があります。 これはPOSIX標準の要件であり、改行文字で終了する一連の文字としての文字列の定義に従っています。







スクリプトを直接実行するには、 executable



属性が必要です。 次のコマンドを使用して、このような属性を端末に追加できます。







 chmod +x .jl
      
      





これらのルールは、おそらくMS Windowsを除くすべての最新のオペレーティングシステムに有効です。







ARGS配列



パラメーターを渡すための最初のオプションに移りましょう。 コマンドライン引数は、配列定数Base.ARGSを介してJuliaスクリプトで使用できます。 最も簡単なスクリプトを準備しましょう。







 #!/usr/bin/env julia @show typeof(ARGS) @show ARGS
      
      





このスクリプトは、ARGS配列のタイプと内容をコンソールに出力するだけです。







多くの場合、ファイル名はコマンドライン引数として渡されます。 そして、ここでは、引数として渡されたファイルテンプレートを処理する特性があります。 たとえば、コマンド./args.jl *.jl



を使用してスクリプトを実行し、次を取得します。







 >./args.jl *.jl typeof(ARGS) = Array{String,1} ARGS = ["argparse.jl", "args.jl", "docopt.jl"]
      
      





次に、マスクを引用符で囲んで、コマンドラインパラメーターを少し変更します。

./args.jl "*.jl"



。 その結果、以下が得られます。







 >./args.jl "*.jl" typeof(ARGS) = Array{String,1} ARGS = ["*.jl"]
      
      





明らかな違いが見られます。 最初のケースでは、同じディレクトリにあるすべてのファイルの名前を持つ配列を取得しました。 2番目の場合、これは引数としてコマンドラインに渡されたマスクと同じです。 スクリプトのこの異なる動作の理由は、スクリプトが実行されたbashインタープリター(およびそれに近いもの)がファイル名テンプレートを認識するためです。 「Bash Pattern Matching」または「Bash Wildcards」の検索エンジンでさらに多くの情報が見つかります。 そして、すべてをまとめてGlobsと呼びます。







パターンの中では、複数の文字をマスクすることができます-*、1文字をマスクします-?..範囲[...]で検索し、さらに、複雑な組み合わせを指定する機能もあります。







 >./args.jl {args,doc}* typeof(ARGS) = Array{String,1} ARGS = ["args.jl", "docopt.jl"]
      
      





詳細については、GNU / Linuxコマンドラインツールの概要を参照してください。







何らかの理由で、bashが提供するglobsメカニズムを使用したくない場合は、Globs.jlパッケージを使用して、スクリプトから既にマスクでファイルを検索できます。

次のコードは、引数文字列で見つかったすべてをファイル名の単一の配列に変換します。 つまり、ユーザーが引用符でマスクを指定したか、引用符なしでマスクを指定したか、既存または存在しないファイルの名前を単にリストしたかに関係なく、実際のファイルまたはディレクトリの名前のみが結果のfilelist



配列に残ります。







 using Glob filelist = unique(collect(Iterators.flatten(map(arg -> glob(arg), ARGS))))
      
      





実際、これらの簡単な例は、プログラマが引数を解析するためのすべてのロジックを実装するARGS配列の使用のデモです。 このアプローチは、引数のセットが非常に単純な場合によく使用されます。 たとえば、ファイル名のリスト。 または、単純な文字列操作で処理できる1つまたは2つのオプション。 ARGS要素へのアクセスは、他の配列の要素と同じです。 Juliaの配列の最初の要素のインデックスが1であることだけを覚えておいてください。







パッケージArgParse.jl



これは、解析ロジックを実装する必要なく、コマンドラインの属性とオプションを記述するための柔軟なツールです。

パッケージのドキュメントから少し変更した例を使用してみましょう-http : //carlobaldassi.github.io/ArgParse.jl/stable/







 #!/usr/bin/env julia using ArgParse function parse_commandline() s = ArgParseSettings() @add_arg_table s begin "--opt1" help = "an option with an argument" "--opt2", "-o" help = "another option with an argument" arg_type = Int default = 0 "--flag1" help = "an option without argument, ie a flag" action = :store_true "arg1" help = "a positional argument" required = true end return parse_args(s) end function main() @show parsed_args = parse_commandline() println("Parsed args:") for (arg,val) in parsed_args print(" $arg => ") show(val) println() end end main()
      
      





引数なしでこのスクリプトを実行すると、構成に関する参照情報の出力が得られます。







 >./argparse.jl required argument arg1 was not provided usage: argparse.jl [--opt1 OPT1] [-o OPT2] [--flag1] arg1
      
      





さらに、角括弧内にオプションの引数があります。 arg1



としてマークされた引数(つまり、それを代用するもの)は必須です。







再度実行しますが、必要な属性arg1



指定します。







 >./argparse.jl test parsed_args = parse_commandline() = Dict{String,Any}("flag1"=>false,"arg1"=>"test","opt1"=>nothing,"opt2"=>0) Parsed args: flag1 => false arg1 => "test" opt1 => nothing opt2 => 0
      
      





parsed_args



は連想配列であり、キーはparse_commandline



関数で行われた宣言による属性の名前であり、それらの値はデフォルトで設定されたかコマンドライン引数の値として渡されたものであることがparse_commandline



ます。 さらに、値は、宣言中に明示的に指定されたタイプです。







引数は@add_arg_table



マクロを使用して@add_arg_table



ます。 オプションを宣言することが可能です:







  "--opt2", "-o" help = "another option with an argument" arg_type = Int default = 0
      
      





または引数







  "arg1" help = "a positional argument" required = true
      
      





さらに、完全な形式と短い形式を示すオプションを指定できます(同時に--opt2



-o



)。 または、単一の形式でのみ。 タイプはarg_type



フィールドで指定されます。 デフォルト値は、 default = ...



を使用してdefault = ...



できdefault = ...



デフォルト値の代わりに、引数を必須にする必要がありますrequired = true



です。

たとえば、引数の有無に応じてtrue



またはfalse



割り当てるなど、自動アクションを宣言することができます。 これは、 action = :store_true



を使用して行われaction = :store_true









  "--flag1" help = "an option without argument, ie a flag" action = :store_true
      
      





help



フィールドには、コマンドラインのプロンプトに表示されるテキストが含まれています。

起動時にすべての属性を指定すると、次のようになります:







 >./argparse.jl --opt1 "2+2" --opt2 "4" somearg --flag parsed_args = parse_commandline() = Dict{String,Any}("flag1"=>true,"arg1"=>"somearg","opt1"=>"2+2","opt2"=>4) Parsed args: flag1 => true arg1 => "somearg" opt1 => "2+2" opt2 => 4
      
      





Atom / Juno IDEからのデバッグ用に、スクリプトの最初の行に、ARGS配列を初期化するために、多少汚いが機能する以下のコードを追加できます。







 if (Base.source_path() != Base.basename(@__FILE__)) vcat(Base.ARGS, ["--opt1", "2+2", "--opt2", "4", "somearg", "--flag"] ) end
      
      





マクロ@__FILE__



は、マクロがデプロイされているファイルの名前です。 また、REPLのこの名前は、 Base.source_path()



取得した現在のプログラムファイルの名前とは異なります。 Base.ARGS



配列Base.ARGS



別の値で初期化することはできませんが、配列自体は定数ではないため、新しい行を追加できます。 配列はJuliaの列であるため、 vcat



(垂直連結)を使用します。







ただし、Junoエディターの設定では、スクリプトを実行するための引数を設定できます。 ただし、デバッグされたスクリプトごとに個別に変更する必要があります。







パッケージDocOpt.jl



このオプションは、docoptマークアップ言語アプローチ-http://docopt.org/の実装です。 この言語の主なアイデアは、フォーム内のオプションと引数の宣言的記述です。これは、スクリプトの内部記述でもあります。 特別なテンプレート言語が使用されます。







このパッケージのドキュメントのサンプルを使用しますhttps://github.com/docopt/DocOpt.jl







 #!/usr/bin/env julia doc = """Naval Fate. Usage: naval_fate.jl ship new <name>... naval_fate.jl ship <name> move <x> <y> [--speed=<kn>] naval_fate.jl ship shoot <x> <y> naval_fate.jl mine (set|remove) <x> <y> [--moored|--drifting] naval_fate.jl -h | --help naval_fate.jl --version Options: -h --help Show this screen. --version Show version. --speed=<kn> Speed in knots [default: 10]. --moored Moored (anchored) mine. --drifting Drifting mine. """ using DocOpt # import docopt function args = docopt(doc, version=v"2.0.0") @show args
      
      





doc = ...



という表記は、docoptの宣言全体を含むJulia doc



文字列の作成です。 引数なしでコマンドラインで実行した結果は次のようになります。







 >./docopt.jl Usage: naval_fate.jl ship new <name>... naval_fate.jl ship <name> move <x> <y> [--speed=<kn>] naval_fate.jl ship shoot <x> <y> naval_fate.jl mine (set|remove) <x> <y> [--moored|--drifting] naval_fate.jl -h | --help naval_fate.jl --version
      
      





ヒントを使用して「新しい船を作成」しようとすると、コマンドラインを解析して生成された連想args



配列の出力が得られます







 >./docopt.jl ship new Bystriy args = Dict{String,Any}( "remove"=>false, "--help"=>false, "<name>"=>["Bystriy"], "--drifting"=>false, "mine"=>false, "move"=>false, "--version"=>false, "--moored"=>false, "<x>"=>nothing, "ship"=>true, "new"=>true, "shoot"=>false, "set"=>false, "<y>"=>nothing, "--speed"=>"10")
      
      





docopt



関数docopt



宣言されます:







 docopt(doc::AbstractString, argv=ARGS; help=true, version=nothing, options_first=false, exit_on_error=true)
      
      





名前付き引数help



version



oprtions_first



exit_on_error



は、コマンドラインの引数パーサーのexit_on_error



動作を指定しexit_on_error



。 たとえば、エラーが発生した場合、バージョンリクエストで実行を完了するには、 -h



リクエストでversion=…



置き換えて値を返します-ヘルプを発行します。 options_first



、オプションが位置引数の前にあることを示すために使用されます。







次に、この宣言型言語と、入力値に対する引数パーサーの反応を詳しく見てみましょう。







宣言は、コマンドラインのテキストに加えて、スクリプト自体のドキュメントの一部である可能性がある任意のテキストで始まります。 サービスワード「Usage:」は、このスクリプトの使用パターンを宣言します。







 Usage: naval_fate.jl ship new <name>... naval_fate.jl ship <name> move <x> <y> [--speed=<kn>]
      
      





引数は、 <name>



<x>



<y>



の形式で宣言されます。 前に取得したargs



連想配列では、これらの引数がキーとして機能することに注意してください。 起動フォーム./docopt.jl ship new Bystriy



を使用したため、次の明示的に初期化された値を取得しました。







  "<name>"=>["Bystriy"], "ship"=>true, "new"=>true,
      
      





docopt言語によると、オプションの要素は角括弧で指定されます。 たとえば、 [--speed=<kn>]



。 必須の要素は括弧で指定されていますが、特定の条件があります。 たとえば、 (set|remove)



は、そのうちの1つの要件を設定します。 要素が角括弧なしで指定されている場合(例えば、 naval_fate.jl --version



、この特定の実行オプションでは--version



が必須オプションであると言います。







次のセクションはオプションの説明セクションです。 「Options:」という単語で始まります

オプションはそれぞれ別々の行で宣言されます。 行頭の左側のインデントは重要です。 オプションごとに、完全な形式と短い形式を指定できます。 ツールチップに表示されるオプションの説明と同様に。 この場合、オプション-h | --help, --version



-h | --help, --version



自動的に認識されます。 それらへの応答は、 docopt



関数への引数によって与えられます。 検討するのは興味深い宣言です:







  --speed=<kn> Speed in knots [default: 10].
      
      





ここで、形式...=<kn>



は何らかの値の存在を指定し、 [default: 10]



はデフォルト値を定義します。 args



取得した値に再び戻ります。







 "--speed"=>"10"
      
      





たとえば、ArgParseパッケージとの基本的な違いは、値が入力されないことです。 つまり、 default: 10



値は文字列「10」として設定されます。

引数を解析した結果としてargs



表示される他の引数については、それらの値に注意する必要があります。







  "remove"=>false, "--help"=>false, "--drifting"=>false, "mine"=>false, "move"=>false, "--version"=>false, "--moored"=>false, "<x>"=>nothing, "shoot"=>false, "set"=>false, "<y>"=>nothing,
      
      





つまり、すべてのユースケースのdocopt宣言で指定されたすべてのテンプレート要素は、元の名前での分析の結果として表示されます。 ここでは、コマンドラインに存在しなかったオプションの引数はすべてfalseです。 引数<x>



<y>



も起動行から欠落しており、値はありません。 解析パターンが一致した他の引数が真になりました:







  "ship"=>true, "new"=>true,
      
      





そして、テンプレートの次の要素に対して特定の値をすでに受け取っています。







  "<name>"=>["Bystriy"], "--speed"=>"10"
      
      





最初の値は引数の置換としてコマンドラインで明示的に設定され、2番目の値はデフォルト値のオプションでした。





また、現在のスクリプトの名前は自動的に計算できることに注意してください。

たとえば、次のように入力できます。







 doc = """Naval Fate. Usage: $(Base.basename(@__FILE__)) ship new <name>… """
      
      





コマンドライン引数パーサーを配置するための追加の推奨事項は、ファイルの先頭に配置することです。 現時点でのジュリアの不快な機能は、モジュールのかなり長い接続です。 たとえば、 using Plots; using DataFrames



using Plots; using DataFrames



すると、数秒間待機するスクリプトを送信できます。 これは、サーバー側のシングルロードスクリプトでは問題になりませんが、コマンドライン引数のヒントを表示したいだけのユーザーを悩ますでしょう。 そのため、最初にヘルプを発行してコマンドライン引数を確認し、その後で作業に必要なライブラリのロードに進む必要があります。







おわりに



この記事は、ジュリアの引数を解析するすべての方法の完全な議論であると主張していません。 ただし、考慮されるオプションは、実際には3つの可能なオプションをカバーしています。 ARGS



アレイの完全な手動分析。 厳密に宣言されていますが、ArgParseの引数を自動的に解析します。 そして、厳密ではありませんが完全に宣言的なdocoptの形式。 ユースケースの選択は、解析された引数の複雑さに完全に依存します。 docoptを使用するオプションは、最も簡単に使用できるようですが、受け取った引数の値に対して明示的な型変換が必要です。 ただし、スクリプトがファイル名以外を受け入れない場合、通常のprintln("Run me with file name")



関数println("Run me with file name")



を使用してヘルプを発行し、示されているようにARGS



から直接ファイル名を解析することができます。最初のセクションで。







参照資料






All Articles