目的
-
bashで複数のファイルをRなどにパラメータと共に渡すときに、デバックモードになると変数の値を表示させたいなーと常々思っていました。
-
その方法を思いついたので備忘録として残したいと思います。何かのお役に立てれば嬉しいです
方針
- 方針はシンプルにシンプルに。なので
getopts
なんかは使わ(え)ないです。 -
bash
で:
は何もしない性質を利用します(今日これを見つけました。先般諸氏ありがとうございます) -
test
コマンド[]
の論理連結で[] && []
などを多用します。 - ユーザ関数を定義しておきます(
abort()
)。次のように記述します。
function abort {
echo "$@" 1>&2
exit 1
}
これでファイルがないと止められます。
[ -f $filename ] || abort "$filenameがないので終了します"
メカニズムですが、ファイルがないと[]
の中がFALSE
と判定されて||
で次のコマンドに進み、abort()
関数が呼ばれ、メッセージを出して止まります。エラーコードは1
です。
引数でデバックモードの切り替え
引数でmode
が設定されれば、表示して何もしないようにするには下記のようにしました。
mode=${1}; # 引数をmodeに設定します。
debug=":"; # これがポイント。$modeが設定されていない場合に用いられます
[ ! -z $mode ] && dryrun="echo dryrun>"
hoge=$(何かの処理);
$dryrun "hoge=${hoge}";
mode
が設定されないと:
で何もしません。設定されるとecho dryrun>
が呼ばれて、後ろの文字列も表示されます。dryrun>
は任意でどうぞお使いくだしあ。実際の例を見てみましょう。
実際の例
mode
にdryrun
とdebug
の2つの機能を持たせたコードの例です。
# !/usr/local/bin/bash
abort(){ echo "$@" 1>&2 ; exit; }
# 引数の解析
inputfile=${1};
[ -z ${inputfile} ] && abort "inputfileが設定されていません"
[ -f ${inputfile} ] || abort "${inputfile}がありません"
shift; [ $# -gt 0 ] && mode=${1}
debug=":"
[ ! -z $mode ] && [ $mode = "dryrun" ] && dryrun="echo dryrun>"
[ ! -z $mode ] && [ $mode = "debug" ] && debug="echo debug>"
_dir=$( dirname ${inputfile} );
$debug "_dir=${_dir}";
backup="${inputfile}.bak";
[ ! -f $backup ] && $dryrun cp $inputfile $backup;
ちょっと解説
[ -z ${inputfile} ] &&
は変数が設定されていないと次に進みます。
[ -f ${inputfile} ] ||
は変数がファイルでないならは次に進みます。
shift
は引数をシフトします。そのため、次の引数呼び出しは必ず$1
でOKです。ただし、引数があるかを[ $# -gt 0 ]
で調べています。
debug=":"
が今回組み合わせを思いついた方法です。通常モードは$debug
以降を何もしません。
mode
が設定されればdebug
はecho
する機能に切り替わります。
ここで[ ! -z $mode ]
ですが$mode
が設定されていないと[ = "test" ]
と解釈され、すなわち空白と"test"
が比較されます。そんなことできない( -bash: [: =: 単項演算子が予期されます
)と怒られるので、設定を確認するためのものです。その前の[ $# -gt 0 ]
でチェックしているので設定が担保されているのですが、バグが少ないような書き方として載せました。
mode
が設定されない通常モードなら、_dir
は表示しませんがdebugモードなら表示されます。
注意としてこのスクリプトではdebug
モードの時はdryrun
になにも設定しないので最終行は実行されてしまいます。つまりバックアップファイルが生成されます。この辺りはご自身の要求で変更してみてください。
mode
がdryrun
なら最終行は期待通りコマンド(cp $inputfile $backup
)を変数展開し表示するだけで実行はしません。
幾つか実行してみますね。
~$ ll a*
ls: cannot access 'a*': No such file or directory
~$ tmp.bash
inputfileが設定されていません
~$ tmp.bash a
aがありません
~$ touch a
~$ tmp.bash a dryrun
dryrun> cp a a.bak
~$ ll a*
-rw-r--r-- 1 osaka 0 6 16 23:03 a
~$ tmp.bash a debug
debug> _dir=.
~$ ll a*
-rw-r--r-- 1 osaka 0 6 16 23:03 a
-rw-r--r-- 1 osaka 0 6 16 23:03 a.bak
~$ rm a.bak
~$ tmp.bash a
~$ ll a*
-rw-r--r-- 1 osaka 0 6 16 23:03 a
-rw-r--r-- 1 osaka 0 6 16 23:03 a.bak
まあ、動いていそうです。
参考にさせていただきましたいくつかのリンクを貼っておきます。
先輩の方々のお陰で何か成長している気がします。
何かのお役に立てれば嬉しいです。