L'Isola di Niente
L'Isola di Niente » ShellScript Tips » シェルスクリプトの制御文

シェルスクリプトの制御文

# 最終更新日 2017.01.01

test コマンドと [[
test コマンドは真偽値を戻す、代わりに [...] を利用することもできる。
更に bash では [[...]] も使える、この三つは下記のような場合はまったく同じです。
if test 1 -ne 2; then
# ↓
if [ 1 -ne 2 ]; then
# ↓
if [[ 1 -ne 2 ]]; then

[[...]] はいったい何かというと [ の拡張と思えばいいようです。
# = でパターンマッチ
[[ おまえはアホ = *アホ ]] && echo 1 || echo 2
#=> 1
[[ おまえはスケベ = *アホ ]] && echo 1 || echo 2
#=> 2

# =~ で部分的正規表現マッチ、単なる文字列探しにも使える
[[ おまえはアホ =~ [(アホ|バカ)] ]] && echo 1 || echo 2
#=> 1
[[ おまえはスケベ =~ [(アホ|バカ)] ]] && echo 1 || echo 2
#=> 2

# 辞書順で文字列比較、数値優先には当然ならないけど
[[ test2.txt < test12.text ]] && echo 1 || echo 2
#=> 2

# 論理演算子が &&, || になる
[[ a = a && b = b ]] && echo 1
#=> 1

if
上記で既に書いていますが単純な if 文であれば &&, || のほうが簡単かつ明解です。
[[ 条件式 ]] && 真の場合 || 偽の場合
if [[ $PWD = $HOME ]]; then
    echo ホームです
else
    cd ~
fi

# ↓

[[ $PWD = $HOME ]] && echo ホームです || cd ~

中括弧でネストすることもできる。
GF=文緒も心実も桃子もクロエも可愛いね

[[ $GF =~ エレナ ]] && {
    echo エレナちゃん見つけた!
} || {
    echo エレナちゃんはいなかった...
    [[ $GF =~ 文緒 ]] && {
        echo でも文緒ちゃんは見つけた!
    }
};

elif を使う場合は素直に if を使ってください。
以下は条件式。

数値
条件式 意味
数値 -eq 数値==
数値 -ne 数値!=
数値 -ge 数値>=
数値 -gt 数値>
数値 -le 数値<=
数値 -lt 数値<
文字列
条件式 意味
-n 文字列文字列が一文字以上ある
-z 文字列文字列がゼロ文字
文字列1 = 文字列2等しい
文字列1 != 文字列2等しくない
ファイル
条件式 意味
-G ファイル名ファイルのグループが実行ユーザー
-O ファイル名ファイルの所有者が実行ユーザー
-S ファイル名ソケット
-b ファイル名ブロックデバイスファイル
-c ファイル名キャラクタデバイスファイル
-d ファイル名ディレクトリ
-e ファイル名存在確認
-f ファイル名通常ファイル
-g ファイル名SGID がある
-h ファイル名, -L ファイル名シンボリックリンク
-k ファイル名スティッキービットがある
-p ファイル名名前付きパイプ
-r ファイル名読み取り可能
-s ファイル名ファイルサイズがゼロではない
-t ファイル名端末でオープンされてい
-u ファイル名SUID がある
-w ファイル名書き込み可能
-x ファイル名実行可能
ファイル名1 -nt ファイル名2ファイル名1のほうが修正時刻が新しい
ファイル名1 -ot ファイル名2ファイル名1のほうが修正時刻が古い
ファイル名1 -ef ファイル名2デバイス番号と i ノード番号が同じ

特殊変数
特殊変数 解説
$n (数字)$0 はスクリプト名、以降 $1, $2…${10}, ${11}… と引数が並ぶ
$#引数の個数
$@$0 以外の全ての引数("$@" の場合 "$1" "$2" …")
$*$0 以外の全ての引数("$*" の場合 "$1 $2 …")
$?最後に実行したコマンドの終了ステータス
$!最後に実行したバックグラウンドコマンドの PID
$$シェルの PID
$-現在のオプションフラグ

以上がありますがほとんど引数のことですね。
こんなふうに使います。
#!/bin/sh

echo ディレクトリ名で圧縮します

if [[ $# -eq 0 ]]; then
    echo 引数が無いよ
else
    path=`pwd`
    name=${path##*/}
    file-roller --add-to="${name}.tar.gz" "$@"
fi

case
case 文は文字列が条件に一致するかで分岐する。
条件 意味
?一文字
*ワイルドカード
[...][...] に含まれる一文字
[!...][!...] に含まれない一文字
また条件を「|」記号で or 演算することもできる。
#!/bin/sh

case $USER in
    root|syacho)  echo おはようございます ;;
    sasakima-nao) echo おまえかよ ;;
    bite-*)       echo うぃーす ;;
    *)            echo 誰だヨ! ;;
esac

while, until
while は条件が真、until は条件は偽の場合に繰り返す制御文。
条件式は if 文と同様 test コマンドを使う。
#!/bin/sh

buf1=あ
while [[ $buf1 != ああああああ ]]
do
    echo $buf1
    buf1=${buf1}あ
done

buf2=あ
until [[ $buf2 == ああああああ ]]
do
    echo $buf2
    buf2=${buf2}あ
done

for
シェルスクリプトの for 文は Python 等と同様に整数を使いません。
半角空白区切りの文字列をシーケンスと扱ってループします。
continuebreak も利用できます。
#!/bin/sh

BIKES="YAMAHA HONDA SUZUKI KAWASAKI"
for bike in $BIKES
do
    [[ $bike = HONDA ]] && continue
    [[ $bike = KAWASAKI ]] && break
    echo $bike
done

整数を使うことも一応できます。
でも使い道が無いよね。
#!/bin/sh

for (( i=0; i<5; i++ )); do
    echo $i
done

関数
シェルスクリプトも関数が作れます。

ただし C 言語の関数等と違い別プロセスとして実行されます。
親シェルの変数は参照できますが戻り値は $? を利用します。

関数の呼び出しに括弧は不要で引数は半角空白区切りで渡します。
関数側から引数を参照するには $1, $2... を利用、別プロセスなのでスクリプトの呼び出し引数とは違う。
等々、C 言語なんかを知っていると少し戸惑ってしまうと思います。
#!/bin/sh

func()
{
    if [[ $1 = Suzuki ]]; then
        BIKE=Yamaha # 親の変数を参照できる
        return 0
    fi
    return 1
}

BIKE=Honda

# 関数呼び出し、Suzuki は引数
func Suzuki

# if test func みたいにできない
if [[ $? ]]; then
    echo $BIKE # Yamaha が表示される
else
    echo Kawasaki
fi

Copyright(C) sasakima-nao All rights reserved 2002 --- 2017.