我定义了一个 shell 函数(我们称之为clock
),我想将其用作另一个命令的包装器,类似于该time
函数,例如clock ls -R
。
我的 shell 函数执行一些任务,然后以 结束exec "$@"
。
我希望这个函数即使在使用 shell 内置函数时也能工作,例如应该输出内置函数clock time ls -R
的结果,而不是可执行文件。但最终总是运行命令。time
/usr/bin/time
exec
如何使我的 Bash 函数作为包装器工作,并且还接受 shell 内置函数作为参数?
编辑:我刚刚了解到这time
不是 Bash 内置的,而是一个特殊保留字与管道相关。即使它不适用于,我仍然对内置解决方案感兴趣,time
但更通用的解决方案会更好。
答案1
您定义了一个 bash 函数。因此,调用该函数时,您已经处于 bash shell 中。因此,该函数可能看起来就像这样:
clock(){
echo "do something"
$@
}
该函数可以通过 bash 内置命令、特殊保留字、命令和其他定义的函数来调用:
别名:
$ clock type ls
do something
ls is aliased to `ls --color=auto'
bash 内置命令:
$ clock type type
do something
type is a shell builtin
另一个功能:
$ clock clock
do something
do something
可执行文件:
$ clock date
do something
Tue Apr 21 14:11:59 CEST 2015
答案2
启动 shell 内置命令或 shell 关键字的唯一方法是启动新 shell,因为 exec “用给定的命令替换 shell”。您应该将最后一行替换为:
IFS=' '; exec bash -c "$*"
这对于内置字和保留字都有效;原理是一样的。
答案3
如果包装器需要插入代码前给定的命令,别名会起作用,因为它们在很早的阶段就被扩展了:
alias clock="do this; do that;"
别名几乎是逐字插入到别名词的位置,因此尾随;
很重要 - 它使其clock time foo
扩展为do this; do that; time foo
。
您可以滥用它来创建魔法别名甚至绕过引用。
用于插入代码后一个命令,您可以使用“DEBUG”挂钩。
shopt -s extdebug
trap "<code>" DEBUG
具体来说:
shopt -s extdebug
trap 'if [[ $BASH_COMMAND == "clock "* ]]; then
eval "${BASH_COMMAND#clock }"; echo "Whee!"; false
fi' DEBUG
钩子仍在运行前命令,但当它返回时,false
它会告诉 bash 取消执行(因为钩子已经通过 eval 运行了它)。
作为另一个示例,您可以使用它来将别名command please
设为sudo command
:
trap 'case $BASH_COMMAND in *\ please) eval sudo ${BASH_COMMAND% *}; false; esac' DEBUG
答案4
到目前为止我能想到的唯一解决方案是进行案例分析以区分第一个参数是命令,内置参数还是关键字,并在最后一种情况下失败:
#!/bin/bash
case $(type -t "$1") in
file)
# do stuff
exec "$@"
;;
builtin)
# do stuff
builtin "$@"
;;
keyword)
>&2 echo "error: cannot handle keywords: $1"
exit 1
;;
*)
>&2 echo "error: unknown type: $1"
exit 1
;;
esac
但是,它不能处理time
,因此可能存在更好(且更简洁)的解决方案。