Shell – How to bypass bash functions called `command`, `builtin` and `unset`

functionshellshell-builtin

I am aware that aliases can be bypassed by quoting the command itself.

However, it seems that if builtin commands are "shadowed" by functions with the same names, there is no way to execute the underlying builtin command except…by using a builtin command. If you can get to it.

To quote the bash man page (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.

So, is it possible to recover from the following, without starting a new 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' "$*";}

I didn't even add readonly -f unset builtin command. If it is possible to recover from the above, consider this a bonus question: can you still recover if all three functions are marked readonly?


I came up with this question in Bash, but I'm interested in its applicability to other shells as well.

Best Answer

When bash is in posix mode, some builtins are considered special, which is compliant with POSIX standard.

One special thing about those special builtins, they are found before function in command lookup process. Taking this advantage, you can try:

$ unset builtin
Haha, nice try!
builtin
$ set -o posix
$ unset builtin
$ builtin command -v echo
echo

though it does not work if set is overridden by a function named set:

$ set() { printf 'Haha, nice try!\n%s\n' "$*";}
$ set -o posix
Haha, nice try!

In this case, you just have to set POSIXLY_CORRECT to make bash enter posix mode, then you have all special builtins:

$ POSIXLY_CORRECT=1