Shellスクリプト

【Shellスクリプト】制御文(条件分岐、繰り返しループ)を理解しよう!

2020年4月16日

実際にスクリプトを書いていると同じ処理を何度もしないといけないことってありますよね?そのときに、条件分岐と繰り返し処理を組み合わせれば、特定の処理を指定した回数、処理してくれるようにすることが可能です。

一度覚えてしまえば便利なので、この機会に条件分岐と繰り返しをマスターしましょう。

それでは、条件分岐から説明していきます。

制御文とは?

シェルでは制御構造コマンドとして、if、case、for、whileという4つの制御文が利用できます。
実際には「select」や「until」等もありますが、使用頻度は非常に低い為、ここでは割愛します。

「if文」や「case文」はシェル以外にも、C言語やjava言語など、他のプログラミング言語でもよく使用されてる条件分岐構文になります。また「for文」や「while文」についても、よく使用される繰り返しループ文です。

条件分岐とは?

条件分岐とは、「Aという条件を満たした場合に繰り返し処理を実行し、Bという条件になったら処理を辞める」といった条件式を指しています。中でも「if文」と「case文」は、よく用いられる条件分岐構文になります。

if文とは?

if文は実行結果「真(true)」「偽(false)」によって分岐する構文です。例えば、「文字列Aと文字列Bは一致しているか?」といった考え方です。「if」文は、ほとんどの場合「test」コマンドと組み合わせて用いられます。

また、多少処理的に複雑になりますが、「ネスト(入れ子)」構造にすることも可能です。

「if-elif-else」文

条件分岐を複数に分けたい場合は「if-elif-else」文で制御します。「elif」は、単純に条件の数分指定可能です。「それ以外(すべてに該当しない場合)」の条件分岐を指定するには「else」を用います。

if-elif-else

上記のイメージを元にした書式を下記に記述します。

コマンドの書式

if  [ 条件式 ];  then
    アクション1
elif  [ 条件式 ];  then
    アクション2
else
    アクション3
fi

実際に「if」文を実行してみます。ここでは、引数に指定した"test.txt"の属性が、「ファイルなのか」「ディレクトリなのか」、もしくはどちらでもない「オブジェクトなのか」を判定するシェルスクリプトを作成してみます。

下記のロジックをコンソールへ入力してみましょう。

# 予め"test.txt"ファイルを作成しておく
touch test.txt

str1="これはファイルです。"
str2="これはディレクトリです。"
str3="不明なオブジェクトです。判定できません。"

judge_type (){
    var=${1}
    if [ -f ${var} ]; then
        echo " [${var}] : ${str1}"
    elif [ -d ${var} ]; then
        echo " [${var}] : ${str2}"
    else
        echo " [${var}] : ${str3}"
    fi
}

下記は実行結果です。

引数で指定した"test.txt"の属性は、ファイルであることが分かりました。

以降で説明する「if-else」文も「if」文も考え方は全く同じです。分岐するアクションの数が異なるだけです。

「if-else」文

単純に2者択一等の「真(true)」「偽(false)」比較の場合には、「if-else」文を使用します。

if-else

書式は、下記の様になります。下記の書式では、[ ]の中の条件式の結果が真となる場合にアクション1を実行し、それ以外の場合はアクション2を実行します。

コマンドの書式

if  [ 条件式 ];  then
    アクション1
else
    アクション2
fi

「if」

単に「真(true)」の時のみの判定も可能です。

if

上記イメージの判定は、「真(true)」の時のみ、アクション1を実行します。それ以外はスルーします。

コマンドの書式

if  [ 条件式 ];  then
    アクション1
fi

case文とは?

case文は1つの変数だけで複数の条件分岐を設定出来るのが特徴です。変数の値がパターン条件にマッチした時にマッチした条件の処理を実行します。

ちなみに、いくつか注意点がありますので下記のリストにまとめてみました。

  • 処理の終了には「;;」を記述すること。
  • case文の終わりにはesacと記述すること。
  • 変数が全ての条件式にマッチしなかったときはなにも処理されません。

