オプションの扱い混乱ネタ
先日Twitterで--
という引数ってなんだろうという話題が上がっていた。@ryoana14さんによる投稿がヒントになった話題だ。
シェルシェルしてるとたまに見かける「--」は何なのだろう
— りょー (@ryoana14) March 1, 2016
確かオプションの打ち止め指定だったと思うのだが、どんな動きになるか手を動かして確認してみることにした。
混乱実験
論より証拠、考えるより実践。ハイフン付きなファイル名のファイルを作成し、手を動かして実験し、混乱を体験してみるべし。
手順に書いているように、念のため作業用のディレクトリを作って試そう。本当に混乱してしまったら作業ディレクトリごと削除すべし。
準備
$ mkdir test $ cd test $ touch -- a b c l -a -b -c -l
混乱トライ!
いったい何が起こっているのか?結果をよく見て考えてみよう。実行環境はMacなので、LinuxなどGNU版コマンドだと違う場合があるかもしれない。
$ echo * -a -b -c -l a b c l $ ls -a -b -c -l a b c l $ ls '-a' -a -b -c -l ./ ../ a b c l $ ls * -rw-r--r-- 1 tashiro staff 0 3 2 14:02 a -rw-r--r-- 1 tashiro staff 0 3 2 14:02 b -rw-r--r-- 1 tashiro staff 0 3 2 14:02 c -rw-r--r-- 1 tashiro staff 0 3 2 14:02 l $ ls -l total 0 -rw-r--r-- 1 tashiro staff 0 3 2 10:36 -a -rw-r--r-- 1 tashiro staff 0 3 2 10:36 -b -rw-r--r-- 1 tashiro staff 0 3 2 10:36 -c -rw-r--r-- 1 tashiro staff 0 3 2 10:46 -l -rw-r--r-- 1 tashiro staff 0 3 2 10:40 a -rw-r--r-- 1 tashiro staff 0 3 2 10:40 b -rw-r--r-- 1 tashiro staff 0 3 2 10:40 c -rw-r--r-- 1 tashiro staff 0 3 2 10:46 l $ ls -l * -rw-r--r-- 1 tashiro staff 0 3 2 14:02 a -rw-r--r-- 1 tashiro staff 0 3 2 14:02 b -rw-r--r-- 1 tashiro staff 0 3 2 14:02 c -rw-r--r-- 1 tashiro staff 0 3 2 14:02 l $ ls -* total 0 -rw-r--r-- 1 tashiro staff 0 3 2 22:41 -a -rw-r--r-- 1 tashiro staff 0 3 2 22:41 -b -rw-r--r-- 1 tashiro staff 0 3 2 22:41 -c -rw-r--r-- 1 tashiro staff 0 3 2 22:41 -l drwxr-xr-x 10 tashiro staff 340 3 2 22:41 ./ drwx------+ 11 tashiro staff 374 3 2 21:42 ../ -rw-r--r-- 1 tashiro staff 0 3 2 22:41 a -rw-r--r-- 1 tashiro staff 0 3 2 22:41 b -rw-r--r-- 1 tashiro staff 0 3 2 22:41 c -rw-r--r-- 1 tashiro staff 0 3 2 22:41 l $ ls ./* ./-a ./-b ./-c ./-l ./a ./b ./c ./l $ ls -a -b -- -c -l -c -l $ ls -- -a -b -c -l -a-b-c-l $ file * file: invalid option -- a file: invalid option -- l Usage: file [-bchikLNnprsvz0] [-e test] [-f namefile] [-F separator] [-m magicfiles] [-M magicfiles] file... file -C -m magicfiles Try file --help' for more information. $ file -- * -a: empty -b: empty -c: empty -l: empty a: empty b: empty c: empty l: empty $ echo * | tr ' ' '\n' | xargs -n 1 ls -l total 0 -rw-r--r-- 1 tashiro staff 0 3 2 14:02 -a -rw-r--r-- 1 tashiro staff 0 3 2 14:02 -b -rw-r--r-- 1 tashiro staff 0 3 2 14:02 -c -rw-r--r-- 1 tashiro staff 0 3 2 14:02 -l drwxr-xr-x 10 tashiro staff 340 3 2 14:02 . drwx------+ 11 tashiro staff 374 3 2 14:02 .. -rw-r--r-- 1 tashiro staff 0 3 2 14:02 a -rw-r--r-- 1 tashiro staff 0 3 2 14:02 b -rw-r--r-- 1 tashiro staff 0 3 2 14:02 c -rw-r--r-- 1 tashiro staff 0 3 2 14:02 l total 0 -rw-r--r-- 1 tashiro staff 0 3 2 14:02 -a -rw-r--r-- 1 tashiro staff 0 3 2 14:02 -b -rw-r--r-- 1 tashiro staff 0 3 2 14:02 -c -rw-r--r-- 1 tashiro staff 0 3 2 14:02 -l -rw-r--r-- 1 tashiro staff 0 3 2 14:02 a -rw-r--r-- 1 tashiro staff 0 3 2 14:02 b -rw-r--r-- 1 tashiro staff 0 3 2 14:02 c -rw-r--r-- 1 tashiro staff 0 3 2 14:02 l total 0 -rw-r--r-- 1 tashiro staff 0 3 2 14:02 -a -rw-r--r-- 1 tashiro staff 0 3 2 14:02 -b -rw-r--r-- 1 tashiro staff 0 3 2 14:02 -c -rw-r--r-- 1 tashiro staff 0 3 2 14:02 -l -rw-r--r-- 1 tashiro staff 0 3 2 14:02 a -rw-r--r-- 1 tashiro staff 0 3 2 14:02 b -rw-r--r-- 1 tashiro staff 0 3 2 14:02 c -rw-r--r-- 1 tashiro staff 0 3 2 14:02 l total 0 -rw-r--r-- 1 tashiro staff 0 3 2 14:02 -a -rw-r--r-- 1 tashiro staff 0 3 2 14:02 -b -rw-r--r-- 1 tashiro staff 0 3 2 14:02 -c -rw-r--r-- 1 tashiro staff 0 3 2 14:02 -l -rw-r--r-- 1 tashiro staff 0 3 2 14:02 a -rw-r--r-- 1 tashiro staff 0 3 2 14:02 b -rw-r--r-- 1 tashiro staff 0 3 2 14:02 c -rw-r--r-- 1 tashiro staff 0 3 2 14:02 l -rw-r--r-- 1 tashiro staff 0 3 2 14:02 a -rw-r--r-- 1 tashiro staff 0 3 2 14:02 b -rw-r--r-- 1 tashiro staff 0 3 2 14:02 c -rw-r--r-- 1 tashiro staff 0 3 2 14:02 l $ echo * | tr ' ' '\n' | xargs -n 1 ls -l -- -rw-r--r-- 1 tashiro staff 0 3 2 22:41 -a -rw-r--r-- 1 tashiro staff 0 3 2 22:41 -b -rw-r--r-- 1 tashiro staff 0 3 2 22:41 -c -rw-r--r-- 1 tashiro staff 0 3 2 22:41 -l -rw-r--r-- 1 tashiro staff 0 3 2 22:41 a -rw-r--r-- 1 tashiro staff 0 3 2 22:41 b -rw-r--r-- 1 tashiro staff 0 3 2 22:41 c -rw-r--r-- 1 tashiro staff 0 3 2 22:41 l $ echo ./* | tr ' ' '\n' | xargs -n 1 ls -l -rw-r--r-- 1 tashiro staff 0 3 2 14:02 ./-a -rw-r--r-- 1 tashiro staff 0 3 2 14:02 ./-b -rw-r--r-- 1 tashiro staff 0 3 2 14:02 ./-c -rw-r--r-- 1 tashiro staff 0 3 2 14:02 ./-l -rw-r--r-- 1 tashiro staff 0 3 2 14:02 ./a -rw-r--r-- 1 tashiro staff 0 3 2 14:02 ./b -rw-r--r-- 1 tashiro staff 0 3 2 14:02 ./c -rw-r--r-- 1 tashiro staff 0 3 2 14:02 ./l
後始末
$ cd .. $ rm -rf test
コマンドの引数に指定されたものが、オプションなのか?オプションの引数なのか?ファイル名なのか?などの判断だが、これはシェルが判断しているのではない。コマンド側の実装問題だということだ。
オプションの処理
getoptについて
コマンド側でオプションの処理をすることになるが、引数が伴うオプションがあったり、複数の引数を指定する場合が考えられ、処理が複雑になることが予想される。そのためC言語ではgetopt
という関数を使うことが多いようだ。詳しくはマニュアルを参照してみよう。
シェルスクリプトでオプション処理
bashにはgetopts
という内部関数がある。下記に動作検証のスクリプトを載せる。
getopts.bash
#!/bin/bash while getopts abc:d: OPT do case in a) echo "option a" ;; b) echo "option b" ;; c) echo "option c; optarg " ;; d) echo "option d; optarg " ;; \?) echo 'another option' ;; esac done
while getopts
の後のabc:d:
がオプション指定だ。このシェルスクリプトは下記のオプションを指定できる。cとdの後ろにコロンが付いてるのは、引数を伴うオプションの意味。
- -a
- -b
- -c 引数
- -d 引数
ではこのスクリプトを実行してみよう。
$ ./getopts.bash -a -c ccc -- -d ddd option a option c; optarg ccc $ ./getopts.bash -a -c ccc -d ddd option a option c; optarg ccc option d; optarg ddd $ ./getopts.bash -c ./getopts.sh: option requires an argument -- c another option $ ./getopts.bash -e ./getopts.sh: illegal option -- e another option
ということで、--
以降のオプションは処理されない。また設定していないオプションが指定されたり、引数が必要なオプションで引数無しだとエラーが発生する。
詳しくは例えば下記のページを参考に。