それマグで!

知識はカップより、マグでゆっくり頂きます。 takuya_1stのブログ

習慣に早くから配慮した者は、 おそらく人生の実りも大きい。

bashの似てて紛らわしいもの =/==と= 代入&比較の注意点

似てて紛らわしいものシリーズ  =/==と=

bash の記号で初心者泣かせの、似てて紛らわしかったり、使い分けがわからなかったり、読み方を間違えてパニックになる記号について書く

=/==と= の違い

=はいくつかのパターンで出てきます。

  • 比較演算子として
    • = で比較
    • == で比較(=と同じ
    • =~ で正規表現マッチ
    • 数値の比較
  • 代入指示子として
    • 変数(文字列)の代入
    • 数値の代入

これらのそれぞれで微妙な差異があるのです。めんどくさいよねー

大きな違い。両端スペース

=のスペースの関係。他のプログラミング言語を触った人はこのへんで、頭が???になると思う。

変数に代入するときはvarname=value のように書く、スペースは許されない。

文字列を比較するときは [[ / [ の中で使われる。スペースの有無は問われない。

変数に代入する場合(文字列)
vaname=value
スペースは許されない
vaname = value # コマンド引数に見えてしまう。
比較で使う場合(文字列
# どちらも可
[[ $vaname = value ]] ; echo $?
[[ $vaname=value   ]] ; echo $?

数値を扱う場合。

数値を扱う場合、文字列の場合と違って= は 代入、 ==は比較と明確に区別されます。

ただし、 let(( )) で記述が異なります。

こちらも=のスペースの関係が異なるので、頭の中をスッキリさせないと混乱します。

変数に代入する場合
let i=0
(( i=0 ))
(( i = 0 ))
let でスペースは許されない(重要
# 次の記述は出来ない
let i = 0
let i= 0
let i =0
(( )) ではスペースは許される
### 次の記述はすべて許される。
(( i = 0 ))
(( i= 0 ))
(( i =0 ))

比較演算子としての=

文字列の比較演算子として使うとき = / == のどちらも同じ役目を果たす。

さらに、= / == の右辺値はglob のパターンとして扱われる。$ から始まる文字列($nameなど)は比較前に変数が展開される。

さらに、glob は shopt extglob で拡張パターンを使うことが出来る。

拡張パターンは正規表現っぽくて反って分かり辛いので、正規表現比較 =~ を使ったほうがわかりやすい。

=/== の比較の例
[[ $name =  takuya ]] ;   echo $?  # == と同じ
[[ $name == takuya ]] ;   echo $?  # = と同じ
[[ $name == $username ]]; echo $?  # 両方に変数が使える。
[[ $name == taku* ]] ;    echo $?  # 右辺値は常にglobと解釈されるです。
[[ a =  [a-z]  ]]    ;    echo $?  # 正規表現ではなくglob です
[[ a =~ [a-z] ]]   ;    echo $?  # これは正規表現

とくに extglob on でのみ許される [[ = [a-z] ]] はshopt 環境に左右されるので怖いね

まとめ

ややこしいよね。とくに =前後のスペースはハマりどころだわ。

出現箇所/opr REPL [[ / [ (( / $(( let
a=0 代入 比較 代入 代入
a = 0 cmd:a args:= 0に解釈 比較 代入 syntax error
a==0 a='=0' 比較 比較 比較
$a=$name 変数展開+代入 変数展開+比較 変数展開+代入 変数展開+代入
a=str 代入 比較 変数展開+代入 変数展開+代入

注意点

この記事においては、コマンドの PS1があると非常に見づらいので、PS1='' にした

$ (( 1==1 ))   #$(( 1==1 )) に見えてしまう。
(( 1==1 ))   #こうした
$ varname=string   #$varname=string に見えてしまう。
varname=string    #こうした

参考資料

  • bash -x による実行により確認した。