スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。
 

Clojure でコマンドライン引数を使う

今回は Clojure でコマンドライン引数を扱うための方法について説明します。

1 ファイルのスクリプト

まずは Clojure で 1 ファイルをスクリプトとして実行する場合です。 スクリプトとして実行する方法については以前の記事をご覧下さい。 1 ファイルを実行する場合には、コマンドライン引数は *command-line-args* という特殊な名前の変数に格納されています。
(特殊な変数が * で囲まれているのは Emacs っぽいですね)

args.clj :
(println *command-line-args*)
~/clojure $ clojure.bat args.clj foo bar baz
(foo bar baz)
*command-line-args*シーケンスなので、各種のシーケンス用関数を使って値を取り出して使います。

args-seq.clj :
(doseq [arg *command-line-args*]
  (println arg))
~/clojure $ clojure.bat args-seq.clj foo bar baz
foo
bar
baz

Leiningen プロジェクト

次は Leiningen を使って作成した Clojure のプロジェクトの場合です。
~/clojure $ lein new app argsample
Generating a project called argsample based on the 'app' template.
プロジェクトの場合は分かりやすいでしょう。 core.clj-main 関数の引数 args がそのままコマンドラインの引数です。

argsample/src/argsample/core.clj :
(ns argsample.core
  (:gen-class))

(defn -main
  "Command line arguments sample"
  [& args]
  (println args)
  (doseq [arg args]
    (println arg))
  )
~/clojure/argsample $ lein run foo bar baz
(foo bar baz)
foo
bar
baz

オプション引数解析

今回はコマンド引数を単に扱う方法を紹介しました。 しかし、実際にプログラムを作る場合には更にオプションの解析を行いたいことでしょう。
Clojure には、 tools.cli というオプション解析用のライブラリーが用意されています。 これの使い方に関しては以下の記事を見てください。
スポンサーサイト
 

コマンドラインプログラムにおける引数、オプションなどの標準仕様

コマンドラインプログラムの引数、オプションといったインターフェースには ちゃんと仕様、ガイドラインといったものが存在しています。 Unix(Linux) では慣習としてなんとなく合うものなのですが、 Windows や Java のプログラムなどでは、インターフェースがめちゃくちゃなプログラムも結構あります。
今回はコマンドラインプログラムの標準的なインターフェースの仕様、動作を紹介します。 プログラムを作る際の参考にしてもらえればと思います。


標準インターフェースはよくあるケースを考慮されて作られているものです。 自分の作っているプログラムは標準的なケースと違うといったことがあるかもしれません。
しかし、 標準のスタイルは合わせること自体に意味があります。
作成する場合にはなるべく標準インターフェースにあわせて作るべきだと思います。

Windows or Unix スタイル

コマンドラインのオプションは Windows では "/"(スラッシュ)、 Unix では "-"(ハイフン) という慣習があります。

コマンドラインはクロスプラットフォームで作りやすいプログラムですし、 もとは Unix 用プログラムでも Windows で使われるものも多いです。 その際、 / は Unix ではディレクトリーの区切りに使われるため 使うことはできませんし、環境で切り替えるのも面倒です。

そのため、コマンドラインプログラムは Windows であっても Unix スタイルで作成するべき でしょう。

仕様、ガイドライン

まずはコマンドラインプログラムのインターフェースに関する規格について説明します。

POSIX 規格

POSIX とは Unix を始めとした各種の OS で移植しやすいプログラムを作成するための国際規格で、 IEEE, The Open Group によって策定、認証されています。 コマンドラインプログラムのインターフェースについてもPOSIX で規定されています。 ここでは、仕様とガイドラインが記述されており、当然これらには従うべきでしょう。 細かい点は上記のリンクを見てもらうとして、重要な点を抜粋してみます。

コマンド名 [-a] [-b] [-c オプション引数] [-d|-e] [-fオプション引数] コマンド引数...
  1. オプションは"-" で始める
  2. オプションは英数字一文字(なるべく小文字)
  3. - を省略して繋げられる ("-a -b -d" == "-abd")
  4. オプションとオプション引数の間には空白を入れる
    • くっつけて書く形式も例外的に認められるところがあるので、作成する側としては両方に対応した方がよいでしょう。
  5. オプションはコマンド引数の前
    • こちらも作成する側としては後や間に書く場合にも対応した方がよいです。
  6. すべてのオプションは省略できなければならない
    • 仕様上は認められていますが、ガイドラインでは禁止です。

このうち特に注意して欲しい点が 2 点あります。

java のプログラムなどで -jar や -output のようなロング名が使われることがあります。 しかし、上記の 2, 3 のルールがあるため、 ロング名は仕様上、完全にアウトです。
ロング名を使いたい場合は、次節の GNU スタイルで書く必要があります。

