"Comando no encontrado" cuando sudo'ing función de ~ / .zshrc

10

Tengo una función en mi ~/.zshrc:

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

La invocación habitual es findPort 3306.

Quiero ejecutarlo con privilegios elevados. Pero me sale "comando no encontrado".

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

Supongo que la razón es que el usuario raíz se ejecuta como un shell no interactivo (por lo tanto, no se refiere a un .zshrc) o se refiere a otro .zshrc .

He visto preguntas similares con aliasrespecto a las funciones definidas por el usuario, pero ninguna. Las respuestas a este problema aliasimplican agregar un alias a ~/.zshrc:

alias sudo='nocorrect sudo '

O quizás:

alias sudo='sudo '

He intentado ambas soluciones y el problema persiste (sí, he relanzado el shell).

También he intentado ejecutar sudo chshpara asegurarme de que mi shell raíz se ejecuta por debajo zsh. Ninguna de estas soluciones elimina el problema "comando no encontrado".

¿Hay alguna manera de ejecutar mis funciones definidas por el usuario en sudo?

Birchlabs
fuente
Ver también: unix.stackexchange.com/questions/181414/… (no marcado porque zsh tiene sus propios trucos)
muru

Respuestas:

13

sudoejecuta comandos directamente, no a través de una concha, e incluso si se corrió una concha para ejecutar ese comando, sería una nueva llamada al shell, y no uno que lee sus ~/.zshrc(incluso si se ha iniciado un shell interactivo, es probable que leer root's ~/.zshrc, no el suyo, a menos que haya configurado sudopara no restablecer la $HOMEvariable).

Aquí, debe indicarle sudoque inicie un nuevo zshshell, y que zshlea su ~/.zshrcantes de ejecutar esa función:

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

O:

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

O para compartir sus funciones zsh actuales con las nuevas zshinvocadas por sudo:

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

Aunque puede obtener un error de lista arg demasiado larga si tiene muchas funciones definidas (como cuando usa el sistema de finalización). Por lo tanto, es posible que desee limitarlo a la findPortfunción (y a cualquier otra función en la que se base si existe)

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

También puedes hacer:

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

Para incrustar el código de la findPortfunción en una función anónima a la que pasa el argumento 3306. O incluso:

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

(el script en línea pasado a -c es el cuerpo de la función).

Puede usar una función auxiliar como:

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

No utilice:

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

Como los argumentos de sdosufrirían otro nivel de análisis de shell. Comparar:

$ e() echo "$@"
$ sdo e 'tname;uname'
tname
Linux
$ zsudo e 'tname;uname'
tname;uname
Stéphane Chazelas
fuente
Necesito una solución que sea tan breve como escribir sudo. Pude adaptar su solución de función anónima para hacer precisamente eso. He agregado la siguiente función a mi ~/.zshrc, que es una función para ejecutar otras funciones con privilegios elevados:sdo() { sudo zsh -c "(){$functions[$1]} ${@:2}" }
Birchlabs
1
@Birchlabs, ver edición
Stéphane Chazelas
Gracias; He actualizado mi respuesta para usar el acceso directo más nuevo que ha propuesto.
Birchlabs
Agregar la definición de función al script ejecutado a través de sudo es inteligente. Pero no es lo suficientemente inteligente si esa función usa otra función (con carga automática o no).
Damien Flament
3

Una solución muy simple para mí fue invocar un shell interactivo usando sudo -s, que parece funcionar en mi versión de zsh (5.2) incluida en macOS. Esto me proporciona las funciones de mi ~/.zshrc.

Desde la página de manual de sudo, que realmente no sugiere este comportamiento:

-s, --shell
Ejecuta el shell especificado por la variable de entorno SHELL si está configurado o el shell especificado por la entrada de la base de datos de contraseñas del usuario que invoca. Si se especifica un comando, se pasa al shell para su ejecución a través de la opción -c del shell. Si no se especifica ningún comando, se ejecuta un shell interactivo.

No estoy seguro de cuál es su caso de uso, pero sospecho que está buscando una solución interactiva.

jhat
fuente
Esto realmente funciona. Aunque no es exactamente el flujo de trabajo que tenía en mente. Mi deseo es permanecer en un mensaje como mi usuario habitual y elevar solo mi función definida por el usuario myFunc, escribiendo sudo myFunc, de la misma manera que elevaría cualquier proceso (como sudo rm -rf .). Esta solución eleva mi solicitud completa y todas las funciones futuras, además de cambiar mi usuario para todas las funciones futuras, lo cual es un poco exagerado.
Birchlabs
El sudo -scomando lanzará otra instancia de su shell como superusuario. Si ejecuta ese comando de forma interactiva, su nuevo shell será interactivo. Pero el comando sudo -s findPort 3306ejecutará el comando findPort 3306en su shell como superusuario y luego saldrá con el código de estado de ese comando.
Damien Flament
2

Pude producir una taquigrafía reutilizable para una de las soluciones descritas en la respuesta de Stéphane Chazelas . [Editar: Stéphane ha iterado en el atajo original que propuse; el código descrito aquí es una versión más nueva de su creación]

Pon esto en tu ~/.zshrc:

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

Ahora puede usarlo sdocomo "solo sudopara funciones definidas por el usuario".

Para confirmar que sdofunciona: puede probarlo en una función definida por el usuario que imprima su nombre de usuario.

 birch@server ~/ 🍔 test() whoami

 birch@server ~/ 🍔 test
birch

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

Esto parece funcionar para mí:

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