Wil*_*ard 6 shell function shell-builtin
我知道可以通过引用命令本身来绕过别名。
但是,似乎如果内置命令被同名函数“遮蔽”,则无法执行底层内置命令,除非...使用内置命令。如果你能做到。
引用 bash 手册页 (at LESS='+/^COMMAND EXECUTION' man bash
):
COMMAND EXECUTION
After a command has been split into words, if it results in a simple
command and an optional list of arguments, the following actions are
taken.
If the command name contains no slashes, the shell attempts to locate
it. If there exists a shell function by that name, that function is
invoked as described above in FUNCTIONS. If the name does not match a
function, the shell searches for it in the list of shell builtins. If
a match is found, that builtin is invoked.
Run Code Online (Sandbox Code Playgroud)
那么,是否可以在不启动新 shell 的情况下从以下内容中恢复?
unset() { printf 'Haha, nice try!\n%s\n' "$*";}
builtin() { printf 'Haha, nice try!\n%s\n' "$*";}
command() { printf 'Haha, nice try!\n%s\n' "$*";}
Run Code Online (Sandbox Code Playgroud)
我什至没有添加readonly -f unset builtin command
。如果它是可以从上面的恢复,认为这是一个加分题:你还能恢复,如果所有三个功能标记为只读?
我在 Bash 中提出了这个问题,但我对它对其他 shell 的适用性也很感兴趣。
如果所有三个功能都标记为只读,你还能恢复吗?
是的,通常你可以,但这并不意味着你应该。
正如您可以通过附加调试器并按照anishsane 对该问题的回答中所示的调用来取消设置只读变量,您也可以取消设置只读函数,将其名称传递给使用调试器。unbind_variable
unbind_func
当它们不是只读时(如果确实如此),这不是一种合理的方法。在这种情况下,您应该使用cuonglm 的解决方案,它利用了unset
POSIX 模式下的处理方式。 该解决方案是您可能在现实生活中实际使用的东西。
由于在您绕过readonly
调试器后,无法保证您的 shell 会合理运行,因此我建议在有更合理的替代方法时避免使用它,例如退出并重新启动您的 shell 或使用新的 shell 替换您的 shell exec
。
话虽如此,这里是anishsane 的方法,适用于 unset 函数而不是变量:
cat <<EOF | sudo gdb
attach $$
call unbind_func("unset")
call unbind_func("builtin")
call unbind_func("command")
detach
EOF
Run Code Online (Sandbox Code Playgroud)
请注意,in$$
扩展为shell 的进程ID,因为EOF
in 的任何部分都没有<<EOF
被引用。
我在 Ubuntu 16.04 LTS 上的 Bash 4.3.48(1)-release 上对此进行了测试,并且成功了。您需gdb
要这样做,尽管它可以适用于其他调试器。如anishsane评论,从管道cat
是为了避免死锁其中,让输入到过程gdb
的是,一个gdb
已停止。我相信它实现了这个目标,因为在两个或多个命令的管道中,Bash 在一个子 shell 中运行每个命令。但我不确定这是否是最稳健的方式。然而,最终并不能保证这无论如何都有效,因为 Bash 假设只读变量和函数不会改变是完全合理的。在实践中,我的猜测是这几乎总是有效。
要按照所写的方式使用此技术,您需要sudo
安装并且需要能够sudo
root。当然,您可以将其替换为另一种提权方法。根据您正在运行的操作系统及其配置方式,您可能可以sudo
完全省略并gdb
以自己的身份而不是 root 身份运行。例如,Linux 内核会参考 的值/proc/sys/kernel/yama/ptrace_scope
,您可以通过 sysctl 设置该值并可以读取或(以 root 身份)写入,以确定哪些进程可以调试其他进程。如果值为1
,则只有进程的直接父进程——或任何以 root 身份运行的进程——可以调试它。大多数最新的 GNU/Linux 系统都将其设置为1
,这就是我将sudo
.
对 Linux 内核行为的描述有些过于简单化,因为ptrace_scope
允许使用其他值,并且1
可以调整所需的关系。有关完整详细信息,请参阅相关文档。
当bash
处于 posix 模式时,一些内置函数被认为是特殊的,这符合 POSIX 标准。
这些特殊内置函数的一个特别之处是,它们在命令查找过程中的函数之前找到。利用这个优势,您可以尝试:
$ unset builtin
Haha, nice try!
builtin
$ set -o posix
$ unset builtin
$ builtin command -v echo
echo
Run Code Online (Sandbox Code Playgroud)
set
但如果被名为 的函数覆盖,它就不起作用set
:
$ set() { printf 'Haha, nice try!\n%s\n' "$*";}
$ set -o posix
Haha, nice try!
Run Code Online (Sandbox Code Playgroud)
在这种情况下,您只需设置POSIXLY_CORRECT
为bash
进入 posix 模式,然后您就拥有了所有特殊的内置命令:
$ POSIXLY_CORRECT=1
Run Code Online (Sandbox Code Playgroud)