I have a function in my ~/.zshrc
:
findPort() {
lsof -t -i :1ドル
}
The usual invocation is findPort 3306
.
I want to run it with elevated privileges. But I get "command not found".
➜ git 🍔 sudo findPort 3306
sudo: findPort: command not found
I presume the reason is that the root user either runs as a non-interactive shell (thus does not refer to a .zshrc), or refers to a different .zshrc
.
I have seen similar questions regarding alias
, but no question regarding user-defined functions. The answers for this problem regarding alias
involves adding an alias to ~/.zshrc
:
alias sudo='nocorrect sudo '
Or perhaps:
alias sudo='sudo '
I have tried both of these solutions, and the problem still exists (yes I've relaunched the shell).
I have also tried running sudo chsh
to ensure that my root shell runs under zsh
. None of these solutions removes the "command not found" problem.
Is there a way to run my user-defined functions under sudo?
-
Also see: unix.stackexchange.com/questions/181414/… (not flagging because zsh has its own tricks)muru– muru2016年10月20日 15:41:03 +00:00Commented Oct 20, 2016 at 15:41
4 Answers 4
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
-
I need a solution that's as short as typing
sudo
. I was able to adapt your anonymous function solution to do just that. I have added the following function to my~/.zshrc
, which is a function to run other functions with elevated privileges:sdo() { sudo zsh -c "(){$functions[1ドル]} ${@:2}" }
Birchlabs– Birchlabs2016年10月20日 16:04:15 +00:00Commented Oct 20, 2016 at 16:04 -
1@Birchlabs, see editStéphane Chazelas– Stéphane Chazelas2016年10月20日 16:28:39 +00:00Commented Oct 20, 2016 at 16:28
-
Thanks; I've updated my answer to use the newer shortcut you've proposed.Birchlabs– Birchlabs2016年10月20日 16:38:48 +00:00Commented Oct 20, 2016 at 16:38
-
Adding the function definition to the script executed through sudo is smart. But not smart enough if that function use another function (autoloaded or not).Damien Flament– Damien Flament2018年11月15日 23:25:38 +00:00Commented Nov 15, 2018 at 23:25
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.
-
This does indeed work. Though it's not quite the workflow I had in mind. My desire is to remain in a prompt as my usual user, and elevate just my user-defined function
myFunc
, by typingsudo myFunc
— the same way I would elevate any process (likesudo rm -rf .
). This solution elevates my entire prompt and all future functions, as well as changing my user for all future functions — which is a bit overkill.Birchlabs– Birchlabs2016年10月24日 11:22:13 +00:00Commented Oct 24, 2016 at 11:22 -
The
sudo -s
command will launch another instance of your shell as the superuser. If you execute that command interactively, your new shell will be interactive. But the commandsudo -s findPort 3306
will run the commandfindPort 3306
in your shell as the superuser then exit with the status code of that command.Damien Flament– Damien Flament2018年11月15日 23:33:24 +00:00Commented Nov 15, 2018 at 23:33
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
This seems to work for me:
function sudo (){
args="$@"
/run/wrappers/bin/sudo -u "$USER" zsh -i -c "$args"
}