また、"必須オプション" などといって省略できないオプションを作る人がまれにいます。 しかし、そもそもデフォルトの動作を変えるためのものが "オプション" です。
仕様上は認められていますが、「オプションの言葉の意味わかってるの?」 と言われかねないので、 オプションは必ず省略できるようにしておくべきです。

GNU スタイル

多くのコマンドラインプログラムを提供している GNU でも、 インターフェースの仕様は慣習として定義されています。 これは POSIX の規格を一部拡張した形式になっています。 POSIX の仕様には最低限従うべきです。 これに加えて GNU の仕様にもなるべく合わせた方がいいと思います。
以下、 GNU の拡張部分について説明していきます。


ロング名

オプションのロング名は POSIX 規格上はアウトと書きましたが、ロング名を使いたいことも多いです。 GNU の仕様では次の形式でロング名を認めています。
  • オプションの前を "--"(ハイフン 2 つ)
アルファベットが足りない場合は別ですが、 -o, --output のようになるべくロング、ショートの両方とも用意した方が良いです。


また、オプション引数はショート形式と同様に空白でわけます。 ただし、 くっつけて書く "--name=value" の形も認められています。


対応すべきオプション

GNU の仕様では次の 2 つのオプションを最低限つけるようになっています。 これらのオプションをつけてプログラムを起動すると、 それぞれの表示を標準出力に出力して終了します。
また、最低限必要なのはロング名だけですが、 それぞれのショート形式 -v, -h にも対応しておいた方がいいでしょう。


引数を全て省略するとヘルプ(Usage)を表示するようになっているプログラムも多いです。 それはそれでいいことだと思いますが、引数を取らずに動作するプログラムもあります。 統一した形式でヘルプを表示できることは大事なことだと思います。

また、バージョンも 他のプログラムなどから呼び出して、バージョンを取得したいという場合があるので、 表示方法は統一されているべきです。


なお、この 2 つ以外のオプション名もなるべく GNU の他のプログラムに合わせた方がいいと思います。

エラーメッセージの書式

前節は起動する際のインプットの話でしたが、 アウトプットに関しても、 GNU ではエラーメッセージの書式をガイドラインで規定しています。
ファイル解析エラーのスタイル

ファイル解析エラーというのはコンパイラーなどのプログラムで、 フォーマットの書式に誤りがあった場合に出すエラーです。 これはコンパイラーでなくとも、設定ファイルの解析などで出すことはあると思います。

これらの出力は Emacs など他のプログラムで利用することが多いため、 ファイル解析エラーのメッセージは特に標準スタイルに合わせるべきです。

標準のスタイルは次の形式です。
ファイル名:行番号:エラー種別(Error, Warning 等): メッセージ
(例) /foo/bar/baz.txt:89:Error: Syntax error.

正確にはガイドラインには"エラー種別"は含まれていないのですが、 種別も入れるのが一般的だと思います。


通常エラーのスタイル

ファイル解析ではなく通常のエラーの場合は、最初にプログラム名を入れます。
プログラム名:エラー種別(Error, Warning 等): メッセージ
(例) foo.exe:Error: No such a file or directory. ("/bar/baz.txt")

こちらもガイドライン上は"エラー種別"は含まれていません。

終了ステータス

アウトプットのインターフェースの一つである終了ステータスにも仕様や慣例があります。 最低限のルールは 正常終了では 0 、異常終了では 0 以外 ということです。 これが守られていないとシェルスクリプトなど他のプログラムから呼び出された場合に成功したかどうかの判断ができず、困ることがあります。

異常終了の場合は 0 以外ですが、これは 1 ~ 255 の値を使って、エラー等の状態を表します。 この値はプログラム側で自由に設定していいのですが、基本は 1 で POSIX システム上の C 言語ではマクロで次のように定義されています。

マクロ 状態
EXIT_SUCCESS 0 正常終了
EXIT_FAILURE 1 異常終了



また、慣例として 128 以上は特殊な値 とされています。 この範囲の値は外部からシグナルを受け取って終了したことを意味しており、 さらに 128 を引いた値がシグナル値となるようにします。
例えば SIGHUP(1), SIGINT(2), SIGTERM(15) のシグナルを受け取ったのであれば、 終了ステータスはそれぞれ 129, 130, 143 です。

標準的なプログラミングの動作

前章は仕様やガイドラインで規定されている部分です。 以降は補足や私が慣例上やっていた方がよいと思う点について書いていきます。

ログ出力

ここではアウトプットの動作についてをまとめてみます。


基本方針

Unix 系のコマンドラインプログラムでは基本的に次のような方針で作られます。
正常終了の場合は何も出力せず、エラーがあった場合にメッセージを出力する

基本的にこの方針でよいと思うのですが、 問題なのは処理時間が長い場合です。

