サイト内検索
Translation here

2016/03/25(金)変数にコマンドを入れて実行したい

お疲れ様です、野村です。

今日もシェルスクリプトの話です。
というかここ連日ずっとこればかりやってますが、ハマってる訳ではないです、まだ。
しかしながら、常にハマるかもしれないという危機感は持ってます…。

では掲題の話。
たとえば環境によって実行パスやコマンドが違う場合、ファイルを使い回している時に代替コマンドが使えたらなぁ、なんて思っていたんですね。
今まではこうしてました。
command1 || command2
こうすると、まずコマンド1を実施して、失敗したらコマンド2を実施するようになります。

しかしですね、この方法はとんでもなく恐ろしい問題が潜んでいます。

コマンド自体が使えなくなる場合

今日ぶちあたったのはこれです。
いつものパターンで行くと、
command1 || command2 || command3
とやればいいんですが、野村はここで不吉なことを考えてしまいました。
  • 「第4、第5のコマンドが現れるのでは?」
  • 「そもそもそのコマンドって間違ってるんじゃね?」
  • 「やだ!パスが変わることなんて全く考慮されてない作りになってる!」
など、とにかく爆弾のように見えて仕方がなかったのです。

どう考えてもこのまま放置をするとヤバいので、何とかしないと精神衛生上好ましくない。

で、探しました。

Linuxのshell変数にコマンドを入れて実行する方法

まず結論から言うと、evalを使います。
#command1 || command2 || command3
command="command arg1 arg2"

eval ${command}
おぉっ、出来た!

この方法だと、環境に合わせてcommandを書き換えれば良いのでとても便利!
コンフィグファイルにこのまま入れてしまって、あとは呼び出し側でゴニョゴニョやって配れば一括で全てのファイルを書き換えられます。

とはいえ、環境毎で書き換える案は普遍性の観点では魅力的ながら、
運用時に常に意識しておく必要があるのはいただけない。
インストーラ側でそこを吸収できればいいんですが、あいにくそう言うものではないので、
色々考えましたが、以下の形に落ち着きました。

Call_master.sh
COMMAND

Call_to.sh
function COMMAND(){
eval $command1 || eval $command2
}

Call.conf
command1
command2

とりあえずコマンドは全部設定します。
ダメだったらたらい回しにします。
呼び出し元はやりたいことだけ書きます。

関数型思考は素晴らしい!

と言いたいだけでした、すみません。*1

このやり方は色々なところで応用が利きます。
賢い設計をすればエラーハンドリングもこの方法で実現が容易になるでしょう。
レガシーコードになりがちなシェルですが、スキトラ*2を前提にした開発だとテストはラクにサボりたいですね!
他社や他業界はどうか分かりませんが、プログラマーのスキルは如何にサボれるかに尽きると思ってます。

最後に、納期はちゃんと守ろうね!守ってね!

野村からの切実なお願いでした。

*1 : オブジェクト型でも同じことは出来ます。

*2 : Skill Transfer 大体はベンダーで仕事をやる時にクライアントサイドのエンジニアに運用できるようにしてくれと言われるのがこれです。

OK キャンセル 確認 その他