"alias method chain"在 Bash 或 Zsh 中



这是(或者至少是(Ruby中的常见模式,但是我不知道如何在Zsh或Bash中做到这一点。

假设我有一个名为"whoosiwhatsit"的shell函数,我想在特定项目中覆盖它,同时仍然以不同的名称保持原始可用。

如果我不知道更好,我可能会尝试创建一个别名来指向 whoosiwhatsit,然后创建一个使用该别名的新"whoosiwhatsit"函数。 当然,这是有效的,因为别名将引用新函数。

有什么方法可以完成我所说的吗?

别名非常弱。不过,您可以使用函数来做到这一点。请考虑以下工具:

#!/usr/bin/env bash
PS4=':${#FUNCNAME[@]}:${BASH_SOURCE}:$LINENO+'
rename_function() {
local orig_definition new_definition new_name retval
retval=$1; shift
orig_definition=$(declare -f "$1") || return 1
new_name="${1}_"
while declare -f "$new_name" >/dev/null 2>&1; do
new_name+="_"
done
new_definition=${orig_definition/"$1"/"$new_name"}
eval "$new_definition" || return
unset -f "$orig_definition"
printf -v "$retval" %s "$new_name"
}
# usage: shadow_function target_name shadowing_func [...]
# ...replaces target_name with a function which will call:
# shadowing_func target_renamed_to_this number_of_args_in_[...] [...] "$@"
shadow_function() {
local shadowed_func eval_code shadowed_name shadowing_func shadowed_func_renamed
shadowed_name=$1; shift
shadowing_func=$1; shift
rename_function shadowed_func_renamed "$shadowed_name" || return
if (( $# )); then printf -v const_args '%q ' "$@"; else const_args=''; fi
printf -v eval_code '%q() { %q %q %s "$@"; }' 
"$shadowed_name" "$shadowing_func" "$shadowed_func_renamed" "$# $const_args" 
eval "$eval_code"
}

。以及这些工具的以下示例应用程序:

whoosiwhatsit() { echo "This is the original implementation"; }
override_in_directory() {
local shadowed_func=$1; shift
local override_cmd_len=$1; shift
local override_dir=$1; shift
local -a override_cmd=( )
local i
for (( i=1; i<override_cmd_len; i++)); do : "$1"
override_cmd+=( "$1" ); shift
done
: PWD="$PWD" override_dir="$override_dir" shadowed_func="$shadowed_func"
: override_args "${override_args[@]}"
if [[ $PWD = $override_dir || $PWD = $override_dir/* ]]; then
[[ $- = *x* ]] && declare -f shadowed_func >&2 # if in debugging mode
"${override_cmd[@]}"
else
"$shadowed_func" "$@"
fi
}
ask_the_user_first() {
local shadowed_func=$1; shift;
shift # ignore static-argument-count parameter
if [[ -t 0 ]]; then
read -r -p "Press ctrl+c if you are unsure, or enter if you are"
fi
"$shadowed_func" "$@"
}
shadow_function whoosiwhatsit ask_the_user_first
shadow_function whoosiwhatsit 
override_in_directory /tmp echo "Not in the /tmp!!!"
shadow_function whoosiwhatsit 
override_in_directory /home echo "Don't try this at home"

最终结果是一个whoosiwhatsit函数,当它的 stdin 是 TTY 时,它会在用户执行任何操作之前询问用户,并在/tmp/home下运行时中止(使用不同的消息(。


也就是说,我不宽恕这种做法。将上述内容视为智力练习。:)

在 bash 中,有一个名为BASH_ALIASES的内置变量,它是一个包含当前别名的关联数组。 当你更新它时,语义有点不一致(RTM!(,但如果你限制自己阅读BASH_ALIASES,你应该能够为自己编写一个实现别名链接的shell函数。

通过可以选择调用其被覆盖的内置或命令的函数创建单个级别的覆盖是很常见且得到很好的支持:

# Make all cd commands auto-exit on failure
cd() { builtin cd "$@" || exit; }
# Make all ssh commands verbose
ssh() { command ssh -vv "$@"; }

它不会超越一个链接,但它完全是 POSIX,在实践中通常比尝试用 Bash 编写 Ruby 更好。

最新更新