順番に読み進めながら学べます

条件分岐 — if と [ ] でファイル・数値を判定

この記事は、基礎コマンドからシェルスクリプトまでLinuxの実践的なスキルを1からマスターする「Linux入門講座の一部」です。
[ -f config.txt ]や[ -z "$name" ]・[ "$count" -lt 5 ]でファイル・文字列・数値を判定し、if/elif/elseで処理を分け、caseで複数パターンを振り分ける方法を、図解とブラウザ端末で書いて実行します。

値を比べて分岐する — 文字列比較と数値比較

シェルスクリプトでは、文字列が一致するか・数値が大きいかを調べて処理を変えます。

判定にはtestコマンド、または同じ働きの[ ... ]を使います。

[]の内側は前後に空白が必要で、[ "$count" -lt 5 ]のように書きます。

判定が真なら終了コード 0、偽なら 1 を返します

文字列の比較は=(一致)・!=(不一致)、空かどうかは-z(空)・-n(非空)で調べます。

数値の比較は-eq(等しい)・-ne(等しくない)・-lt(より小さい)・-gt(より大きい)を使い、文字列と数値で演算子が違う点に注意します。

変数は[ -z "$name" ]のように二重引用符で囲むと、空でも構文が崩れず安全です

count=3
mode="dev"
[ "$count" -lt 5 ] && echo small    # 3 は 5 未満なので small
[ "$count" -gt 0 ] && echo positive # 0 より大きいので positive
[ "$mode" = "prod" ] || echo other  # prod ではないので other
[ -z "$mode" ] || echo has-mode     # 空ではないので has-mode
比較から分岐への流れ
[ "$count" -lt 5 ]終了コード 0終了コード 1then の処理else の処理真なら偽なら真 (0)偽 (1)
[ ... ]は真なら終了コード 0、偽なら 1 を返し、ifが真でthen、偽でelseに進みます。
書き方意味
[ "$a" = "$b" ]文字列 a と b が一致すれば真[ "$mode" = "prod" ]
[ "$a" != "$b" ]文字列 a と b が異なれば真[ "$mode" != "dev" ]
[ -z "$v" ]変数 v が空なら真[ -z "$name" ] && echo empty
[ -n "$v" ]変数 v が空でなければ真[ -n "$name" ] && echo set
[ "$a" -eq N ]数値 a が N と等しければ真[ "$count" -eq 0 ]
[ "$a" -ne N ]数値 a が N と異なれば真[ "$count" -ne 0 ]
[ "$a" -lt N ]数値 a が N より小さければ真[ "$count" -lt 10 ]
[ "$a" -gt N ]数値 a が N より大きければ真[ "$count" -gt 0 ]

数値と文字列を引数で受け取り、その値に応じて分岐する判定スクリプトを作成します。

vi compare.shcompare.shを開き、iで挿入モードに入って、先頭に#!/bin/shを書いてください。

② 1 つ目の引数$1を数値として受け取り、-ltである値より小さいかで判定して、小さいとき・そうでないときで表示を分けてください。

③ 2 つ目の引数$2を文字列として受け取り、=である語と一致するかで判定して、一致するとき・しないときで表示を分けてください。

Escを押し:wqで保存し、実行権限を付けてから、./compare.sh 3 devのように引数を渡して実行してください。

⑤ 引数を./compare.sh 8 prodなどに変えて、入る分岐が変わることを確認してください。

⑥ 書く内容に迷ったら、解答パネルの本文をコピーしてviの挿入モードに貼り付けられます。(正しく実行できれば解説が表示されます)

Linux console
0 / 3 実行済み
Loading Linux Terminal...

ファイルやフォルダを調べる — -f / -d

スクリプトでは、設定ファイルがあるか・出力先のフォルダができているかを確認してから処理を進めます。

ファイルの有無は-f(通常ファイルが存在)、フォルダの有無は-d(ディレクトリが存在)で判定します。

種類を問わず存在だけを確認したいときは-e、中身が空でないかは-sを使います。

判定の書き方は文字列・数値のときと同じ[ ... ]で、[ -f config.txt ]のようにパスを渡します。

真なら終了コード 0、偽なら 1 を返すので、if&& / ||と組み合わせてあれば使う・なければ作るという初期化処理を書けます。

touch report.txt                  # 素材を作成
[ -f report.txt ] && echo exists  # ファイルがあるので exists
[ -d report.txt ] || echo notdir  # ディレクトリではないので notdir
mkdir logs                        # フォルダを作成
[ -d logs ] && echo hasdir        # ディレクトリがあるので hasdir
ファイル判定の種類
調べたいパス[ -f path ][ -d path ][ -e path ]通常ファイルなら真ディレクトリなら真種類を問わず存在すれば真判定の種類真になる条件
-fは通常ファイル、-dはディレクトリ、-eは種類を問わず存在を判定します。同じパスでも調べる観点で結果が変わります。
書き方意味
[ -f f ]f が通常ファイルとして存在すれば真[ -f config.txt ] && echo found
[ -d d ]d がディレクトリとして存在すれば真[ -d logs ] && echo dir
[ -e p ]p が種類を問わず存在すれば真[ -e data ] && echo there
[ -s f ]f が存在し中身が空でなければ真[ -s log.txt ] && echo nonempty

