Interpret argument with bash

bashシェルスクリプトで引数を解釈し処理するスクリプトを書きたい時に必要になるコマンドや特殊変数、関数をまとめました。例もあるよ。

$@について追記しました!感謝! https://twitter.com/masutaka/status/796703261185216512

$*

順番に見てく。まず$*

すべての引数が設定される特殊変数。

こんなスクリプトがあったとして…。

#!/bin/sh

echo $*

こうなる。

$ ./argument.sh a bb ccc
a bb ccc

$@

すべての引数が設定される特殊変数。え!?@*と同じじゃん!

違いは、「$@はスペース区切りで設定される」のに対し、「@*は環境変数IFS区切りで設定される」こと!知らなかった...。

IFSが空の場合は上にもあるようにスペース区切り。

$ ./argument.sh a bb ccc
a bb ccc

環境変数IFSを設定すると...。

#!/bin/sh

IFS='区切りだよ'
echo $*

おお...。おもろい。

$ ./argument.sh abbccc
abbccc

$ ./argument.sh a区切りだよbb区切りだよccc
a     bb     ccc

ちなみにIFSはInternal Field Separatorの略!

内部フィールド区切り文字 (Internal Field Separator) です。展開を行った後に単語を分割する場合や、組み込みコマンドの read を使ったときに行を単語に分割する場合に使われます。 デフォルト値は "<空白><タブ><改行>" です。

https://linuxjm.osdn.jp/html/GNU_bash/man1/bash.1.html

getopt

コマンドラインオプションを解釈するコマンド。

動きを見てく。

#!/bin/sh

getopt ab:c: $*
echo exit status is $?

-aは引数無くてOK。

$ ./getopt.sh -a
 -a --
exit status is 0

あってもOK。

$ ./getopt.sh -a hoge
 -a -- hoge
exit status is 0

-bは引数必須!

$ ./getopt.sh -b
getopt: option requires an argument -- b
 --
exit status is 1

$ ./getopt.sh -b hoge
 -b hoge --
exit status is 0

複数のオプション。

$ ./getopt.sh -a -b hoge
 -a -b hoge --
exit status is 0

$ ./getopt.sh -a -b hoge -c fuga
 -a -b hoge -c fuga --
exit status is 0

set

オプションを設定するコマンド。オプション無しで引数を渡して実行した場合、引数は順番に位置パラメータ変数($1,$2,$3,...)に格納される。

#!/bin/sh

args=$(getopt ab:c: $*)

set $args

echo \$1 : $1
echo \$2 : $2
echo \$3 : $3
echo \$4 : $4
echo \$5 : $5

この例だと-a,-bはsetコマンドのオプションとして解釈されてしまっているので位置パラメータに保存されていない。

$ ./setopt.sh -a -b hoge -c fuga
$1 : hoge
$2 : -c
$3 : fuga
$4 : --
$5 :

--

  • --以降はオプションではないということを明示するオプション
$ cat setopt.sh
#!/bin/sh

args=$(getopt ab:c: $*)

set -- $args

echo \$1 : $1
echo \$2 : $2
echo \$3 : $3
echo \$4 : $4
echo \$5 : $5
  • -a,-bがオプション扱いされなくなった
$ ./setopt.sh -a -b hoge -c fuga
$1 : -a
$2 : -b
$3 : hoge
$4 : -c
$5 : fuga

$#

引数の数が格納されている特殊変数。

$ cat setopt.sh
#!/bin/sh

args=$(getopt ab:c: $*)

set -- $args

echo \$1 : $1
echo \$2 : $2
echo \$3 : $3
echo \$4 : $4
echo \$5 : $5
echo \$6 : $6
echo \$7 : $7

echo \$# is $#

こんな感じ。

$ ./setopt.sh -a -b hoge -c fuga
$1 : -a
$2 : -b
$3 : hoge
$4 : -c
$5 : fuga
$6 : --
$7 :
$# is 6

shift

位置パラメータの名前を変えるコマンド。

shift 2$3の位置パラメータが$3マイナス2のようにずれていく。

$ cat shiftopt.sh
#!/bin/sh

args=$(getopt ab:c: $*)

set -- $args
shift 2

