シェルスクリプトの中では、さまざまな条件を指定して使います。今回は、if文での書き方を中心に「条件」の書き方を解説します。また、スクリプトで問題になりやすい「変数の引用符」についても取り上げます。
シェルスクリプトの中では、さまざまな「条件」を指定して使います。この“条件”とは、基本的に“コマンドの実行結果”になります。
例えば、「if コマンドA then コマンドB else コマンドC fi」のような場合は、コマンドAの実行結果がTRUE(成功)だったらコマンドBが実行され、FALSE(失敗)だったらコマンドCが実行されます。
ファイルの有無や属性、変数の内容などによって判定したい場合は、「test」コマンドを使います。
例えば、「ファイルabcが存在するかどうか」は「test -f "abc"」となります。この場合、abcという名前のファイルが存在した場合、結果はTRUEになります。同様に、「変数VARの内容がxyzかどうか」は「test "$VAR" = "xyz"」で調べることができます。ここで使用している「-f」や「"abc"」、あるいは「=」は、それぞれがtestコマンドの引数なので、空白で区切る必要があります。
testコマンドの実行結果はTRUEかFALSEという値、具体的には「0」か「1」という数値がシェルに返されるだけなので、コマンドラインで実行しても結果を見ることができません。
if文で使うと「0」はTRUE、それ以外はFALSEとして扱われるので、スクリプトの中で試すことができます(※1)。
【※1】シェルスクリプトを作らずに試してみたい場合は、連載『Linux基本コマンドTip』の「test」コマンド(基礎編)を参照。
例えば、以下のスクリプトでは、変数LANG(使用する言語が設定されている環境変数)が「C」だった場合はTRUE、それ以外の場合はFALSEに続けて、LANGの内容を出力しています。
- #! /bin/bash
- if test "$LANG" = "C"
- then
- echo "TRUE"
- else
- echo "FALSE $LANG"
- fi
- $ chmod +x testlang
- $ ./testlang
- FALSE ja_JP.utf8 #← LANGの内容がCではないのでFALSEが表示された
- $ LANG=C ./testlang #環境変数LANGにCをセットしてtestlangを実行
- TRUE #← LANGがCなのでTRUEが表示された
testコマンドは、省略して「[ ]」記号で表現できます。これは、testコマンドと同等の働きを持つ「[」コマンドで実現されています。testコマンドと「[」コマンドの違いは、「[」コマンドは最後の引数として「]」が必要という点です。
例えば、「test -f abc」ならば「[ -f abc ]」、「test "$LANG" = "C"」ならば「[ "$LANG" = "C" ]」となります。
「-f」や「=」などが、それぞれ「[」コマンドの引数であるという点は、testコマンドと同じです。従って、条件を「[ ]」で書く場合も、空白は省略できません。
以下は、先ほどの「testlang」スクリプトを「[ ]」記号で書き換えています。実行結果は同じです。
- #! /bin/bash
- if [ "$LANG" = "C" ] #← 「if test $LANG = C」を「[」コマンドで書き換えた
- then
- echo "TRUE"
- else
- echo "FALSE $LANG"
- fi
「[ -f abc ]」のような書き方は、一般に「条件式」と呼ばれています。主な条件式には以下のようなものがあります。全てtestコマンドのオプションなので、「man」コマンドを使い、「man test」で詳細を確認できます。
式 | TRUEになる条件 |
---|---|
-e ファイル名 | ファイルが存在するとき |
-f ファイル名 | ファイルが通常のファイルのとき |
-d ファイル名 | ディレクトリのとき |
-s ファイル名 | ファイルの長さが0ではない(ファイルが空ではない)とき |
-L ファイル名 | ファイルがシンボリックリンクのとき |
-h ファイル名 | ファイルがシンボリックリンクのとき(「-L」と同じ) |
ファイル1 -ef ファイル2 | ファイル1がファイル2のハードリンクのとき |
式 | TRUEになる条件 |
---|---|
-r ファイル名 | ファイルが存在し、読み出しの権限がユーザーにあるとき |
-w ファイル名 | ファイルが存在し、書き込み権限がユーザーにあるとき |
-x ファイル名 | ファイルが存在し、ファイルの実行権限がユーザーにあるとき |
-O ファイル名 | ファイルの実体の所有者が実効ユーザーIDと同じとき(※2) |
-G ファイル名 | ファイルの実体の所属グループが実効グループIDと同じとき |
-u ファイル名 | ファイルの実体にsetuidビットが立っているとき |
-g ファイル名 | ファイルの実体にsetgidビットが立っているとき |
-k ファイル名 | ファイルの実体にstickyビットが立っているとき |
ファイル1 -nt ファイル2 | ファイル1の修正時刻がファイル2の修正時刻より新しいとき |
ファイル1 -ot ファイル2 | ファイル1の修正時刻がファイル2の修正時刻より古いとき |
【※2】「実効ユーザーID(euid)」は、プログラムが動作するときの権限。プログラムを起動したユーザーのID(実ユーザーID、ruid)と等しいことが多い。
式 | TRUEになる条件 |
---|---|
-t 0 | 標準入力が端末 |
-t 1 | 標準出力が端末 |
-t 2 | 標準エラー出力が端末 |
-t 数値 | 数値番目のファイルディスクリプターが端末 |
式 | TRUEになる条件 |
---|---|
文字列1 = 文字列2 | 文字列1と文字列2が等しいとき |
文字列1 != 文字列2 | 文字列1と文字列2が等しくないとき |
-z 文字列1 | 文字列1の長さが0のとき |
-n 文字列1 | 文字列1の長さが0ではないとき |
式 | TRUEになる条件 |
---|---|
数値1 -eq 数値2 | 数値1と数値2が等しいとき(equal) |
数値1 -ne 数値2 | 数値1と数値2が等しくないとき(not equal) |
数値1 -gt 数値2 | 数値1が数値2より大きいとき(greater than) |
数値1 -ge 数値2 | 数値1が数値2より大きいか等しいとき(greater or equal) |
数値1 -lt 数値2 | 数値1が数値2より小さいとき(lesser than) |
数値1 -le 数値2 | 数値1が数値2より小さいか等しいとき(lesser or equal)) |
式 | TRUEになる条件 |
---|---|
-o オプション | シェルオプションが定義されていた(※3) |
-v 変数名 | 変数が定義されていた(※3) |
! 条件式 | 条件式が偽のとき(not) |
条件式1 -a 条件式2 | 条件式1と条件式2がどちらも真のとき(and) |
条件式1 -o 条件式2 | 条件式1と条件式2のどちらかが真のとき(or) |
( 条件式 ) | ()の中を優先して評価する(※4) |
TRUE | 常に真 |
FALSE | 常に偽 |
【※3】「-o」と「-v」は「/bin/test」では使用できない(-vはbashバージョン4.2以降)。シェルオプションについては連載『Linux基本コマンドTips』の「set」コマンドを参照。
【※4】「(」「)」を使用する際はバックスラッシュなどでエスケープする必要がある。
シェルスクリプトの場合、文字列の引用符("")は必須ではないため、変数を参照する際の引用符も必須ではありません。
しかし、原則としては付けておく方が安全です。変数を条件式の中で使う場合、引用符がないとエラーになって実行できなくなるケースがあるためです。
次の例を見てみましょう。この「quottest1」スクリプトは、変数TESTSTRに1つ目の引数をセットし、TESTSCRの値がabcだったら「OK」、それ以外の時は「NO」と表示する、という内容です。
- #! /bin/bash
- TESTSTR=$1
- if [ $TESTSTR = "abc" ]
- then
- echo "OK"
- else
- echo "NG"
- fi
ここでは、“変数TESTSTRの値がabcだったら~”というつもりで「if [ $TESTSTR = "abc" ]」と書いています。しかし、TESTSTRが定義されていない場合は実行時に「if [ = "abc" ]」となり、文法上の誤りとなり実行できません。
- $ chmod +x quottest1
- $ ./quottest1 abc
- OK #← 1つ目の引数がabcなので「OK」と表示された
- $ ./quottest1 aaa
- NG #← 1つ目の引数がabcではないので「NG」と表示された
- $ ./quottest1
- ./quottest1: line 3: [: =: unary operator expected
- NG
- #↑ 引数がないので変数TESTSTRが定義されておらず、ifの行がエラーになっている
これに対し、「if [ "$TESTSTR" = "abc" ]」としていれば、変数TESTSTRが定義されていない場合は「if [ "" = "abc" ]」となり、文法上の誤りはなくなります。
- #! /bin/bash
- TESTSTR=$1
- if [ "$TESTSTR" = "abc" ]
- then
- echo "OK"
- else
- echo "NG"
- fi
- $ ./quottest1 abc
- OK #← 1つ目の引数がabcなので「OK」と表示された
- $ ./quottest1 aaa
- NG #← 1つ目の引数がabcではないので「NG」と表示された
- $ ./quottest1
- NG #← 引数がない=1つ目の引数がabcではないので「NG」と表示された
ファイル名が入っている想定の変数に、空白入りのファイル名が入っているような場合は、引用符がないとうまく実行できません。
例えば、次の例を見てみましょう。変数FILENAMEにセットされている名前のファイルが存在しているかどうかを調べる、というスクリプトです。
使用イメージとしては、変数FILENAMEにセットするファイル名はキーボードから入力したり、ファイルから入力したりしてチェックするというものですが、ここでは、実験のために変数FILENAMEには直接「01 My Song.mp3」という文字列をセットしています。
そして、$FILENAMEの引用符の有無で実行結果がどのように変わるかを見ています。1つ目のif文は引用符なし、2つ目のif文は引用符ありです。
- #! /bin/bash
- FILENAME="01 My Song.mp3"
- if [ -f $FILENAME ] #← 1つ目のif文
- then
- echo "$FILENAME is a regular file"
- else
- echo "$FILENAME is not a regular file"
- fi
- if [ -f "$FILENAME" ] #← 2つ目のif文
- then
- echo "$FILENAME is a regular file"
- else
- echo "$FILENAME is not a regular file"
- fi
1つ目のif文は、実行時は「[ -f $FILENAME ]」が「[ -f 01 My Song.mp3 ]」となります。これは、「[ -f」に対して「01」「My」「Song.mp3」の3つの引数を渡したことになるのでエラーとなります。
これに対し、2つ目のif文は「[ -f "$FILENAME" ]」が「[ -f "01 My Song.mp3" ]」となるので、正しくファイルの存在をテストできます。
実際に実行すると以下のような結果となります。ここでは、動作テスト用に「touch」コマンドであらかじめ「01 My Song.mp3」という名前のファイルを作成しています。
- $ touch "01 My Song.mp3" #← 動作テスト用にtouchコマンドでファイルを作成
- $ chmod +x quottest2
- $ ./quottest2
- ./quottest2: line 3: [: too many argument #← 1つ目のif文がエラーになった
- 01 My Song.mp3 is not a regular file #← エラーのためelse以下が実行されている
- 01 My Song.mp3 is a regular file #← 2つ目のif文の実行結果
引数なしで実行すると1つ目のif文がエラーになるため、エラーメッセージが表示されています。さらに、ifの判定もFALSEとなっているため、「~is not a regular file」というメッセージが表示されています。
これに対し2つ目のif文は正しく実行されており、「~is a regular file」というメッセージが表示されています。
コマンドラインでも事情は同じです。先ほどの例と同じように、環境変数に空白入りのファイル名をセットして、「ls」コマンドの引数として実行してみましょう。
変数FILENAMEに「01 My Song.mp3」をセットし、(1)「ls $FILENAME」のように引用符なしで実行した場合と、(2)「ls "$FILENAME"」と引用符ありで実行した場合を比較しています。
- $ FILENAME="01 My Song.mp3"
- $ ls $FILENAME #← (1)引用符なしで実行
- ls: 01: No such file or directory
- ls: My: No such file or directory
- ls: Song.mp3: No such file or directory
- (lsコマンドは「01」と「My」と「Song.mp3」という3つの引数を受け取っている)
- $ ls "$FILENAME" #← (2)引用符ありで指定
- 01 My Song.mp3
- (lsコマンドは「01 My Song.mp3」を受け取った)
西村 めぐみ(にしむら めぐみ)
PC-9801NからのDOSユーザー。PC-486DX時代にDOS版UNIX-like toolsを経てLinuxへ。1992年より生産管理のパッケージソフトウェアの開発およびサポート業務を担当。著書に『図解でわかるLinux』『らぶらぶLinuxシリーズ』『Accessではじめるデータベース超入門[改訂2版]』『macOSコマンド入門』など。2011年より、地方自治体の在宅就業支援事業にてPC基礎およびMicrosoft Office関連の教材作成およびeラーニング指導を担当。
Copyright © ITmedia, Inc. All Rights Reserved.
Linux �� OSS 險倅コ九Λ繝ウ繧ュ繝ウ繧ー