シェルスクリプトの基礎 - コマンドライン引数

提供:MochiuWiki - SUSE, Electronic Circuit, PCB
ナビゲーションに移動 検索に移動

概要

コマンドライン引数とは、シェルスクリプト実行時に渡される値のことである。

引数は実行時にシェルスクリプト内で参照され、シェルスクリプトの実行結果に影響を与える。
引数はパラメータと呼ばれることもある。

command 引数1 引数2 … 引数n


引数処理に使用する変数
シェルスクリプト実行時に指定された引数は、位置パラメータと呼ばれる特殊な変数に自動的に設定される。
シェルスクリプト内からは、この変数を参照することで、引数を処理することが可能になる。
位置パラメータ以外にも、引数の処理に関連した特殊な変数がいくつかあり、 これらを組み合わせて使用することによって柔軟に引数を処理することが可能になる。

特殊変数名 自動的に設定される値
$# 実行時に指定された引数の数を表す変数。
「$ ./sample.sh AAA BBB CCC」を実行した場合、sample.sh内で変数$#を参照するとその値は3となる。
$@ シェルスクリプト実行時またはsetコマンド実行時に指定された全パラメータが設定される変数である。
変数$*と基本的に同じだが、""で囲んだ時の動作が異なる。
$* シェルスクリプト実行時またはsetコマンド実行時に指定された全パラメータが設定される変数である。
変数$@と基本的に同じだが、""で囲んだ時の動作が異なる。
$0 実行時のコマンド名が設定される変数である。
./sample.sh(/home/user/sample.sh)を実行した場合、/home/user/sample.shが設定される。
$1 - $n シェルスクリプト実行時に指定した各パラメータの値が設定される変数である。
1番目に指定した引数は$1、2番目に指定した引数は$2、n番目に指定した引数は$nに設定される。
10番目以降の引数参照時は${10}のように{}を使用する必要がある。
これは、$10を$1 "0"のように、シェルに誤った解釈をされることを防ぐためである。



パラメータ取得の基本

シェルスクリプト実行時にコマンドラインで渡したパラメータは、以下のような特殊変数で参照できる。

$1 – 第1パラメータ
$2 – 第2パラメータ
$3 – 第3パラメータ


パラメータの取得には、$1〜$9が使用できる。
10番目以降のパラメータを参照する場合は、${10}のように数値を{}で囲めば参照できる。

 #!/bin/sh
 
 echo '$1 =' "$1"
 echo '$2 =' "$2"
 echo '$3 =' "$3"


# 実行
./sample.sh AAA BBB "CCC  DDD"

# 出力
$1 = AAA
$2 = BBB
$3 = CCC  DDD


パラメータを指定しない場合は、対応する変数は空になる。

# 実行
./sample.sh AAA BBB

# 出力
$1 = AAA
$2 = BBB
$3 =


echoの引数として変数を渡す場合は、"$1"のようにダブルクォートで囲む癖をつける。
$1のようにそのまま記述すると、値として連続するスペースが含まれていた時に1つのスペースに纏められてしまう。

