- 技術 💻
シェルスクリプトの文字列比較|= == != -z -n と[[ ]]の違い
シェルスクリプトの文字列比較とは、[ "$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期連続の黒字決算。