"command not found" when sudo'ing function from ~/.zshrc
sudo
runs commands directly, not via a shell, and even if it ran a shell to run that command, it would be a new shell invocation, and not one that reads your ~/.zshrc
(even if it started an interactive shell, it would probably read root
's ~/.zshrc
, not yours unless you've configured sudo
to not reset the $HOME
variable).
Here, you'd need to tell sudo
to start a new zsh
shell, and tell that zsh
to read your ~/.zshrc
before running that function:
sudo zsh -c '. $0; "$@"' ~/.zshrc findPort 3306
Or:
sudo zsh -c '. $0; findPort 3306' ~/.zshrc
Or to share your current zsh functions with the new zsh
invoked by sudo
:
sudo zsh -c "$(functions); findPort 3306"
Though you might get an arg list too long error if you have a lot of functions defined (like when using the completion system). So you may want to limit it to the findPort
function (and every other function it relies on if any):
sudo zsh -c "$(functions findPort); findPort 3306"
You could also do:
sudo zsh -c "(){$functions[findPort]} 3306"
To embed the code of the findPort
function in an anonymous function to which you pass the 3306 argument. Or even:
sudo zsh -c "$functions[findPort]" findPort 3306
(the inline script passed to -c
is the body of the function).
You could use a helper function like:
zsudo() sudo zsh -c "$functions[$1]" "$@"
Do not use:
sdo() { sudo zsh -c "(){$functions[$1]} ${@:2}" }
As the arguments of sdo
would undergo another level of shell parsing. Compare:
$ e() echo "$@"
$ sdo e 'tname;uname'
tname
Linux
$ zsudo e 'tname;uname'
tname;uname
A very simple solution for me was to invoke an interactive shell using sudo -s
, which appears to work on my macOS bundled version of zsh (5.2). This provides me with the functions from my ~/.zshrc
.
From the sudo manpage, which doesn't really hint at this behaviour:
-s, --shell
Run the shell specified by the SHELL environment variable if it is set or the shell specified by the invoking user's password database entry. If a command is specified, it is passed to the shell for execution via the shell's -c option. If no command is specified, an interactive shell is executed.
I'm not sure what your use case is, but I suspect you're looking for an interactive solution.
I was able to produce a reusable shorthand for one of the solutions described in Stéphane Chazelas' answer. [Edit: Stéphane has iterated on the original shortcut I proposed; the code described here is a newer version, of his making]
Put this into your ~/.zshrc
:
sdo() sudo zsh -c "$functions[$1]" "$@"
Now you can use sdo
as a "sudo
for user-defined functions only".
To confirm that sdo
works: you can try it out on a user-defined function that prints your username.
➜ birch@server ~/ test() whoami
➜ birch@server ~/ test
birch
➜ birch@server ~/ sdo test
root