以下に、コマンドライン引数のを使用した応用例を示す。
シェルスクリプトが2つ以上のパラメータを必要としている場合、シェルスクリプトの先頭で以下のように確認する。

 # 例 : 2つ以上のパラメータが必要な場合
 #!/bin/bash
 
 if [ $# -lt 2 ]; then
    echo "Usage: $(basename $0) <file1> <file2>" >&2
    exit -1
 fi


以下の例に、様々なコマンドライン引数の確認方法を示す。

 # 例 : 1つ以上のコマンドライン引数が必要な場合
 #!/bin/bash
 
 if [ ! "$1" ]; then
    echo "Usage: $(basename $1) <file>" >&2
    exit -1
 fi


 # 例 : 1つ以上の引数が必要かつ特定のファイルが存在する場合
 #!/bin/bash
 
 # 引数の確認
 if [ ! "$1" ]; then
    echo "Usage: $(basename $0) <file>" >&2
    exit -1
 fi
 
 # ファイルの存在の確認
 if [ ! -f "$1" ]; then
    echo "$1 is not found" >&2
    exit -1
 fi



デフォルト値を指定する

パラメータが指定されなかった時のために、デフォルト値を指定しておくこともできる。
下記の例では、1番目、2番目、3番目のパラメータの初期値を、それぞれ、100、200、300 に設定している。

 #!/bin/sh
 
 echo '$1 =' "${1:-100}"
 echo '$2 =' "${2:-200}"
 echo '$3 =' "${3:-300}"


# 実行
./sample.sh 500

# 出力
$1 = 500
$2 = 200
$3 = 300



パラメータを反復処理で順番に処理する($@ と $*)

for文を使用する方法

シェルスクリプト実行時に渡されたパラメータは、特殊変数$@を使用して参照することができる。
以下の例では、for文を使用して、$@の要素を1つずつ抽出して処理している。(カウンタ変数$countをインクリメントしながらループしている)

 #!/bin/sh
 
 count=1
 for arg in "$@"
 do
    echo "$count: $arg"
    let count=$count+1
 done


# 実行
./sample.sh AAA BBB CCC

# 出力
1: AAA
2: BBB
3: CCC


また、特殊変数の指定部分in "$@"は省略して記述することができる。
明示的にin "$@"を記述する場合は、$@の部分をダブルクォーテーションで囲むことに注意する。

 #!/bin/sh
 
 count=1
 
 for arg
 do
    echo "$count: $arg"
    let count=$count+1
 done


while文を使用する方法

shiftコマンドを実行することで、$1〜$9に格納されたパラメータを1つずつ前にシフトすることができる。
shiftコマンドを実行する度に、$1に格納されていたパラメータは破棄され、パラメータ数を表す$#の値が1つずつ減っていく。

下記の例では、パラメータ数($#)が1以上の間、処理を続けるwhile文を定義している。
$1はパラメータの最初の要素を参照する変数であるが、直後のshiftによってパラメータを1つずつシフトしているので、
結果として、全てのパラメータを順番に参照することができる。

 #!/bin/sh
 
 count=1
 
 while [ "$#" -ge "1" ]
 do
    echo "$count: $1"
    shift
    let count=$count+1
 done


# 実行
./sample.sh AAA BBB CCC

# 出力
1: AAA
2: BBB
3: CCC



$@ と $* の違い

$@と似た特殊変数に$*が存在する。どちらもパラメータ全体を表す特殊変数であるが、
$@が各パラメータを個別に保持しているのに対し、$*は全てのパラメータを結合した1つの文字列である。
それぞれ、ダブルクォートで囲んで参照した場合と、囲まずに参照した場合の展開方法も含めて理解する。

 #!/bin/sh
 
 echo '=== Pattern 1 ==='
 for arg in "$@"
 do
    echo "$arg"
 done
 
 echo -e '\n=== Pattern 2 ==='
 for arg in $@
 do
    echo "$arg"
 done
 
 echo -e '\n=== Pattern 3 ==='
 for arg in "$*"
 do
    echo "$arg"
 done
 
 echo -e '\n=== Pattern 4 ==='
 for arg in $*
 do
    echo "$arg"
 done


# 実行
./sample.sh "100 200" "CCC DDD"

# 出力
=== Pattern 1 ===
100 200
CCC DDD

=== Pattern 2 ===
100
200
CCC
DDD

=== Pattern 3 ===
100 200 CCC DDD

=== Pattern 4 ===
100
200
CCC
DDD


Pattern 1の方法は、"$@"と指定することで、内部的には下記のように各要素を""で囲んで指定されたものとみなされるため、
正しく2つのパラメータとしてハンドルされる。

for arg in "100 200" "AAA BBB"; ...


Pattern 2の方法は、$@を""で囲まずに渡すと、下記のように各要素が展開して指定されたものとみなされる。
for文はスペース区切りで各要素が渡されていると判断するため、結果的に4つの要素として処理される。

for arg in 100 200 AAA BBB; ...


Pattern 3の方法は、"$*"と指定すると、下記のように全てのパラメータをスペースで繋げて、
更に全体を""で囲んで指定されたものとして処理される。
つまり、for文は、"100 200 AAA BBB"という文字列の1回分しか回らない。

for arg in "100 200 AAA BBB"; do


Pattern 4の方法は、$*と指定した場合は、Pattern 2と同様に、全ての要素がスペース区切りで渡されたとみなされる。(4つの要素として処理される)

for arg in 100 200 AAA BBB; ...



ラッパー

シェルスクリプトの実行時の引数を、そのまま別のコマンドに渡す場合、"$@"を使用する。
このような用途で記述されているシェルスクリプトのことをラッパーと呼ぶ。

 #!/bin/sh
 
 ./do_command.sh "$@"



shiftコマンドによる引数の操作

shiftコマンドは、シェルスクリプトに渡した引数を順番に処理することができる。

上記のセクションに記述した通り、シェルスクリプトに渡した引数は、$1や$2等の特殊変数として扱われる。
shiftコマンドを使用すると、この特殊変数を順番にずらすことができる。
したがって、shiftコマンドを実行すると、$2の値は$1に代入され、$3の値は$2に代入されるといった変数の値をシフトすることができる。

以下の例では、shiftコマンドを実行して$1の値を出力し続けている。
shiftコマンドにより、3つの引数が順番にシフトされている。

 #!/bin/sh
 
 # $1が空になるまで繰り返し
 while [ -n "$1" ]
 do
    echo "param = $1"
    shift
 done

 # 実行
 ./test-shift.sh a b c
 
 # 出力
 param = a
 param = b
 param = c



コマンドライン引数の解析 : getopts

複雑なコマンドライン引数の指定する場合、全てのコマンドライン引数の妥当性の確認等、解析にはかなりの労力を要する。
そのため、getoptsコマンドを使用することで、コマンドライン引数の解析を容易に行うことができる。

以下の例のように、コマンドライン引数の解析には、while文getoptsコマンドを組み合わせて使用する。
getoptsコマンドの第1引数には、シェルスクリプトに指定可能な引数名を指定して、その引数が値を要する場合は:(コロン)を付加する。
第2引数には、解析に使用する任意の変数名を指定する。あくまでも変数名であるので、$を付ける必要はない。

以下の例において、xxx.sh -a -b "BBB" -c "CCC"といったコマンドを実行する時、"BBB"および"CCC"等の値は変数OPTARGに代入される。

 #!/usr/bin/env sh
 
 while getopts ab:c: OPT
 do
    case $OPT in
       "a" )
          FLG_A="TRUE"
          ;;
       "b" )
          FLG_B="TRUE"
          VALUE_B="$OPTARG"
          ;;
       "c" )
          FLG_C="TRUE"
          VALUE_C="$OPTARG"
          ;;
    esac
 done


