特殊変数

bashでは、通常の変数以外に最初から用意されている「特殊変数」というものが存在します。
既に説明した、コマンドの戻り値を取得する「$?」などが特殊変数です。
今回は、使いこなせればかなり便利になる、特殊変数の扱い方を学びましょう。

 

・特殊変数一覧


○位置パラメータ
$0      シェルスクリプト名を表示
$1~$9  シェルスクリプトのオプションを表示(数値は位置を現す)

○引数(オプション)関連
$#      現在のプロセスに与えられた引数の数を表示
$@      現在のプロセスに与えられた引数を表示
$*      現在のプロセスに与えられた引数を表示
$-      シェルを起動する際に指定されていたオプションを表示

○PID(プロセスID)関連
$?      直前のコマンドの終了ステータスを表示
$$      現在のシェルのプロセスIDを表示
$!      最期にバックグラウンドで実行されたコマンドのプロセスIDを表示

○その他、シェルスクリプトで使いそうなもの
IFS     フィールドの区切り文字
PWD     カレントディレクトリを表示
PPID    親プロセスのプロセスIDを表示
HOME    ホームディレクトリを表示

といった所です。
基本的にbashスクリプトに記述する物はbashのコマンドラインで行う事と一緒ですので、「PATH」や「PS1」などの変数も、もちろん扱えます。ただ、作成したシェルスクリプトの用途にもよりますが、使う事は稀ですので、今回の説明では省かせて頂きます。

では、順に説明していきましょう。

○位置パラメータ
シェルスクリプトの引数を取得する時に使う「位置パラメータ」も特殊変数です。
既に説明しましたが、再度説明すると、こんな感じになります。

「test01.sh」というシェルスクリプトに「aaa」,「bbb」,「ccc」という3つのオプションを付加して起動する場合、以下の様にして実行する事になります。


# ./test01.sh aaa bbb ccc

この時のシェルスクリプト名や引数は、以下の位置パラメータで取り出せます。


      $0       $1  $2  $3
# ./test01.sh aaa bbb ccc

「$0」はスクリプト名で、「$1」からはオプションが割り当てられます。
ちなみに、オプションが9個以上あった場合はどうなるでしょうか。


      $0       $1  $2  $3  $4  $5  $6  $7  $8  $9 $10 $11
# ./test01.sh aaa bbb ccc ddd eee fff ggg hhh iii jjj kkk

10個以上のオプションも、上記の様に順に位置パラメータで取得できます。
ですが、ここで問題があります。

10個目の位置パラメータは「$10」になるのですが、2桁になった場合、「{}」で括らなければ値を取り出せません。
これは以前の変数の説明でも取り上げましたが、「$10」という位置パラメータは、「$1」という位置パラメータと、その直後に「0」が付いているものと解釈されてしまうからです。
ですので、2桁以上の位置パラメータを扱う場合、「${10}」という形で括ってやる必要があります。
恐らく、10個以上ものオプションを必要とするスクリプトを書く事は稀だとは思いますが、心の隅にでも留めて置いた方が良いでしょう。

○引数(オプション)関連
引数のオプションを確認する為に、以下の様なスクリプトを作成してみました。

#!/bin/bash

## 引数確認スクリプト

## 引数の個数を表示
echo "引数の個数を表示"
echo $#

## 引数を全て表示
echo "引数を全て表示(@)"
echo $@
echo "引数を一つずつ表示(@)"
for i in "$@"
do
        echo $i
done

echo "引数を全て表示(*)"
echo $*
echo "引数を一つずつ表示(*)"
for i in $*
do
        echo $i
done

## シェル起動時に指定されていたオプションを表示

echo "シェル起動時に指定されていたオプションを表示"
echo $-

exit 0

引数をつけて実行するとこうなります。

# ./option01.sh aaa bbb ccc ddd
引数の個数を表示
4
引数を全て表示(@)
aaa bbb ccc ddd
引数を一つずつ表示(@)
aaa
bbb
ccc
ddd
引数を全て表示(*)
aaa bbb ccc ddd
引数を一つずつ表示(*)
aaa
bbb
ccc
ddd
シェル起動時に指定されていたオプションを表示
hB
# ./option01.sh "aaa" "bbb ccc" "ddd"
引数の個数を表示
3
引数を全て表示(@)
aaa bbb ccc ddd
引数を一つずつ表示(@)
aaa
bbb ccc
ddd
引数を全て表示(*)
aaa bbb ccc ddd
引数を一つずつ表示(*)
aaa
bbb
ccc
ddd
シェル起動時に指定されていたオプションを表示
hB
# 

上記のスクリプト実行結果のポイントはいくつかあります。
上の方は、引数を「4つ」付けました。「aaa」と「bbb」と「ccc」と「ddd」です。
下の方は、引数を「3つ」付けました。「aaa」と「bbb ccc」と「ddd」です。
下のほうはスペース混みの引数を扱っています。

「$#」とい特殊変数は、正しく引数の個数を判断して出力しているのが判りますでしょうか?
ところが、「$@」と「$*」という特殊変数は全てのオプションを出力してますが、一つずつ表示した際に差が出ています。これは次の「配列の基本」で詳しく説明しますが、「$*」は同じく特殊変数の「IFS」に定義されているフィールド区切り文字で、付加されたオプションを区切って判断します。
正しいオプションの個数を知りたい場合は「$@」を使うべきでしょう。

