catコマンドでシェルスクリプトファイルを作成してハマった話とか

シェルスクリプトで条件判定をして、ある条件を満たした場合に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" >> 

 

ドルマーク付きで記述した変数が消えてしまっている。

 

unix.stackexchange.com

 

この辺りを見るに、恐らく最初のシェルスクリプト実行時に、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が生成された。

 

[macOSAmazon Linux]

Amazon Linux環境で同じように実行したところ、なぜかうまくいかなかった。が、次のように変数部分の記述に{}を追加で利用することで、syntax errorは出なくなり、同じ結果を得ることができた。

myshell=~/my.sh
touch $myshell

cat <<EOF >$myshell
mytxt=~/my.txt
touch \${mytxt}
echo "hello world" >> \${mytxt}
EOF

上のサイトで出てきたように()で括っても良かったかもしれないが、ググる前に適当に{}で試してうまくいったので、()のケースについては試していない。

 

[まとめ]

シェルスクリプトに限らないけど、特殊記号を普通のテクストとして扱いたい場合はエスケープすること。 

 

今日のブログ執筆BGMはこちら。

www.youtube.com