注意点に挙げるほどではないですが、複数の処理をさせたい場合は改行をすることで後で読み返したときに可読性が高くなります。

コマンドの書式

case  値  in
    パターン条件1)  処理1 ;;
    パターン条件2)  処理2 ;;
    パターン条件3)  処理3 ;;
esac

「Case」文も「if」文同様に、「ネスト(入れ子)」構造にすることが可能です。

キーボード入力を受け付けるロジックになるため、少し実践的に「シェルスクリプト」ファイルとして作成します。判定には「Case」文を使用します。

これから作成する「シェルスクリプト」は、キーボードから入力された引数が「アルファベット」「数値」「それ以外」のいずれかに該当するモノかを判定するロジックです。

str1="入力されたのはアルファベットですね。"
str2="入力されたのは数字ですね。"
str3="入力された文字は不明な文字種です。"

read -p "文字を入力してください。:" input

case "${ input }" in
    [a-zA-Z] ) echo "${ str1 }" ;; 👈(1)
         [0-9] ) echo "${ str2 }" ;; 👈(2)
               * ) echo "${ str3 }" ;; 👈(3)
esac

1:[a-zA-Z]は、変数の値がアルファベットの小文字と大文字のときにマッチします。
2:[0 - 9]は、変数の値が数字の0~9のときにマッチします。
3:*は、すべての文字列にマッチします。

パターン条件に記述可能なワイルドカードは、以下の意味を持ちます。

*   :文字列全部(文字がなくても)に合致する
?   :1文字に合致する
[...]     :[ ]の中に含まれる文字のどれか1つ
[!...]       :[ ]に含まれない文字に合致する
                複数の条件を指定することも可能です。

ちなみに、下記のコマンドでプロンプトからの引数入力を要求します。

# プロンプトからの引数入力を要求
read -p "文字を入力してください。:" input

Case分に対応した記述例)

[ <BASE_DIR>/case_pattern.sh ]

Case分に対応した実行例)

さらに詳しく

  • *(ワイルドカード)のみを条件とした場合は全ての文字列をマッチします。
    *は、すべての文字列にマッチします。
    abc*:「abc」で始まるすべての文字列にマッチします。
    a*bc :「a」ではじまり「bc」で終わる文字列すべてにマッチします。

  • [ - ]で設定した範囲の文字や数字にマッチします。
    具体的には下記のように範囲指定をしていします。
    [a - z]:変数の値が小文字のアルファベット「a~z」のときにマッチします。
    [A - Z]:変数の値が大文字のアルファベット「A~Z」のときにマッチします
    [a-zA-Z]は、変数の値がアルファベットの小文字と大文字のときにマッチします。
    [0 - 9]は、変数の値が数字の0~9のときにマッチします。

  • OR条件
    a || A):変数の値がaまたはAの時にマッチします。

|(パイプ)」は「or (または)」の意味で、パターンの一番最後には「何も合致しなかった場合」という意味で「 * ) 」とするのが一般的です。

*(アスタリスク)」は「すべてに合致する」という意味の特殊文字なので、上から順番にパターンを比較していき、すべてに合致しなかった場合、必ず一番最後の「*(アスタリスク)」でキャッチされ、処理を実行することになります。

ループ処理とは?

ループ処理とは、簡潔に説明すると、処理を繰り返すための制御文です。処理を繰り返すための制御文には、主に「for」文と「while」文が用いられます。ただし、シェルスクリプトでは複雑な繰り返し処理はwhile文を使用し、簡単な繰り返しの制御にfor文を用いることが多いです。

ループ処理は、ほとんどの場合「if」と同時に用いられ、「test」コマンドを使用してその後の挙動を指定します。

下記に「for」文及び、「while」文について、それぞれ説明します。

for文とは?

for文は、for直後の変数名に、in直後に羅列したワードリスト( 値 1 値 2…)を順番に代入しながら、「do」から「done」によって囲まれた処理を実行するループする構文です。

C言語やjavaではカウンタを使ってカウンタの分だけ繰り返しますが、シェルスクリプトではワードリストを使って繰り返し処理をするという違いがあります。ただし、bashの場合はC言語やjavaと同じような記述にも対応しています。