ファイルやフォルダの有無で処理を分けるチェックスクリプトを作成します。

vi setup.shsetup.shを開き、iで挿入モードに入って、先頭に#!/bin/shを書いてください。

② 対象ファイルを[ -f ... ]で判定し、あれば使う旨、なければ既定値を作る旨を表示する処理を書いてください。

③ 続けてログ用フォルダを[ -d ... ]で判定し、有無に応じた表示を書いてください。

Escを押し:wqで保存してから、実行権限を付けてください。

⑤ スクリプトを実行し、判定結果が表示されることを確認してください。

⑥ 書く内容に迷ったら、解答パネルの本文をコピーしてviの挿入モードに貼り付けられます。

Linux console
0 / 3 実行済み
Loading Linux Terminal...

処理を分ける — if / elif / else

if条件が真のときだけthenの処理を実行します。

複数の条件を順に試すときはelif、どれにも当てはまらないときはelseを続け、fiで閉じます。

if 条件; then 処理; fiのようにセミコロンで区切るか、行を分けて書きます。

count=3
if [ "$count" -eq 0 ]; then
  echo "none"
elif [ "$count" -lt 5 ]; then
  echo "few ($count)"
else
  echo "many ($count)"
fi                              # few (3) と表示される
if / elif / else の流れ
if [ A ]A の処理elif [ B ]B の処理elseその他の処理上から順に判定最初に真の枝だけ実行
ifから順に条件を試し、最初に真になった枝の処理だけを実行します。どれも偽ならelseに進みます。
書き方意味
if 条件; then … fi条件が真のとき処理するif [ -f f ]; then echo ok; fi
then条件が真のとき本体を開始if [ -f f ]; then echo ok; fi
elif 条件; then …前の条件が偽で次を試すelif [ "$n" -lt 5 ]; then …
else …どれも偽のとき処理するelse echo other; fi
fiif を閉じるif …; then …; fi

件数によってメッセージを変える判定スクリプトを作成します。

vi check.shcheck.shを開き、iで挿入モードに入って、先頭に#!/bin/shを書いてください。

② スクリプト内で数値を持つ変数を用意し、-eqで 0 のとき・-ltで小さいとき・それ以外をif / elif / elseで分けて表示する処理を書いてください。

Escを押し:wqで保存してから、実行権限を付けてください。

④ スクリプトを実行し、どの分岐に入ったかを確認してください。

⑤ 書く内容に迷ったら、解答パネルの本文をコピーしてviの挿入モードに貼り付けられます。

Linux console
0 / 3 実行済み
Loading Linux Terminal...

複数パターンを振り分ける — case

1 つの値を多くの候補と照合するときはcaseが読みやすく書けます。

case 値 in パターン) 処理 ;; esacの形で、各パターンの末尾に;;を置き、*)でどれにも一致しない場合を受けます。

ifを何段も重ねるより分岐の意図が明確になります

action="start"
case "$action" in
  start) echo "starting service" ;;
  stop)  echo "stopping service" ;;
  *)     echo "unknown action: $action" ;;
esac                            # starting service と表示される
case の振り分け
case "$action" instart)stop)*)起動処理 ;;停止処理 ;;その他 ;;パターン照合一致した処理 (;;)
caseは値を上からパターンに照合し、最初に一致した枝だけを実行してesacで閉じます。*)はどれにも一致しない場合です。
書き方意味
case x in pat) … ;; esac値を複数パターンに振り分けるcase "$1" in start) … ;; esac
pat)値と照合するパターンstart) echo go ;;
;;case の各処理の終わりstart) echo go ;;
*)どれにも一致しない場合*) echo other ;;
esaccase を閉じるcase x in …; esac

操作名を引数で受け取り、その値で振り分けるコントロールスクリプトを作成します。

vi service.shservice.shを開き、iで挿入モードに入って、先頭に#!/bin/shを書いてください。

② 1 つ目の引数$1を操作名として受け取り、casestart / stop / それ以外(*)の 3 パターンに振り分けて、それぞれ別のメッセージを表示してください。各分岐は;;で終え、最後にesacで閉じてください。

Escを押し:wqで保存し、実行権限を付けてから、./service.sh startのように引数を渡して実行してください。

④ 引数を./service.sh stop./service.sh restartなどに変えて、一致するパターンが変わることを確認してください。

⑤ 書く内容に迷ったら、解答パネルの本文をコピーしてviの挿入モードに貼り付けられます。

Linux console
0 / 3 実行済み
Loading Linux Terminal...
QUIZ

理解度チェック

まずは1問ずつ答えてみましょう。

Q1[ -f config.txt ]は何を判定しますか?

Q2前の条件が偽だったときに別の条件を続けて試すキーワードはどれですか?

Q3caseで各パターンの処理の終わりに置く記号はどれですか?