honapon
@honapon

Are you sure you want to delete the question?

Leaving a resolved question undeleted may help others!

ShellScriptの「()」のエスケープについて質問です。

解決したいこと

ShellScriptにおいて、subshell以外の用途で()を使用するときにエスケープが必要なのだという認識でおりましたが、自分の認識の何かが間違えていると思い、ご指摘頂きたいです。

現在ShellScriptの勉強のために
https://linuxconfig.org/bash-scripting-tutorial-for-beginners
にて勉強をしています。

let a=2+2 

の実行後

let b=4*($a-1) を実行したところ
-bash: syntax error near unexpected token '('

と表示されたので () のような特殊な用途のある文字に関しては
エスケープが必要なのだと思い、

let b=4*\($a-1\) 

としたところ、期待通りの計算結果となりました。

ですがその後エスケープをせずに

let c=($b**3)/2  

を実行した場合はエラーが出ませんでした。
以上のことから、
・()がある場合にエスケープが必要
・ syntax error near unexpected token '(' の解決策としてエスケープをする

どちらかが間違えているのだと思っております。
自分の認識で間違えている部分をご指摘頂きたいです。

発生している問題・エラー

linuxconfig.org:~$ let a=2+2                                                            
linuxconfig.org:~$ echo $a                                                              
4                                                                                       
linuxconfig.org:~$ let b=4*($a-1)                                                       
linuxconfig.org:~$ echo $b                                                              
12                                                                                      
linuxconfig.org:~$ let c=($b**3)/2                                                      
linuxconfig.org:~$ echo $c                                                              
864  

を順に実行したときに、let b=4*($a-1)  のみ
-bash: syntax error near unexpected token '('が表示される


0

1Answer

let の引数において () のエスケープは必要です。もっと言えば * などの特殊文字も本当はエスケープが必要です。

let c=($b**3)/2 は原則でいえばシンタックスエラーになるべきですが、 let の引数の解釈規則が普通とは若干異なるため、イコールの直後に開きカッコが来るときだけたまたまエラーにならないようです。バグか仕様か分かりませんが当てにしないほうがいいです。

* のエスケープが必要な点について補足します。 let a=3*3 と書いたとき、 a=3*3 はまず「a=3 で始まり、任意の文字が0個以上続き、 3 で終わる」ファイル名にマッチする glob パターンとして扱われ、ファイルが検索されます。ファイルが見つかればファイル名に展開され、見つからなかったときは文字通りの a=3*3 になります。よってパターンにマッチするファイルの有無で挙動が変わってしまいます:

$ let a=3*3
$ echo $a
9

$ touch 'a=333' # この名前のファイルを作る
$ let a=3*3     # パターン a=3*3 がファイル名 a=333 にマッチし、それに展開され、 a=333 が式として評価される
$ echo $a
333

$ let a=3\*3 # エスケープすればパターンにならないのでマッチしない
$ echo $a
9

let はエスケープが面倒なので、数式を扱うのであれば (( )) の使用をお勧めします。 (( b = 4 * ( $a - 1 ) )) のように、エスケープ不要かつ空白を混ぜて書くことができます。

2Like

Comments

  1. @honapon

    Questioner

    丁寧に回答いただき、ありがとうございます。
    さらに「*」に関して例も挙げて言及いただいたことにより、+αで理解することができました。
    自分では考えても解決が難しかった事柄について、教えていただいたこと感謝します。
    ※おっしゃっていただいたように(())を使用していくのが良いかと思いますので、(())を使って数式を書く練習を したいと思います。

Your answer might help someone💌