「$-」という特殊変数は、その時起動しているプロセス(要はbash)についているオプションを表示します。
bash付けるオプションと言うのは「set」コマンドで定義しているオプションの事です。
入門編で少し扱いましたが、デバッグ時に付けた「-x」というのもbashに対するオプションです。
試しに以下のスクリプトを実行してみましょう。

#!/bin/bash

## シェル起動時に指定されていたオプションを表示
echo "シェル起動時に指定されていたオプションを表示"
echo $-
echo ""

## デバッグモードON
echo "デバッグモードON"
set -x

echo "再度オプションを表示"
echo $-
echo ""

## デバッグモードOFF
echo "デバッグモードOFF"
set +x

echo "再々度オプションを表示"
echo $-

 

# ./option02.sh 
シェル起動時に指定されていたオプションを表示
hB

デバッグモードON
+ echo $'\345\206\215\345\272\246\343\202\252\343\203\227\343\202\267\343\203\247\343\203\263\343\202\222\350\241\250\347\244\272'
再度オプションを表示
+ echo hxB
hxB
+ echo ''

+ echo $'\343\203\207\343\203\220\343\203\203\343\202\260\343\203\242\343\203\274\343\203\211OFF'
デバッグモードOFF
+ set +x
再々度オプションを表示
hB
# 

この様に、bash自体についているオプションを表示できます。

○PID(プロセスID)関連
プロセスID関連の特殊変数は使わなそうで結構使います。
「$?」なんかはコマンドの終了コードを取得する為に使うので、まだ判りますが、他の二つはどの様にして使うのでしょうか?

「$$」という特殊変数はロックファイルや、そのbashスクリプトで一時的に使うファイルなどに実行しているスクリプト自身のPIDを利用して管理したりします。
終了時にそのPIDで判別して消したりする訳です。

「$!」は現在実行しているスクリプトからバックグラウンドで実行したコマンドのPIDを取得します。
一度実行すると数分かかるコマンドなどを実行する際は、バックグラウンドで実行させて、そのバックグラウンド処理のPIDを監視してコマンド終了を判定したりします。
バックグラウンドで処理してる間にフォアグラウンドで別処理をやる事が出来る訳ですね。

本当なら、ここで実際にスクリプトを作って説明するべきなのですが、複数スクリプトを駆使して説明する必要があり、あまり初級的ではないと思うのでここでは上記の説明で軽く流します。
来るべき時が来たらしっかりと説明させてもらいます。(恐らく実践編での説明になると思います)

○その他、シェルスクリプトで使いそうなもの

「IFS」変数は、上記で軽く触れましたが、「フィールドセパレータ」が格納されている変数です。
デフォルトではスペースが格納されています。
このIFS変数を変更する事でCSVファイルなどのカンマ区切りファイルの読み込みなどで威力を発揮します。

こんな使い方をします。

#!/bin/bash

TESTSTR="aaa,bbb,ccc"

## TESTSTRを表示
echo "TESTSTRを表示"
for i in ${TESTSTR}
do
        echo $i
done
echo ""

## IFS変数のバックアップ
OLDIFS=$IFS

## IFS変数の変更
echo "IFS変数の変更(スペースからカンマへ)"
IFS=,

## TESTSTRを表示(今度はカンマ区切りの為一つずつ取り出せる)
echo "TESTSTRを表示(今度はカンマ区切りの為一つずつ取り出せる)"
for i in ${TESTSTR}
do
        echo $i
done
echo ""

## IFS変数を戻す
echo "IFS変数を戻す"
IFS=${OLDIFS}

echo "TESTSTRを表示(バックアップしたIFS変数に戻したので最初の状態と一緒になる)"
for i in ${TESTSTR}
do
        echo $i
done
echo ""

exit 0

実行すると以下の様になります。

# ./ifs01.sh aaa,bbb
TESTSTRを表示
aaa,bbb,ccc

IFS変数の変更(スペースからカンマへ)
TESTSTRを表示(今度はカンマ区切りの為一つずつ取り出せる)
aaa
bbb
ccc

IFS変数を戻す
TESTSTRを表示(バックアップしたIFS変数に戻したので最初の状態と一緒になる)
aaa,bbb,ccc

# 

スクリプトを書く際、IFS変数が標準であるスペース区切りを想定して書かれている事が普通ですので、「IFS」変数を変更する時は、上記の様に処理後に古いIFS変数に戻した方がよいでしょう。

「PWD」は現在のカレントディレクトリを表示します。
注意しなければいけないのは、「スクリプトがあるディレクトリ」ではなく、「スクリプトを実行した時にいるディレクトリ」です。
スクリプトが存在するディレクトリを取得する場合は「dirname」コマンドに「$0」を付けて(スクリプト名を渡して)スクリプト内で実行しましょう。

ただ、これも実行しているディレクトリからの相対パスになります。

「PPID」は実行しているプロセスの「親」プロセスIDを表示します。これも「$!」や「$$」と同じく複数のスクリプトを使用する大規模なスクリプトでは結構使ったりします。
前述の通り初級的ではないので、これも実例はここでは説明しません。

「HOME」は実行しているユーザのホームディレクトリを表示します。
「~(チルダ)」と同じですが、スクリプトで使用する場合は、チルダだと見づらいので、こちらを使用した方がいいでしょう。