コマンドの書式

for 変数 in ワードリスト
do
    処理...
done

for文に対応した記述例)

for i in a b c d
do
if [ "${i}" = "b" ]; then
    echo ${i}
fi
done

実際にfor文に対応したシェルスクリプトを記述記述してみます。これから作成するシェルスクリプトは、ワードリスト(「a」「b」「c」「d」)の順にループ処理を行い、変数の値が「b」の時のみ、変数の値をコンソールへ出力します。

for文の実行例)

結果としては表示されてはいませんが、変数の値が「b」の時、コンソールへの出力処理が終了した後も、ループ処理は(変数「d」が終了するまで)続いています。ハッキリいって無駄な処理です。

そこで使用されるのが「break」コマンドと「continue」コマンドです。

「continue」で処理のスキップが行え、「break」コマンドで、ループ処理から抜け出すことが可能です。「break」コマンドと「continue」コマンドは、予め組み込みコマンドとして、Linuxへ標準で用意されているコマンドです。

ベテランエンジニアとエンジニア初心者の差は、無駄な処理の後始末に顕著に表れます。ループ処理の実装時、それ以降必要のないロジックは、「スキップ」、もしくは「ループから抜ける」等、なるべく無駄を省く実装を心がけましょう。

詳しくは、下記のリンクページで「break」コマンドと「continue」コマンドを参照してください。

while文とは

while文は、条件文の実行結果が真であるかぎり、ループ中の処理を繰り返し実行する構文です。一番最後の終了コードが「0」か「0以外」かを判定し、「0」なら「do」から「done」によって囲まれた処理を実行するループ構文です。

コマンドの書式

while コマンドリスト
do
    処理...
done

while文に対応した記述例)

count=1
while [ ${count} -lt 3 ]
  do
  echo ${count}
  count=$(expr ${count} + 1)
done

while文に対応したスクリプトの実行例)

1行目:「count」という変数を用意し、これに数値の「1」を代入します。
2行目:「count」の値が3より小さいかどうかを調べます。
4行目:この結果は真(3より小さい)ので、echoコマンドでその値「1」を出力します。
5行目:「count」の値に1を加えます。これで1回目のループが終了し、判定文のところに戻ります。
2行目:「count」の値は2になっていますが、まだ3より小さいのでもう一度doの中の処理を実行します。
4-5行目:echoで2を表示し、1を加えます。2回目のループが終了し、whileの判定文に戻ります。
2行目:「count」は3になっているため、「3より小さい」という条件に対しては偽となり、0以外(false)を返します。この時点で、whileが終了します。

「while」文も「for」文同様に、「break」コマンドと「continue」コマンドを使用可能です。詳しくは「for」文説明欄を参照してください。

よく読まれている記事

1

独立前は、誰もが不安でいっぱい。近い将来、フリーランスとして独立を考えている方が独立初期に躓かないために「独立前にやるべきことチェックリスト」をご紹介します。 当記事は前編となる「お金まわり編」。全て ...

2

ゲームなどのエンタメ分野での導入が進んでいるVR。言葉自体は耳にしたことがあっても、その意味を詳しく知らないという人も多いのではないでしょうか。また、次世代の新たな技術をビジネスに積極的に活用していく ...

3

フリーランスの確定申告には、青色申告と白色申告の2種類があります。いったいどちらの申告方法を選ぶべきなのでしょうか。 この記事では、青色申告と白色申告、それぞれのメリット・デメリットを把握することでベ ...

4

フリーランスの仕事場は、自宅をはじめとし、カフェや図書館、コワーキングスペースなど自由に選べます。しかし全ての場所で100%集中ができるどうかは、人それぞれです。どのような環境が自身の仕事場として快適 ...

5

フリーランスのITエンジニアとして、一番難しいのは「自己管理」と言えるでしょう。 自分の体調管理はもとより、仕事のスケジュール等すべて自分で管理します。もちろんフリーランスに有給休暇等ありませんから休 ...

-Shellスクリプト

Copyright© Beエンジニア , 2020 All Rights Reserved.