シェルスクリプトで条件判定をして、ある条件を満たした場合にcatコマンドで別のシェルスクリプトファイルを生成する、ということをやっていてハマったので備忘。
[catで変数($)を含むファイルを生成したい]
単純化するために、catコマンドでシェスクリプトを生成する部分だけを抜き出すと以下のようになる。
myshell=~/my.sh touch $myshell cat <<EOF >$myshell mytxt=~/my.txt touch $mytxt echo "hello world" >> $mytxt EOF
touchコマンドでmy.shファイルを作成し、catコマンドでファイルの中身を書き込んでいる。
[catの中ではバックスラッシュで$をエスケープする必要がある]
上記内容のシェルスクリプトを実行したあとbash ~/my.sh
と実行すると、次のようなエラーが出る。
usage: touch [-A [-][[hh]mm]SS] [-acfhm] [-r file] [-t [[CC]YY]MMDDhhmm[.SS]] file ... my.sh: line 3: syntax error near unexpected token `newline' my.sh: line 3: `echo "hello world" >> '
エラーの1-2行目は、touchコマンドの使い方が正しくないために、正しいコマンドの使い方が表示されてしまっている。オプションの部分はともかくとして、引数としてfileを指定しなければならない、ということらしい。
また、3行目は改行コードが入ってしまっているというエラーだ。
どちらも、touch $mytxt
あるいはecho "hello world" >> $mytxt
という形で$変数を使ってコマンドの引数を指定しているが、それがうまくいっていないらしい。
生成したmy.shを見てみると、エラーの内容が良くわかった。my.shの中身は次のようになっていた。
mytxt=~/my.txt touch echo "hello world" >>
ドルマーク付きで記述した変数が消えてしまっている。
この辺りを見るに、恐らく最初のシェルスクリプト実行時に、catコマンドが実行される際に、$myfileが変数扱いとなり、その時点では変数としては中身が空なので、my.shに書き出される際には空になってしまっている、ということなのだろう。
macOS環境では、以下のようにバックスラッシュをつけてエスケープすることで回避できた。
myshell=~/my.sh touch $myshell cat <<EOF >$myshell mytxt=~/my.txt touch \$mytxt echo "hello world" >> \$mytxt EOF
このスクリプトを実行すると以下のように意図した形でmy.shが作成される。
mytxt=~/my.txt touch $mytxt echo "hello world" >> $mytxt
bash my.shを実行すると、正しくmy.txtが生成された。
[macOSとAmazon Linux]
Amazon Linux環境で同じように実行したところ、なぜかうまくいかなかった。が、次のように変数部分の記述に{}を追加で利用することで、syntax errorは出なくなり、同じ結果を得ることができた。
myshell=~/my.sh touch $myshell cat <<EOF >$myshell mytxt=~/my.txt touch \${mytxt} echo "hello world" >> \${mytxt} EOF
上のサイトで出てきたように()で括っても良かったかもしれないが、ググる前に適当に{}で試してうまくいったので、()のケースについては試していない。
[まとめ]
シェルスクリプトに限らないけど、特殊記号を普通のテクストとして扱いたい場合はエスケープすること。
今日のブログ執筆BGMはこちら。