以下の例では、getoptsコマンドを利用したシェルスクリプトを作成している。

 # param_getopts.shファイル
 
 #!/usr/bin/env sh
 
 CMDNAME=`basename $0`
 
 while getopts ab:c: OPT
 do
    case $OPT in
       "a" )
          FLG_A="TRUE"
          ;;
       "b" )
          FLG_B="TRUE"
          VALUE_B="$OPTARG"
          ;;
       "c" )
          FLG_C="TRUE"
          VALUE_C="$OPTARG"
          ;;
       * )
          echo "Usage: $CMDNAME [-a] [-b VALUE] [-c VALUE]" 1>&2
          exit 1
          ;;
    esac
 done
 
 if [ "$FLG_A" = "TRUE" ]; then
    echo '"-a"オプションが指定されました'
 fi
 
 if [ "$FLG_B" = "TRUE" ]; then
    echo '"-b"オプションが指定されました'
    echo "値は$VALUE_Bです"
 fi
 
 if [ "$FLG_C" = "TRUE" ]; then
    echo '"-c"オプションが指定されました'
    echo "値は$VALUE_Cです"
 fi
 
 exit 0
 
 # 実行
 ./param_getopts.sh  # 引数の指定が無い場合、何も出力せずに終了する
 
 ./param_getopts.sh -a
 
 ./param_getopts.sh -a -b "123456"
 
 ./param_getopts.sh -ab "123456"  # -abのように複数のオプションをまとめて指定することも可能
 
 ./param_getopts.sh -a -b "123456" -c  # -cオプションに値が指定されていないためエラー
 
 ./param_getopts.sh -a -b "123456" -c "ABCDEF"
 
 # 出力
 "-a"オプションが指定されました
 
 "-a"オプションが指定されました
 "-b"オプションが指定されました
 値は123456です
 
 "-a"オプションが指定されました
 "-b"オプションが指定されました
 値は123456です
 
 ./param_getopts.sh: option requires an argument -- c
 Usage: param_getopts.sh [-a] [-b VALUE] [-c VALUE]
 
 "-a"オプションが指定されました
 "-b"オプションが指定されました
 値は123456です
 "-c"オプションが指定されました
 値はABCDEFです



getoptsコマンドとshiftコマンドの組み合わせ

複雑なコマンドライン引数の指定が必要なシェルスクリプトの場合、
引数の解析後に不要となった引数に対して、shiftコマンドで切り捨てることができる。

以下の例では、変数OPTINDから1を引いた分だけshiftコマンドを実行することにより、不要になった引数を切り捨てている。

 shift $(expr $OPTIND - 1)


変数OPTINDは、getoptsコマンドがオプションを順番に処理するために使用するオプション位置を示すカーソルのような働きをする変数である。
getoptsコマンドの終了後は、変数OPTINDの値がオプションの直後を指し示しているため、
この値から1減算した分だけshiftすることにより、オプション部分を切り捨てることができる。

以下の例では、シェルスクリプト内において、getopts abc OPTと記述されている場合、getoptsコマンドの終了後の変数OPTINDの値は、
<パラメータ>の位置を指し示している。
その値から1減算してshiftするということは、-a -b -cを切り捨てることになる。

xxx.sh -a -b -c "<パラメータ>"


以下の例では、オプション部分を切り捨てるシェルスクリプトを作成している。
どのようなコマンドライン引数であっても、オプション部分のみが確実に切り捨てていることが確認できる。

 #!/usr/bin/env sh
 
 # -a, -b, -cオプションを指定可能とする
 while getopts abc OPT
 do
    # ...解析処理は省略
 done
 
 # オプション部分を切り捨てる
 shift $(expr $OPTIND - 1)
 
 # オプション部分を切り捨てたため、変数$1には先頭の引数が設定されている
 echo "引数に\"$1\"が指定されました"
 
 exit 0
 
 # 実行
 ./param_optind.sh -abc "PARAMETER"
 
 ./param_optind.sh -ab "PARAMETER"
 
 ./param_optind.sh -ac "PARAMETER"
 
 ./param_optind.sh "PARAMETER"
 
 ./param_optind.sh -a -b -c "PARAMETER1" "PARAMETER2"
 
 # 出力
 引数に"PARAMETER"が指定されました
 
 引数に"PARAMETER"が指定されました
 
 引数に"PARAMETER"が指定されました
 
 引数に"PARAMETER"が指定されました
 
 引数に"PARAMETER1"が指定されました