Ruby on Rails (ActiveSupport) の alias_method_chain1 を Bash で使えるように実装しました。ソースコードは GitHub (https://github.com/suzuki-kei/bash-alias-method-chain) で公開しています。
(Ruby) alias_method_chain とは
Ruby on Rails (ActiveSupport) の alias_method_chain
を使うと、既存のメソッドの前後に処理を追加できます:
module Hello
def hello(name)
puts "Hello #{name}"
end
def hello_with_trace(name)
puts '---- BEGIN ----'
hello_without_trace(name)
puts '---- END ----'
end
# hello_without_trace は元の hello のエイリアスになり,
# 新しく hello は hello_with_trace のエイリアスになる.
alias_method_chain :hello, :trace
end
hello
と呼び出すと hello_with_trace
が実行されます:
> include Hello
> hello('Taro')
---- BEGIN ----
Hello Taro
---- END ----
(Bash) alias_method_chain
以下のように実装しました。
# スクリプト中で alias を使用する場合に必要.
shopt -s expand_aliases
function alias_method_chain {
local -r name="$1"
local -r functionality="$2"
case $(type -t "$name") in
alias)
eval "$(cat <<EOS
function ${name}_without_${functionality} {
$(type "$name" | sed -r "s/^$name is aliased to \`|'$//g") "\$@"
}
builtin alias ${name}=${name}_with_${functionality}
EOS)"
return 0
;;
builtin | file | function)
eval "$(cat <<EOS
function ${name}_without_${functionality} {
${name} "\$@"
}
builtin alias ${name}=${name}_with_${functionality}
EOS)"
return 0
;;
*)
echo "ERROR: $(type -t '${name}') is unsupported type." >&2
return 1
;;
esac
}
使用例:
source alias-method-chain.sh
# スクリプト中で alias を使用する場合に必要.
shopt -s expand_aliases
function hello {
echo "Hello $1"
}
function hello_with_trace {
echo '---- BEGIN ----'
hello_without_trace "$@"
echo '---- END ----'
}
alias_method_chain hello trace
hello 'Taro'
実行結果:
---- BEGIN ----
Hello Taro
---- END ----
解説
スクリプト中で alias を使う方法
Bash スクリプトの中で alias
を使う場合は以下の指定が必要です。
# スクリプト中で alias を使用する場合に必要.
shopt -s expand_aliases
...
type の結果で分岐する
Bash のビルトイン関数である type
を使うと、指定した名前がどのように解釈されるのか調べることができます。
$ type bash
bash is /bin/bash
-t
オプションで種類が分かります。type -t
の結果は alias
, keyword
, function
, builtin
, file
もしくは空文字列になります。
$ type -t bash
file
この結果で alias_method_chain
の処理を分岐します(種類によって実装を変える必要があるため)。
alias を置き換える
alias を置き換える実装は以下のようにしました。これは type
の出力結果に依存してしまっているので不本意です。他に良い方法があれば改善したい部分です。
...
case $(type -t "$name") in
alias)
eval "$(cat <<EOS
function ${name}_without_${functionality} {
$(type "$name" | sed -r "s/^$name is aliased to \`|'$//g") "\$@"
}
builtin alias ${name}=${name}_with_${functionality}
EOS)"
return 0
;;
...
-
alias_method_chain は Ruby on Rains 5 で非推奨になりましたが、実現したいことが伝わりやすいと思い alias_method_chain と書きました。 ↩