【bash】変数の値に含まれる変数を展開させる方法

背景

bashを使用して大量の実行コマンドを自動的に流したいなって思って、サクッと書けるかなって思ってたらトラップ発動したのでメモ。

やりたいことは単純。
・コマンドが複数行記述されているファイル cmd.txt を用意。
cmd.txtを一行ずつreadして実行する runcmd.shを実行するとコマンドが1行ずつ実行される。

実行したいコマンドに埋め込まれた変数が展開されずエラーに

よくある話ですが、実行したいコマンドに環境変数が埋め込まれていることがあると思います。こんな感じ。
/{$HOME}/{$FOO}/{$BAR}/run.sh
これを外部ファイルからreadしようとすると、単純な文字列として解釈されるので、{$...}が展開されないのです。

【解決法】evalを使って評価してあげる

evalコマンドを使用して文字列を評価してあげると、埋め込まれている変数が展開されて期待通りの挙動となりました!

具体的には次の通り。

path1=foo
path2=bar

# 実際は下記コマンドは外部ファイルからreadされる
# 擬似的に、内部変数を評価せず文字列として認識するようにように ' ' で宣言
cmd='${path1}/${path2}/hogehoge.sh'

echo ${cmd} # 普通に展開しようとすると変数が展開されない
            # ${path1}/${path2}/hogehoge.sh

echo $(eval echo ${cmd}) # evalで評価してあげると正しく展開された!
                         # foo/bar/hogehoge.sh

このように、evalコマンドで文字列に含まれる変数部分を再展開し、その後$( ... )で囲うことで、全体の文字列を新たな変数として再定義していることになります。ちょっと勉強になった。

【おまけ】変数が保持している文字列。これを変数として解釈させるには?

今回躓いた部分からは外れるが、色々調べたら面白いことがわかったのでついでに共有。

変数がある文字列を保持しているとする。この文字列を変数として解釈させたいことがある。
具体的には次の通り。

pro=apple
echo ${pro} # apple

macbook=pro
echo ${macbook} # ${pro}として変数が展開されているわけではないので、
                # 当然文字列として "pro" が表示

ここで変数${macbook}が保持する文字列proを変数${pro}として解釈させるにはどのようにすれば良いだろうか。裏技的だが次のように表現する。

# パターン1
echo ${!macbook} # apple

# パターン2
eval echo '$'${macbook} # apple

パターン2について補足しておくと、
1. echo${macbook}が展開される
2. 文字'$'と文字列proが合体し、一つの文字列$proができる
3. evalコマンドが$proを評価し、最終的にappleが出力される

まとめ

シェルスクリプトを書いているとよく文字列操作で躓いてしまいます。あまりゴリゴリ書かないので不慣れな部分が多いのですが、、
それでも他言語と比べて文字列操作がシビアだと思うんですよ。ちゃんと理解してないとすぐエラー。嫌になりますね。
なのでこういう細かい仕様は目を逸らさずちゃんと理解しないとダメですね!

Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account log in.