変数に格納された文字列”~”(チルダ)が展開されなかった件(eval を使おう)
$HOMEを表す”~”(チルダ)が変数に文字列として格納されているときに展開されない
こんな風に、ログのパスを決めておいて、そのパスにログ出力(リダイレクト)できるものかと思っていた。
$ log="~/.config/log/test.log" $ echo "Start" >> $log
結果は以下の通り。
$ echo "Start" >> $log bash: ~/.config/log/test.log: No such file or directory
なんで~?ってことで調べ始めた。
目次
本文
1. 基礎調査
◆パスをベタ書きして確認する
$ echo "Start" >> "~/.config/log/test.log" bash: ~/.config/log/test.log: No such file or directory
→ パスを文字列として与えるとNG。
$ echo "Start" >> ~/.config/log/test.log (エラーなし→成功)
→ パスをパスとして与えるとOK
◆フルパスならどうか?
$ echo $HOME /c/Users/user01 $ echo "Start" >> "/c/Users/user01/.config/log/test.log" (エラーなし→成功)
→ なんか文字列として与えられた "~"(チルダ)が怪しい感じがする。
2. 調査結果
参考にさせていただいた。
"~"(チルダ)を含む文字列は、シェル実行時に$HOMEの値として展開されないらしい。
同じ実験をしてみた。
$ echo $HOME /c/Users/user01 $ echo ~ /c/Users/user01 ・・・(A) $ echo "~" ~ ・・・(B) $ echo '~' ~ ・・・(C)
チルダが文字列として与えられた(B)(C)のパターンで、チルダがチルダという文字として解釈されている。
一方で(A)では、いわゆるHOMEとして解釈され展開されている。
3. 文字列内のチルダを展開させたかったら「eval」コマンドを使おう
$ eval echo "~" /c/Users/user01
→ "~"(チルダ)が展開されてechoされた。
$ eval 'echo "~"' ~
evalに与えるものを' 'シングルクォートでくくると結果が異なる。
4. 今回の課題の答えを探る
$ eval echo "Start1" ">>" "~/.config/log/test.log" → 成功。logに出力されていた。
ということで、evalに与えるコマンドの指定の仕方を3種類試してみた。
$ eval echo "Start2" ">>" $log・・・(A) →成功 $ eval "echo \"Start3\" >> $log"・・・(B) →成功 $ eval 'echo \"Start3\" >> $log'・・・(C) bash: ~/.config/log/test.log: No such file or directory
evalに与えるものは、””ダブルクォートでくくり内容展開される形で与えた方が良いみたい。(B)
''シングルクォートだと展開されない。 (C)
主要なところを文字列としてevalに与えればうまいこと解釈してくれるみたいだが、なんとなく気持ち悪い。(A)
evalコマンドは、上記のページからすると、powershellの「Invoke-Expression」みたいなものらしい。
もう一つの解としては、$log変数自体に展開されたパスで格納して使いまわす手法がある。
$ logtmp="~/.config/log/test.log" $ echo $logtmp ~/.config/log/test.log $ log=`eval "echo $logtmp"` ・・・(A) $ echo $log /c/Users/hisabo/.config/log/test.log
log変数にチルダを展開して設定。echo使わなければならないのが、ちょっとダサい。
(ほかの方法があるかは後日にまわす)
バージョン情報
$ bash --version GNU bash, version 4.4.23(1)-release (x86_64-pc-msys) Copyright (C) 2016 Free Software Foundation, Inc.