echo \$1 : $1
echo \$2 : $2
echo \$3 : $3
echo \$4 : $4
echo \$5 : $5
echo \$6 : $6
echo \$7 : $7

echo \$# is $#

2つ位置パラメータをずらしたことによって-a-bが消えた。

$ ./shiftopt.sh -a -b hoge -c fuga
$1 : hoge
$2 : -c
$3 : fuga
$4 : --
$5 :
$6 :
$7 :
$# is 4

while

条件満たすまで繰り返し処理。

例は無し!

case

特定の条件の時所定の動作させる。

例は無し!

[]と[[]]

見た目にてるけど別物![]より[[]]のほうが2倍強い(?)。

  • [はtestコマンドで][の必須の引数
    • こういう既に存在する構文に別の記法を与えたものを「シンタックスシュガー」という
  • [[は複合コマンド(?)の1つでシェルの構文(testコマンドではない)
  • 違いは、[[]]は中で&&とかできる。[]はtestコマンドなので&&みたいなtestコマンドにない挙動は出来ない

渡したオプションと引数を表示するシェルスクリプト

オプションや特殊変数について見てきました。その集大成として、渡したオプションを表示するシェルスクリプトを書いてみます。

  • whileで位置パラメータの数がゼロになるまで処理を繰り返す
  • caseでgetoptで指定したオプションにマッチさせechoする
  • caseせのマッチ処理実行後shiftで位置パラメータをずらし、処理済みのマッチさせたパラメータを消す
    • 引数付きのオプションはオプションと引数で2つ分の位置パラメータを有しているので2つずらしている
  • --(getoptが自動的に最後にくっつけるオプション)が来たらshitしてcaseを抜ける
#!/bin/sh

args=$(getopt ab:c: $*)

set -- $args

while [[ $# -gt 0 ]]; do
  case $1 in
    -a) echo option is $1
        shift 1;;
    -b) echo option -b\'s argument is $2
        shift 2;;
    -c) echo option -c\'s argument is $2
        shift 2;;
    --) shift
        break;;
  esac
done

実行。

$ ./showopt.sh -a -b hoge -c fuga
option is -a
option -b's argument is hoge
option -c's argument is fuga

$ ./showopt.sh -a -c fuga
option is -a
option -c's argument is fuga

$ ./showopt.sh -b hoge -c fuga -x
getopt: illegal option -- x
option -b's argument is hoge
option -c's argument is fuga

やったぜ。

かんそう

シェルスクリプトでオプションや引数を処理するのはけっこう大変...。シェルはプログラミング言語ではないので、独特な書き方になるのですね。

おまけ

シェルスクリプトでデフォルト値有りの変数を定義するやり方。

:

何もしないコマンド。何もしないけどコマンドなので: > hoge.txtのように引数を渡せる。

${parameter:=word}

bashパラメータ。指定がない場合、wordがデフォルトの値としてparameterに入る。ほかにも幾つか種類がある。

見てく。 以下のようなスクリプトを用意。

#!/bin/sh

echo ${MY_PARAMETER:='is default parameter'}  

こんな感じで何もしないとデフォルトのis default parameter が表示される。MY_PARAMETER変数に値を入れておくと、その値が使われる。

$ ./param.sh
is default parameter  

$ export MY_PARAMETER='is specified parameter'

$ ./param.sh
is specified parameter  

なんか既視感がと思ったらこの話は既に書いていた!

: ${parameter:=word}

行頭に「:」が入ってます。

  • シェルスクリプトでparameterを変数の初期化で使いたい場合(上の例のようにechoしたりコマンドの引数として使わない時)、そのまま${parameter:=word}と書くとwordがコマンドと解釈されてしまう
  • そこで:コマンドの引数として書けばデフォルトの値設定ができる

:コマンド使わないとis default parameterがコマンド扱いになる。

見てく。スクリプト用意。

#!/bin/sh

${MY_PARAMETER:='is default parameter'}  

echo $MY_PARAMETER

実行。

$ ./param.sh
./param.sh: line 3: is default parameter: command not found
is default parameter

そこで、何もしない:コマンドを使うことでis default parameterをデフォルト値持ちの変数として定義出来る!

$ cat param.sh
#!/bin/sh

: ${MY_PARAMETER:='is default parameter'}

echo $MY_PARAMETER

$ ./param.sh
is default parameter

参考