オプションの扱い混乱ネタ
先日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
ということで、--以降のオプションは処理されない。また設定していないオプションが指定されたり、引数が必要なオプションで引数無しだとエラーが発生する。
詳しくは例えば下記のページを参考に。