“Comando não encontrado” ao executar a função sudo'ing em ~ / .zshrc

10

Eu tenho uma função no meu ~/.zshrc:

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

A invocação usual é findPort 3306.

Eu quero executá-lo com privilégios elevados. Mas eu recebo "comando não encontrado".

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

Presumo que o motivo é que o usuário root é executado como um shell não interativo (portanto, não se refere a um .zshrc) ou se refere a um diferente .zshrc .

Tenho visto perguntas semelhantes a respeito alias, mas nenhuma pergunta sobre funções definidas pelo usuário. As respostas para esse problema relacionadas à aliasadição de um alias para ~/.zshrc:

alias sudo='nocorrect sudo '

Ou talvez:

alias sudo='sudo '

Eu tentei essas duas soluções e o problema ainda existe (sim, relancei o shell).

Também tentei executar sudo chshpara garantir que meu shell raiz seja executado sob zsh. Nenhuma dessas soluções remove o problema "comando não encontrado".

Existe uma maneira de executar minhas funções definidas pelo usuário no sudo?

Birchlabs
fonte
Veja também: unix.stackexchange.com/questions/181414/... (não sinalizar porque zsh tem seus próprios truques)
Muru

Respostas:

13

sudoexecuta comandos diretamente, e não através de um shell, e mesmo que correu um shell para executar esse comando, seria uma nova chamada de shell, e não aquele que lê seus ~/.zshrc(mesmo que começou um shell interativo, seria provavelmente ler root's ~/.zshrc, não o seu, a menos que você tenha configurado sudopara não redefinir a $HOMEvariável).

Aqui, você precisa informar sudopara iniciar um novo zshshell e informar zshpara ler o seu ~/.zshrcantes de executar essa função:

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

Ou:

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

Ou, para compartilhar suas funções zsh atuais com o novo zshinvocado por sudo:

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

Embora você possa receber um erro muito longo da lista arg se tiver muitas funções definidas (como ao usar o sistema de conclusão). Portanto, você pode limitá-lo à findPortfunção (e a qualquer outra função em que dependa):

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

Você também pode fazer:

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

Para incorporar o código da findPortfunção em uma função anônima à qual você passa o argumento 3306. Ou até:

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

(o script embutido passado para -c é o corpo da função).

Você poderia usar uma função auxiliar como:

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

Não use:

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

Como os argumentos de sdopassariam por outro nível de análise de shell. Comparar:

$ e() echo "$@"
$ sdo e 'tname;uname'
tname
Linux
$ zsudo e 'tname;uname'
tname;uname
Stéphane Chazelas
fonte
Preciso de uma solução tão curta quanto digitar sudo. Consegui adaptar sua solução de função anônima para fazer exatamente isso. Eu adicionei a seguinte função ao meu ~/.zshrc, que é uma função para executar outras funções com privilégios elevados:sdo() { sudo zsh -c "(){$functions[$1]} ${@:2}" }
Birchlabs
1
@Birchlabs, see edit #
Stéphane Chazelas
Obrigado; Atualizei minha resposta para usar o atalho mais recente que você propôs.
Birchlabs
Adicionar a definição de função ao script executado através do sudo é inteligente. Mas não é inteligente o suficiente se essa função usar outra função (carregada automaticamente ou não).
Damien Flament
3

Uma solução muito simples para mim foi chamar um shell interativo usando sudo -s, que parece funcionar na minha versão do zsh (5.2) do macOS. Isso me fornece as funções do meu ~/.zshrc.

Na página de manual do sudo, que realmente não sugere esse comportamento:

-s, --shell
Execute o shell especificado pela variável de ambiente SHELL se estiver definido ou o shell especificado pela entrada do banco de dados de senha do usuário que está chamando. Se um comando for especificado, ele será passado para o shell para execução através da opção -c do shell. Se nenhum comando for especificado, um shell interativo será executado.

Não sei ao certo qual é o seu caso de uso, mas suspeito que você esteja procurando uma solução interativa.

jhat
fonte
Isso realmente funciona. Embora não seja exatamente o fluxo de trabalho que eu tinha em mente. Meu desejo é permanecer em um prompt como meu usuário usual e elevar apenas minha função definida pelo usuário myFunc, digitando sudo myFunc- da mesma maneira que elevaria qualquer processo (como sudo rm -rf .). Essa solução eleva meu prompt inteiro e todas as funções futuras, além de alterar meu usuário para todas as funções futuras - o que é um pouco exagerado.
Birchlabs
O sudo -scomando iniciará outra instância do seu shell como superusuário. Se você executar esse comando interativamente, seu novo shell será interativo. Mas o comando sudo -s findPort 3306executará o comando findPort 3306no seu shell como superusuário e sairá com o código de status desse comando.
Damien Flament
2

Consegui produzir uma abreviação reutilizável para uma das soluções descritas na resposta de Stéphane Chazelas . [Editar: Stéphane repetiu o atalho original que propus; o código descrito aqui é uma versão mais recente de sua criação]

Coloque isso no seu ~/.zshrc:

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

Agora você pode usar sdocomo "apenas sudopara funções definidas pelo usuário".

Para confirmar que sdofunciona: você pode testá-lo em uma função definida pelo usuário que imprime seu nome de usuário.

 birch@server ~/ 🍔 test() whoami

 birch@server ~/ 🍔 test
birch

 birch@server ~/ 🍔 sdo test
root
Birchlabs
fonte
0

Isso parece funcionar para mim:

function sudo (){
  args="$@"
  /run/wrappers/bin/sudo -u "$USER" zsh -i -c "$args"
}
Chris Stryczynski
fonte