从〜/ .zshrc sudo'ing功能时,“找不到命令”


10

我有一个功能~/.zshrc

findPort() {
    lsof -t -i :$1
}

通常的调用是findPort 3306

我想以提升的特权运行它。但是我得到“找不到命令”。

  git 🍔 sudo findPort 3306
sudo: findPort: command not found

我猜想原因是root用户或者以非交互式shell运行(因此不引用.zshrc),或者引用了别的 .zshrc

我曾经看到过类似的问题alias,但是关于用户定义的函数却没有问题。有关此问题的答案alias涉及为添加别名~/.zshrc

alias sudo='nocorrect sudo '

也许:

alias sudo='sudo '

我已经尝试了这两种解决方案,但问题仍然存在(是的,我已经重新启动了外壳)。

我还尝试运行sudo chsh以确保我的根shell在.NET下运行zsh。这些解决方案都不能消除“找不到命令”问题。

有没有办法在sudo下运行用户定义的函数?


另请参见:unix.stackexchange.com/questions/181414/…(未标记,因为zsh有其自己的技巧)
muru

Answers:


13

sudo直接运行命令,而不是通过外壳程序运行,即使运行外壳程序来运行该命令,也将是新的外壳程序调用,而不是读取您的命令~/.zshrc(即使它启动了交互式外壳程序,也可能会读取root~/.zshrc,除非您已配置sudo为不重置该$HOME变量,否则请不要使用您的)。

在这里,您需要告诉sudo您启动一个新的zshshell,并告诉zsh~/.zshrc在运行该功能之前先阅读一下:

sudo zsh -c '. $0; "$@"' ~/.zshrc findPort 3306

要么:

sudo zsh -c '. $0; findPort 3306' ~/.zshrc

或与以下zsh调用的新函数共享您当前的zsh函数sudo

sudo zsh -c "$(functions); findPort 3306"

如果您定义了很多函数(例如使用完成系统),则可能会出现一个arg列表太长的错误。因此,您可能希望将其限制为该findPort函数(以及它依赖的所有其他函数):

sudo zsh -c "$(functions findPort); findPort 3306"

您也可以这样做:

sudo zsh -c "(){$functions[findPort]} 3306"

要将findPort函数的代码嵌入到传递3306参数的匿名函数中。甚至:

sudo zsh -c "$functions[findPort]" findPort 3306

(传递给的内联脚本-c 函数的主体)。

您可以使用以下辅助功能:

zsudo() sudo zsh -c "$functions[$1]" "$@"

不使用:

sdo(){sudo zsh -c“(){$ functions [$ 1]} $ {@:2}”}

由于的参数sdo将经历另一个层次的shell解析。相比:

$ e() echo "$@"
$ sdo e 'tname;uname'
tname
Linux
$ zsudo e 'tname;uname'
tname;uname

我需要一个与输入一样短的解决方案sudo。我能够调整您的匿名函数解决方案来做到这一点。我向我添加了以下函数~/.zshrc,该函数可以以提升的特权运行其他函数sdo() { sudo zsh -c "(){$functions[$1]} ${@:2}" }
Birchlabs

1
@Birchlabs,看到编辑
斯特凡Chazelas

谢谢; 我已经更新了答案,以使用您建议的较新的快捷方式。
Birchlabs

将功能定义添加到通过sudo执行的脚本中是很聪明的。但是,如果该功能使用其他功能(是否自动加载),则不够智能。
Damien Flament

3

对我来说,一个非常简单的解决方案是使用调用一个交互式shell sudo -s,它似乎在我的macOS捆绑版本的zsh(5.2)上运行。这为我提供了我的功能~/.zshrc

在sudo联机帮助页上,它并不能真正暗示这种行为:

-s,--
shell如果设置了SHELL环境变量指定的外壳程序,或者运行调用用户的密码数据库条目指定的外壳程序,则运行该外壳程序。如果指定了命令,则通过外壳的-c选项将其传递到外壳以执行。如果未指定命令,则执行交互式外壳程序。

我不确定您的用例是什么,但是我怀疑您正在寻找一种交互式解决方案。


这确实有效。尽管这并不是我所想到的工作流程。我的愿望是像平时一样留在提示符下,并myFunc通过键入仅提升用户定义的函数sudo myFunc-与提升任何进程(如sudo rm -rf .)的方式相同。该解决方案提升了我的整个提示和所有将来的功能,以及将我的用户更改为所有将来的功能-这有点过头了。
Birchlabs

sudo -s命令将以超级用户身份启动您的Shell的另一个实例。如果以交互方式执行该命令,则新外壳将是交互式的。但是该命令sudo -s findPort 3306将以findPort 3306超级用户的身份在您的Shell中运行该命令,然后以该命令的状态代码退出。
Damien Flament

2

我能够为StéphaneChazelas的答案中描述的一种解决方案提供可重复使用的速记。[编辑:斯特凡(Stéphane)重复了我提出的原始快捷方式;这里描述的代码是他的更新版本]

将其放入您的~/.zshrc

sdo() sudo zsh -c "$functions[$1]" "$@"

现在,您可以将其sdo用作“ sudo仅用于用户定义的函数”。

要确认该方法sdo有效:您可以在打印您的用户名的用户定义函数中进行尝试。

 birch@server ~/ 🍔 test() whoami

 birch@server ~/ 🍔 test
birch

 birch@server ~/ 🍔 sdo test
root

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.