読者です 読者をやめる 読者になる 読者になる

日々之迷歩

世の中わからんことだらけ

ITが複雑で難しくなっていく様に翻弄される日々です

オプションの扱い混乱ネタ

Shell シェル芸

先日Twitterで--という引数ってなんだろうという話題が上がっていた。@ryoana14さんによる投稿がヒントになった話題だ。

確かオプションの打ち止め指定だったと思うのだが、どんな動きになるか手を動かして確認してみることにした。

混乱実験

論より証拠、考えるより実践。ハイフン付きなファイル名のファイルを作成し、手を動かして実験し、混乱を体験してみるべし。

手順に書いているように、念のため作業用のディレクトリを作って試そう。本当に混乱してしまったら作業ディレクトリごと削除すべし。

準備

$ 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という関数を使うことが多いようだ。詳しくはマニュアルを参照してみよう。

Man page of 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

ということで、--以降のオプションは処理されない。また設定していないオプションが指定されたり、引数が必要なオプションで引数無しだとエラーが発生する。

詳しくは例えば下記のページを参考に。

逆引きシェルスクリプト/getoptsを利用して引数を取得する(bashビルドイン) - Linuxと過ごす