Wrapping Shell Commands and Keep the Original Name
Shell by snap713 is licensed under CC BY-NC-ND
I wanted to wrap a shell command (that I had no ownership of) so that I could execute additional statements around the original command. In the following sections, I present two scenarios for wrapping shell commands.
I do want to say there is a possibility that the wrapped command could potentially cause some issues down the road, however, I have not encountered any as of yet with my limited testing.
You can create a new function with the name of the executable that you wish to wrap. This works because the function lookup occurs before the executable lookup on your PATH.
# In .zshrc or .bashrc
ping() {
echo "before"
/sbin/ping $@
local exit_code=$?
echo "after"
return $exit_code
}
The function itself uses the original executable (/sbin/ping
) by specifying the full-path along with the original parameters (via $@
). We grab the exit code (via $?
) and store it to be returned at the ending. This ensures that the new function still operates as it did before.
❯ ping google.com -c 1
before
PING google.com (172.217.0.238): 56 data bytes
64 bytes from 172.217.0.238: icmp_seq=0 ttl=115 time=25.484 ms
--- google.com ping statistics ---
1 packets transmitted, 1 packets received, 0.0% packet loss
round-trip min/avg/max/stddev = 25.484/25.484/25.484/0.000 ms
after
We can see that that the ping
command still works as expected, but we also now have additional statements around it.
The approaches for dealing with a function were mostly borrowed from this Unix StackExchange answer.
# Not a function I have defined (i.e., defined in a library I sourced)
function example() {
echo "inside example function ($@)"
}
# In .zshrc or .bashrc
eval _"$(declare -f example)"
example() {
echo "before function"
_example $@
local exit_code=$?
echo "after function"
return $exit_code
}
The eval
is essentially renaming the original function (example
) to have a leading underscore (_example)
. A new function is then defined that overrides example
, and actually uses the original’s function (now called _example
). You define additional statements before and after the wrapped function, in a similar fashion to how it was handled with wrapping an executable. The exit code is also preserved from the wrapped function.
❯ example 5
before
inside example function 5
after
One downside to the above approach is that the old function is still present (_example
). While the following only works in ZSH, it is possible to directly override the original function by using the original’s definition when creating the new function. To note, I do find the former approach simpler.
# .zshrc
functions[example]='
(){
echo "before"
'$functions[example]';
} "$@";
local exit_code=$?
echo "after"
return $exit_code'