こういった場合、ユーザーに固まっていると思われないように "r"(キャリッジリターン, CR)を使って、 進捗表示を出すといったテクニックもあります。
しかし、これは他のプログラムから呼び出したり、ログをファイルに保存したりする時には非常に邪魔になります。 こういう場合は現在の処理内容をログとして出力するといったことがよくやられます。


ログ出力系オプション

基本方針としてはエラーメッセージ以外は出さないものなので、 処理内容を出力する場合、次のようにすることが多いです。
  1. 標準でログ出力して、 quite モード(-q, --quite)を用意
  2. 標準では出力せず、 verbose モード(-V, --verbose)を用意
個人的には処理が長いプログラムに関しては、ユーザーを不安にさせないために 1 つ目の quite モードを用意した方がよいと思います。

ただ、処理が短い場合でも verbose モードはデバッグ用として用意されることも多いです。
また、この verbose のオプションはショート形式が -v にされがちですが、 --version にも -v が使われるので、 ショート形式は大文字の -V にするべきだと思います。


制御コード

ターミナルの出力では制御コードを使って、太字にしたり、色を変えたりといった装飾ができます。
しかし、 これは先ほどの "r"(CR) と同様にログ取りなどで邪魔です。 また、制御コードによる装飾に対応していないターミナルもあります。
出力に制御コードは使うべきではないと思います。

ただし、なるべくわかりやすく表示したいというのも大事なことです。 制御コードを使いたい場合にはオプションで使用しないモードも必ず用意しておいた方がいいでしょう。

標準入出力

コマンドラインプログラムはパイプでつなげたり、 Web サービスなどの他のプログラムから呼び出されることがあります。 このため、ファイルの入出力だけでなく、 なるべく 標準入力、 標準出力にも対応しておかなければなりません。

出力に関しては -o(--output) のオプションで出力ファイル名を指定することが多いと思います。 この際、オプションの省略時に標準出力にしておけば、必須オプションといったものを作る必要もなくなります。

入力に関しては cat プログラムのように入力ファイルはコマンド引数で指定して、 引数を全て省略した場合に標準入力になるといった形式が多いのではないかと思います。

標準エラー出力

先ほどのログ出力のところの方針で正常終了の場合には何も出力しないのは、 標準出力が生成物の出力先でもあるためです。 また、そのためエラー出力用に標準エラー出力も用意されています。

ちゃんとエラーメッセージの出力は標準エラー出力にしておくべきでしょう。
この際、標準エラー出力は標準出力とバッファリングが違ったり、出力先を変えていたりします。 そのため、ログ出力と合わせてみないとわからないようなものではなく、 標準エラー出力だけで独立して理解できるメッセージにしておく必要があります。

サブコマンド スタイル

svn, git といったバージョン管理や gem, lein などのパッケージ管理のプログラムなどでは機能が多かったり、 色々な用途で呼び出されることがあります。
こういった場合には、コマンド引数の最初をサブコマンドとして後の指定を変えるスタイルが取られることが多いです。
コマンド [共通オプション] サブコマンド [サブコマンド別のオプション] コマンド引数...
(例) svn --username foo add -m "message" bar.cpp

引数とオプションの順番など厳密には POSIX 規格から外れていますが、結構認められてきているスタイルです。そのため、機能が多すぎて規格に合わせられないといった場合には サブコマンドのスタイルを検討してみるのもよいと思います。

まとめ

最後にコマンドラインプログラム作成において守っておいた方が良い点をまとめておきます。
  • インターフェース仕様
    • Unix スタイル(オプションは - ) で作る
    • オプションにロング名は使わない (GNU スタイルの -- を使う)
    • 省略できない必須オプションは作らない
    • --help, --version (-h, -v) のオプションを用意する
    • ファイル解析時のエラーメッセージは標準スタイルに合わせる
    • 終了ステータスは 正常終了で 0、 異常終了で 1 ~ 127
  • 推奨動作
    • 余計なメッセージ出力はしない (モードで切り替えるようにしておく)
    • 出力に制御コードは使わない (使う場合は使わないモードも用意)
    • 標準入力、出力に対応する
    • エラーメッセージは標準エラー出力に出す


 
このページをシェア
アクセスカウンター
アクセスランキング
[ジャンルランキング]
コンピュータ
9位
アクセスランキングを見る>>

[サブジャンルランキング]
プログラミング
3位
アクセスランキングを見る>>
カレンダー(アーカイブ)
プルダウン 降順 昇順 年別

01月 | 2014年02月 | 03月
- - - - - - 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 -


はてな新着記事
はてな人気記事
ブロとも申請フォーム
プロフィール

yohshiy

Author:yohshiy
職業プログラマー。
仕事は主に C++ ですが、軽い言語マニアなので、色々使っています。

はてブ:yohshiy のブックマーク
Twitter:@yohshiy

サイト紹介
プログラミング好きのブログです。プログラミング関連の話題や公開ソフトの開発記などを雑多に書いてます。ただ、たまに英語やネット系の話になることも。
上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。