シェルスクリプトの文字列比較とは、[ "$a" = "$b" ] や [[ $a == $b ]] のように、2つの文字列が同じか・空かどうかを調べて if 文などの分岐に使う書き方です。 等しいかは =、等しくないかは !=、空かどうかは -z、空でないかは -n で調べます。bash では [[ ]] を使うと、ワイルドカード一致(== の右辺に *)と正規表現(=~)も使えます。

この記事では、演算子ごとに「[ ]・[[ ]]・sh(dash) のどこで使えるか」を○×の早見表にまとめ、よく出会うエラー文(unary operator expected / too many arguments / unexpected operator)を実際に再現して、原因と直し方を並べました。前方一致・後方一致・部分一致・正規表現・大文字小文字の無視は、そのまま貼って使えるコードと実行結果を載せています。動作確認は GNU bash 5.2.21 と dash 0.5.12(Ubuntu の /bin/sh) で行いました。

文字列比較の早見表

まずは「どの演算子が、どの書き方で使えるか」の一覧です。○は今回の実行で動作を確認したもの、×はエラーまたは意図どおりに動かなかったものです。

やりたいこと 演算子 [ ](bash) [[ ]](bash) [ ](sh = dash)
等しい = ○ ○ ○
等しい(別表記) == ○ ○ ×(unexpected operator)
等しくない != ○ ○ ○
辞書順で前 < △(\< と書けば○) ○ △(\< と書けば○)
辞書順で後 > △(\> と書けば○) ○ △(\> と書けば○)
空文字である -z ○(変数はクォート) ○ ○(変数はクォート)
空文字でない -n ○(変数はクォート必須) ○ ○(変数はクォート必須)
正規表現に一致 =~ ×(binary operator expected) ○ ×(unexpected operator)
ワイルドカード一致 == a* ×(文字どおりの比較になる) ○ ×(文字どおりの比較になる)
[[ ]] そのもの ― ― ○ ×([[: not found)

迷ったときの選び方は次の3行で足ります。

スクリプトの1行目 使う書き方 等しいかの演算子
#!/bin/bash [[ ]] == でも = でもよい
#!/bin/sh [ ] と case = だけを使う
どちらで動くか決めていない [ ] と case = だけを使う

本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。

(記載例)出典:株式会社RJC「シェルスクリプトの文字列比較|= == != -z -n と[[ ]]の違い」

文字列比較の基本の書き方

等しい・等しくないを調べる

もっともよく使う形です。[ の直後、] の直前、演算子の両側には必ず半角スペースを入れます。

#!/bin/bash
ans="yes"

if [ "$ans" = "yes" ]; then
  echo "yes です"
fi

if [ "$ans" != "no" ]; then
  echo "no ではありません"
fi

# bash なら [[ ]] でも同じ
if [[ $ans == yes ]]; then
  echo "[[ ]] でも yes です"
fi

実行結果(bash 5.2.21):

yes です
no ではありません
[[ ]] でも yes です

= と == は bash の中では同じ意味です。違いが出るのは sh(dash) で動かしたときで、== は dash の [ ] では受け付けられません(後半で再現します)。どのシェルでも動かしたいスクリプトでは = に統一しておくと安心です。

空かどうかを調べる -z と -n

演算子 真になる条件 覚え方
-z "$v" 長さが 0(空文字、または未定義) zero の z
-n "$v" 長さが 1 以上 non-zero の n

未定義・空文字・スペース1つの3パターンで結果を確かめました。-v は bash の [[ ]] で使える「変数が定義されているか」の判定です。

#!/bin/bash
unset u; e=""; sp=" "
for n in u e sp; do
  [[ -z ${!n} ]] && z=真 || z=偽
  [[ -n ${!n} ]] && nn=真 || nn=偽
  [[ -v $n ]]    && vv=真 || vv=偽
  echo "$n: -z=$z -n=$nn -v=$vv"
done

実行結果:

u: -z=真 -n=偽 -v=偽
e: -z=真 -n=偽 -v=真
sp: -z=偽 -n=真 -v=真

-z は「未定義」と「空文字」を区別しません。 区別したいときだけ -v 変数名 を使います。また、スペース1つは「空ではない」と判定されます。入力の前後の空白を取り除いてから比べたい場合は、後述の「比較が合わないときの確認ポイント」を参照してください。

辞書順で比べる

< と > は文字列を辞書順(文字コード順)で比べます。数値の大小ではない点がポイントです。

#!/bin/bash
[[ apple < banana ]] && echo "apple < banana"
[[ 10 < 9 ]] && echo '文字列では "10" < "9"(1文字目で比べる)'
(( 10 > 9 )) && echo "数値は (( )) か -gt"

実行結果:

apple < banana
文字列では "10" < "9"(1文字目で比べる)
数値は (( )) か -gt

数値として比べたいときは -eq -lt -gt などか (( )) を使います。 文字列の == では 05 と 5 は別物ですが、-eq なら同じ数値として扱われます。

[[ 05 == 5 ]] || echo "05 vs 5 文字列は不一致"
[[ 05 -eq 5 ]] && echo "-eq は一致"
05 vs 5 文字列は不一致
-eq は一致

なお、今回の環境(ロケール未設定=C ロケール相当)では [[ a < B ]] は偽でした(小文字 a の文字コードが大文字 B より後ろのため)。[[ ]] の < > は現在のロケールの並び順を使うので、ja_JP.UTF-8 などのロケールでは順序が変わる場合があります(他のロケールでの挙動は今回未実行です)。

シングルブラケットとダブルブラケットの違い

[ と [[ は見た目が似ていますが、成り立ちが違います。bash で type を実行するとわかります。

type [
type [[
type test
ls -l /usr/bin/[

実行結果:

[ is a shell builtin
[[ is a shell keyword
test is a shell builtin
-rwxr-xr-x 1 root root 55744 Jan 23  2026 /usr/bin/[

[ は test と同じ「コマンド」、[[ は bash の「構文(キーワード)」です。この違いが、次の表のような挙動の差につながります。

観点 [ ] [[ ]]
正体 コマンド(test と同じ) bash のキーワード
使えるシェル sh・dash・bash など bash・zsh・ksh(dash では使えない)
変数のクォート 必須(空やスペースでエラーになる) 省略しても単語分割されない
< > \< \> とエスケープが必要 そのまま書ける
ワイルドカード一致 使えない == != の右辺で使える
正規表現 =~ 使えない 使える
複数条件 [ A ] && [ B ] と外でつなぐ 中で && と || が使える
大文字小文字の無視 効かない shopt -s nocasematch が効く

[ はコマンドなので、シェルは先に変数を展開し、スペースで区切って引数として渡します。空の変数は引数が消え、スペース入りの変数は引数が増えるため、エラーにつながります。[[ ]] は構文として解釈されるので、変数の中身が空でもスペース入りでも1つの値として扱われます。

複数条件の書き方も確認しておきます。

#!/bin/bash
a=abc; b=""
[[ -n $a && -z $b ]] && echo "[[ ]] の中で && が使える"
[ -n "$a" ] && [ -z "$b" ] && echo "[ ] は外側で && をつなぐ"
[[ $a == abc || $a == xyz ]] && echo "|| も可"
[[ ]] の中で && が使える
[ ] は外側で && をつなぐ
|| も可

[ ] には -a(かつ)と -o(または)もありますが、値によっては解釈があいまいになります。今回、変数の値が ( のときに [ -n "$a" -a "$a" = "(" ] を実行すると、次のエラーになりました。

bash: line 1: [: `)' expected, found (

[ ] の複数条件は [ A ] && [ B ] と外でつなぐのが、値に左右されない書き方です。

エラーメッセージ別の原因と直し方

ここからは、文字列比較でよく出会うエラーを1本のスクリプトにまとめて再現します。

#!/bin/bash
# err.sh
name=""
msg="hello world"
if [ $name = "taro" ]; then echo "一致"; fi          # 4行目
if [ $msg = "hello world" ]; then echo "一致"; fi    # 5行目
if [ -n $msg ]; then echo "空ではない"; fi            # 6行目
if [ "$msg" =~ ^hello ]; then echo "一致"; fi        # 7行目
if [ "$msg" = "hello world"]; then echo "一致"; fi   # 8行目
if ["$msg" = "hello world" ]; then echo "一致"; fi   # 9行目
if [ "$msg"="bye" ]; then echo "スペースなしは常に真"; fi  # 10行目

実行結果(bash 5.2.21):

err.sh: line 4: [: =: unary operator expected
err.sh: line 5: [: too many arguments
err.sh: line 6: [: hello: binary operator expected
err.sh: line 7: [: =~: binary operator expected
err.sh: line 8: [: missing `]'
err.sh: line 9: [hello world: command not found
スペースなしは常に真

それぞれの原因と直し方は次のとおりです。

エラーメッセージ 起きる場面 シェルから見た姿 直し方
unary operator expected クォートなしの変数が空 [ = "taro" ] "$name" とクォートする
too many arguments クォートなしの変数にスペース [ hello world = ... ] "$msg" とクォートする
binary operator expected -n -z にスペース入りの値、または [ ] で =~ [ -n hello world ] クォートする/[[ ]] に変える
[: missing で始まるメッセージ ] の前にスペースがない 最後の引数が world] になる ] の前にスペースを入れる
command not found [ の後にスペースがない [hello というコマンドを探す [ の後にスペースを入れる
エラーなしで常に真 演算子の両側にスペースがない [ "hello world=bye" ](1つの文字列) = の両側にスペースを入れる

unary operator expected の直し方

変数が空のとき、[ $name = "taro" ] は [ = "taro" ] に展開されます。左辺がなくなり、= が「引数を1つ取る演算子(unary operator)」の位置に来てしまうのが原因です。

# 直す前
if [ $name = "taro" ]; then echo "一致"; fi

# 直した後(1):ダブルクォートで囲む
if [ "$name" = "taro" ]; then echo "一致"; else echo "不一致(エラーなし)"; fi

# 直した後(2):bash なら [[ ]] にする
if [[ $name = "taro" ]]; then echo "一致"; else echo "不一致([[ ]] はクォートなしでも動く)"; fi
不一致(エラーなし)
不一致([[ ]] はクォートなしでも動く)

too many arguments の直し方

msg="hello world" のとき、[ $msg = "hello world" ] は [ hello world = "hello world" ] と引数が4つになり、[ が解釈できなくなります。直し方は unary operator expected と同じで、変数をダブルクォートで囲むだけです。

if [ "$msg" = "hello world" ]; then echo "一致"; fi
if [[ $msg = "hello world" ]]; then echo "一致"; fi
一致
一致

クォートなしの -n は空でも真になる

エラーが出ないぶん気づきにくいのがこのパターンです。

e=""
[ -n $e ] && echo '[ -n $e ] はクォートなしだと空でも真'
[ -n "$e" ] || echo '[ -n "$e" ] は偽'
[ -n $e ] はクォートなしだと空でも真
[ -n "$e" ] は偽

[ -n $e ] は [ -n ] に展開され、「-n という文字列は空ではない」という判定になります。[ ] の中の変数は、いつもダブルクォートで囲むと覚えておくと、この3つをまとめて避けられます。

エスケープなしの > はファイルを作る

[ ] の中で < > をそのまま書くと、比較ではなくリダイレクトとして解釈されます。空のフォルダで試しました。

a=abc
[ "$a" > "abd" ] && echo "真になった"
ls -l    # abd という 0 バイトのファイルができている
真になった
0 abd

(ls -l の出力はサイズとファイル名だけを抜き出しています。)abc は abd より辞書順で前なので本来は偽ですが、[ "$a" ](空でないので真)+「abd への出力リダイレクト」と解釈されました。< の場合は abd: No such file or directory と表示されます。[ ] では \< \>、bash なら [[ ]] を使います。

未定義変数と set -u

set -u(未定義変数の参照で止める設定)を使っているスクリプトでは、未定義の変数を比較すると unbound variable で止まります。${v:-} と書くと、未定義のときは空文字として扱えます。

set -u
unset v
[ -z "${v:-}" ] && echo "set -u でも止まらない"
[ -z "$v" ]
set -u でも止まらない
bash: line 1: v: unbound variable

sh(dash)で動かすときの違い

Ubuntu や Debian では /bin/sh の実体が dash です。1行目が #!/bin/sh のスクリプトや、sh script.sh で起動したスクリプトは dash で動き、bash 専用の書き方はエラーになります。

#!/bin/sh
# err2.sh
name=""
ans="yes"
if [ "$ans" == "yes" ]; then echo "一致"; fi   # 4行目
if [ $name = "taro" ]; then echo "一致"; fi    # 5行目
if [[ $ans == y* ]]; then echo "一致"; fi      # 6行目
if [ "$ans" =~ ^y ]; then echo "一致"; fi      # 7行目
echo "${ans^^}"                                # 8行目

sh err2.sh の実行結果(dash 0.5.12):

err2.sh: 4: [: yes: unexpected operator
err2.sh: 5: [: =: unexpected operator
err2.sh: 6: [[: not found
err2.sh: 7: [: yes: unexpected operator
err2.sh: 8: Bad substitution

同じファイルを bash err2.sh で動かした結果:

一致
err2.sh: line 5: [: =: unary operator expected
一致
err2.sh: line 7: [: =~: binary operator expected
YES
dash のメッセージ 原因 直し方
[: yes: unexpected operator == や =~ は dash の [ ] にない == を = に変える。正規表現は grep -E か case
[: =: unexpected operator クォートなしの変数が空(bash の unary operator expected と同じ原因) "$name" とクォートする
[[: not found [[ ]] は dash にない [ ] と case で書く、または1行目を #!/bin/bash にする
Bad substitution ${var^^} ${var,,} は bash 4 以降の機能 tr で変換する

エラーが出ても if 文は「偽」として先へ進む点に注意が必要です。次のスクリプトは dash では else 側に入りました。

#!/bin/sh
ans="yes"
if [ "$ans" == "yes" ]; then
  echo "OK"
else
  echo "NG"
fi
eq.sh: 3: [: yes: unexpected operator
NG

メッセージを見落とすと「yes なのに NG になる」という現象に見えます。#!/bin/sh のスクリプトでは = を使うのが確実です。

どのシェルでも動く書き方

[[ ]] を使わずに、前方一致・後方一致・部分一致・正規表現・大文字小文字の無視を書いた例です。dash で実行しました。

#!/bin/sh
file="report_2026.csv"
case "$file" in report_*) echo "前方一致";; esac
case "$file" in *.csv)    echo "後方一致";; esac
case "$file" in *2026*)   echo "部分一致";; esac

# パラメータ展開:先頭や末尾を削って、元と変わったかで判定
[ "${file#report_}" != "$file" ] && echo "前方一致(パラメータ展開)"
[ "${file%.csv}" != "$file" ]    && echo "後方一致(パラメータ展開)"

# 正規表現は grep -E に任せる(-q は出力なしで終了コードだけ返す)
if printf '%s\n' "$file" | grep -Eq '^report_[0-9]{4}\.csv$'; then
  echo "正規表現(grep -E)"
fi

# 大文字小文字の無視は tr で小文字にそろえる
ans="Yes"
lower=$(printf '%s' "$ans" | tr '[:upper:]' '[:lower:]')
[ "$lower" = "yes" ] && echo "大文字小文字を無視して一致"
case "$ans" in [Yy][Ee][Ss]) echo "case のブラケットでも一致";; esac

実行結果(dash 0.5.12):

前方一致
後方一致
部分一致
前方一致(パラメータ展開)
後方一致(パラメータ展開)
正規表現(grep -E)
大文字小文字を無視して一致
case のブラケットでも一致

前方一致・後方一致・部分一致の書き方

bash の [[ ]] では、== の右辺にワイルドカード(* は0文字以上、? は1文字、[abc] はいずれか1文字)を書けます。

やりたいこと 書き方 意味
前方一致 [[ $s == abc* ]] abc で始まる
後方一致 [[ $s == *abc ]] abc で終わる
部分一致 [[ $s == *abc* ]] abc を含む
含まない [[ $s != *abc* ]] abc を含まない
変数の値を含む [[ $s == *"$key"* ]] key の中身を文字どおり含む
1文字だけ任意 [[ $s == a?c ]] a、任意の1文字、c
#!/bin/bash
file="report_2026.csv"
[[ $file == report_* ]] && echo "前方一致: report_ で始まる"
[[ $file == *.csv ]]    && echo "後方一致: .csv で終わる"
[[ $file == *2026* ]]   && echo "部分一致: 2026 を含む"
[[ $file != *.txt ]]    && echo "否定: .txt では終わらない"

key="2026"
[[ $file == *"$key"* ]] && echo "変数で部分一致: $key を含む"

pat="*"
[[ "a*b" == *"$pat"* ]] && echo "クォートした変数は文字どおり: * を含む"
[[ "axb" == *"$pat"* ]] || echo "axb は文字の * を含まない"

実行結果:

前方一致: report_ で始まる
後方一致: .csv で終わる
部分一致: 2026 を含む
否定: .txt では終わらない
変数で部分一致: 2026 を含む
クォートした変数は文字どおり: * を含む
axb は文字の * を含まない

ポイントは右辺のクォートの有無です。

右辺の書き方 扱い
[[ $s == a* ]] * はワイルドカード
[[ $s == "a*" ]] a* という文字列そのものと比較
[[ $s == *"$key"* ]] 外側の * はワイルドカード、$key の中身は文字どおり
[[ $s == *$key* ]] $key の中の * や ? もワイルドカードとして働く

実際に a=abc で [[ $a == "a*" ]] を実行すると偽(終了コード 1)、[[ $a == a* ]] は真(終了コード 0)でした。変数で部分一致をするときは *"$key"* の形にしておくと、変数に記号が入っていても文字どおりに探せます。

[ ] でワイルドカードを書いた場合、[ "$a" = a* ] は終了コード 1(偽)でした。a* はシェルによってファイル名に展開されるか、展開先がなければ a* という文字列のままになり、どちらにしてもパターン一致にはなりません。

正規表現で比べる

[[ 文字列 =~ 正規表現 ]] で、拡張正規表現(ERE)による一致を調べられます。かっこで囲んだ部分は配列 BASH_REMATCH に入ります。

#!/bin/bash
file="report_2026.csv"
re='^report_([0-9]{4})\.(csv|tsv)$'
if [[ $file =~ $re ]]; then
  echo "一致: 全体=${BASH_REMATCH[0]} 年=${BASH_REMATCH[1]} 拡張子=${BASH_REMATCH[2]}"
fi

[[ $file =~ '^report' ]] || echo "右辺をクォートすると正規表現にならず、文字列 ^report を探す"
[[ $file =~ ^report ]]   && echo "クォートなしなら正規表現として一致"

[[ "12345" =~ ^[0-9]+$ ]] && echo "数字だけ"
[[ "12a45" =~ ^[0-9]+$ ]] || echo "数字以外を含む"

実行結果:

一致: 全体=report_2026.csv 年=2026 拡張子=csv
右辺をクォートすると正規表現にならず、文字列 ^report を探す
クォートなしなら正規表現として一致
数字だけ
数字以外を含む
正規表現でのポイント 内容
右辺はクォートしない クォートすると文字どおりの検索になる
パターンは変数に入れる re='...' としてから =~ $re と書くと、かっこや縦棒のエスケープに悩まない
全体一致には ^ と $ 付けないと「含む」の判定になる
取り出しは BASH_REMATCH [0] が全体、[1] 以降がかっこの中
\d や \w ERE には含まれないので [0-9] や [[:alnum:]_] と書く

ワイルドカードと正規表現の使い分けは、「始まる・終わる・含む」だけならワイルドカード、「数字4桁」「2種類の拡張子のどちらか」のように形を指定したいときは正規表現、と考えると選びやすくなります。

大文字と小文字を区別せずに比べる

方法は3つあります。

方法 書き方 使える場所 向いている場面
小文字にそろえる ${ans,,} bash 4 以降 1か所だけ比べたい
大文字にそろえる ${ans^^} bash 4 以降 同上
nocasematch shopt -s nocasematch bash の [[ ]] と case 続けて何度も比べたい
tr で変換 tr '[:upper:]' '[:lower:]' sh(dash) を含むどのシェルでも #!/bin/sh のスクリプト
#!/bin/bash
ans="Yes"
[[ $ans == yes ]] || echo "そのままでは不一致"
[[ ${ans,,} == yes ]] && echo '${ans,,} で小文字にそろえて一致'
[[ ${ans^^} == YES ]] && echo '${ans^^} で大文字にそろえて一致'

shopt -s nocasematch
[[ $ans == yes ]]      && echo "nocasematch: == で一致"
[[ $ans =~ ^y(es)?$ ]] && echo "nocasematch: =~ でも一致"
case $ans in yes) echo "nocasematch: case でも一致";; esac
[ "$ans" = yes ] || echo "nocasematch は [ ] には効かない"
shopt -u nocasematch

実行結果:

そのままでは不一致
${ans,,} で小文字にそろえて一致
${ans^^} で大文字にそろえて一致
nocasematch: == で一致
nocasematch: =~ でも一致
nocasematch: case でも一致
nocasematch は [ ] には効かない

nocasematch はスクリプト全体に効き続ける設定なので、使い終わったら shopt -u nocasematch で戻しておくと、後ろの比較に影響しません。1か所だけなら ${ans,,} のほうが影響範囲が小さく、読み手にも意図が伝わりやすい書き方です。

case文で文字列を振り分ける

比較が3つ以上並ぶときは、case にまとめると読みやすくなります。case はどのシェルでも使え、ワイルドカード一致が最初から使えるのが利点です。

#!/bin/bash
check() {
  case "$1" in
    report_*.csv) echo "$1: レポートCSV";;
    *.csv|*.tsv)  echo "$1: 表データ";;
    "")           echo "(空): 空文字";;
    *[!0-9]*)     echo "$1: 数字以外を含む";;
    *)            echo "$1: 数字だけ";;
  esac
}
check report_2026.csv; check data.tsv; check ""; check 12a; check 123

実行結果:

report_2026.csv: レポートCSV
data.tsv: 表データ
(空): 空文字
12a: 数字以外を含む
123: 数字だけ
if で書いた場合 case で書いた場合
[[ $s == a* ]] case "$s" in a*) ...;; esac
[[ $s == a* || $s == b* ]] case "$s" in a*|b*) ...;; esac
[[ -z $s ]] case "$s" in "") ...;; esac
[[ $s =~ ^[0-9]+$ ]] case "$s" in ""|*[!0-9]*) 数字以外;; *) 数字だけ;; esac

上から順に調べて最初に一致した行だけが実行されるので、条件の狭いものを上、広いものを下に置きます。

比較が合わないときの確認ポイント

「同じはずなのに一致しない」ときは、目に見えない文字が混ざっていることが多くあります。

#!/bin/bash
a="abc "
[[ $a == abc ]] || echo "末尾スペースで不一致"

a=$(printf "abc\r")          # Windows の改行コード(CR)が残った想定
[[ $a == abc ]] || echo "CRで不一致"
printf "%q\n" "$a"           # 中身を見える形で表示
a=${a%$'\r'}                 # 末尾の CR を取り除く
[[ $a == abc ]] && echo "CR除去で一致"

実行結果:

末尾スペースで不一致
CRで不一致
$'abc\r'
CR除去で一致
症状 確認方法 直し方
見た目は同じなのに不一致 printf '%q\n' "$v" で中身を表示 末尾の空白や CR を取り除く
Windows で作ったファイルを読んだ値 $'...\r' と表示される v=${v%$'\r'}
数値のつもりが不一致 05 と 5 を == で比べている -eq か (( )) を使う
常に真になる = の両側にスペースがない スペースを入れる
sh で動かすと分岐が変わる == や [[ ]] を使っている = と case にする、または bash で起動する
大文字小文字で不一致 Yes と yes ${v,,} か nocasematch

printf '%q' は、スペースや制御文字をエスケープした形で表示してくれる bash の機能です。比較が合わないときは、まず両辺を %q で表示すると原因に早くたどり着けます。

また、[ abc -eq 0 ] のように数値用の演算子へ文字列を渡すと、次のエラーになります。文字列には =、数値には -eq と使い分けます。

bash: line 1: [: abc: integer expression expected

コピペ用のテンプレート

bash 用

#!/bin/bash
s="$1"

# 空チェック
if [[ -z $s ]]; then echo "引数が空です"; exit 1; fi

# 等しい・等しくない
if [[ $s == start ]]; then echo "start です"; fi
if [[ $s != stop ]];  then echo "stop ではありません"; fi

# 前方・後方・部分一致
if [[ $s == st* ]];   then echo "st で始まる"; fi
if [[ $s == *rt ]];   then echo "rt で終わる"; fi
if [[ $s == *ar* ]];  then echo "ar を含む"; fi

# 正規表現
re='^[a-z]+$'
if [[ $s =~ $re ]];   then echo "英小文字だけ"; fi

# 大文字小文字を無視
if [[ ${s,,} == start ]]; then echo "START でも start でも一致"; fi

sh 用(dash でも動く)

#!/bin/sh
s="$1"

# 空チェック
if [ -z "$s" ]; then echo "引数が空です"; exit 1; fi

# 等しい・等しくない(= を使う)
if [ "$s" = "start" ];  then echo "start です"; fi
if [ "$s" != "stop" ];  then echo "stop ではありません"; fi

# 前方・後方・部分一致は case
case "$s" in st*)  echo "st で始まる";; esac
case "$s" in *rt)  echo "rt で終わる";; esac
case "$s" in *ar*) echo "ar を含む";; esac

# 正規表現は grep -E
if printf '%s\n' "$s" | grep -Eq '^[a-z]+$'; then echo "英小文字だけ"; fi

# 大文字小文字を無視
lower=$(printf '%s' "$s" | tr '[:upper:]' '[:lower:]')
if [ "$lower" = "start" ]; then echo "START でも start でも一致"; fi

よくある質問

Q. = と == はどちらを使えばよいですか? A. bash ではどちらも同じ結果になります。sh(dash) の [ ] では == が unexpected operator になるため、どのシェルでも動かしたいなら = に統一するのがおすすめです。1行目が #!/bin/bash で [[ ]] を使うなら、好みで == を選んで構いません。

Q. [[ ]] の中でも変数をクォートしたほうがよいですか? A. 左辺はクォートなしでも単語分割されないため、空やスペース入りでも動きます。右辺は意味が変わります。== の右辺をクォートすると「文字どおりの比較」、クォートなしだと「ワイルドカード一致」です。=~ の右辺もクォートすると正規表現として働かなくなります。

Q. unary operator expected が出たり出なかったりします。 A. 変数が空のときだけ出るエラーだからです。[ $v = "x" ] は、v に値があれば動き、空のときだけ [ = "x" ] になってエラーになります。[ "$v" = "x" ] とクォートすれば、値に関係なく動きます。

Q. 文字列が数字だけかどうかを調べるには? A. bash なら [[ $s =~ ^[0-9]+$ ]]、sh なら case "$s" in ""|*[!0-9]*) 数字以外;; *) 数字だけ;; esac と書けます。今回の実行では、=~ は 12345 と 12a45、case は 123 と 12a で、数字だけ・数字以外を見分けられました。

まとめ

  • 等しいは =、等しくないは !=、空は -z、空でないは -n。この4つは [ ]・[[ ]]・sh(dash) のどこでも使える
  • ==・=~・ワイルドカード一致・[[ ]] は bash 向け。sh(dash) では unexpected operator や [[: not found になる
  • [ ] の中の変数は必ずダブルクォートで囲む。空なら unary operator expected、スペース入りなら too many arguments になるのを防げる
  • 前方一致は abc*、後方一致は *abc、部分一致は *abc*。変数を使うときは *"$key"* の形にする
  • 正規表現は [[ $s =~ $re ]]。パターンは変数に入れ、右辺はクォートしない。取り出しは BASH_REMATCH
  • 大文字小文字の無視は ${v,,} か shopt -s nocasematch。sh では tr で小文字にそろえる
  • 比較が合わないときは printf '%q\n' "$v" で、末尾の空白や CR が混ざっていないか確かめる

本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。

(記載例)出典:株式会社RJC「シェルスクリプトの文字列比較|= == != -z -n と[[ ]]の違い」

株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。

WE ARE HIRING

RJCで一緒に開発しながら、
成長を楽しみませんか?

RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!

  • 127日年間休日
  • 12時間平均残業時間
  • 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!

ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。

ABOUT RJC RJCがどんな会社か知る 考え方、研修、働き方、福利厚生、社員の前職まで。RJCのことが丸わかり! RJC丸わかりページへ JOB DESCRIPTION 仕事内容・待遇を見てみる 仕事内容、給与・待遇、選考の流れ。経験者も未経験も!応募前に知りたいこと、まとめました! 募集要項を見る ENTRY エントリーする エントリーは1〜2分・履歴書不要です。まずは話を聞いてみたい、という方でも歓迎です! エントリーフォームへ

RJCで一緒に開発しながら、 成長を楽しみませんか?