2012-02-25 14 views
23

He intentado exportar la función y luego ejecutarlo con bash, pero eso no funciona:¿Cómo puedo ejecutar una función bash usando sudo?

$ export -f my_func 
$ sudo bash -c 'my_func' 
bash: my_func: command not found 

Si trato de ejecutar la función con fiesta sin sudo (bash -c 'mi_func'), funciona .

¿Alguna idea?

+0

Por qué necesita para funcionar de esa manera? –

+0

Porque toda la secuencia de comandos se canaliza a bash a través de stdin. Puede venir de curl, o cat, .. – Miroslav

+0

La misma pregunta en SF: http://serverfault.com/questions/177699/how-can-i-execute-a-bash-function-with-sudo/ –

Respuesta

13

A partir de la respuesta de bmargulies, escribí una función para cubrir este problema, que básicamente se da cuenta de su idea.

# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # 
# EXESUDO 
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # 
# 
# Purpose: 
# -------------------------------------------------------------------- # 
# Execute a function with sudo 
# 
# Params: 
# -------------------------------------------------------------------- # 
# $1: string: name of the function to be executed with sudo 
# 
# Usage: 
# -------------------------------------------------------------------- # 
# exesudo "funcname" followed by any param 
# 
# -------------------------------------------------------------------- # 
# Created 01 September 2012    Last Modified 02 September 2012 

function exesudo() 
{ 
    ### ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ## 
    # 
    # LOCAL VARIABLES: 
    # 
    ### ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ## 

    # 
    # I use underscores to remember it's been passed 
    local _funcname_="$1" 

    local params=("[email protected]")    ## array containing all params passed here 
    local tmpfile="/dev/shm/$RANDOM" ## temporary file 
    local filecontent     ## content of the temporary file 
    local regex       ## regular expression 
    local func       ## function source 


    ### ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ## 
    # 
    # MAIN CODE: 
    # 
    ### ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ## 

    # 
    # WORKING ON PARAMS: 
    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 

    # 
    # Shift the first param (which is the name of the function) 
    unset params[0]    ## remove first element 
    # params=("${params[@]}")  ## repack array 


    # 
    # WORKING ON THE TEMPORARY FILE: 
    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 

    content="#!/bin/bash\n\n" 

    # 
    # Write the params array 
    content="${content}params=(\n" 

    regex="\s+" 
    for param in "${params[@]}" 
    do 
     if [[ "$param" =~ $regex ]] 
      then 
       content="${content}\t\"${param}\"\n" 
      else 
       content="${content}\t${param}\n" 
     fi 
    done 

    content="$content)\n" 
    echo -e "$content" > "$tmpfile" 

    # 
    # Append the function source 
    echo "#$(type "$_funcname_")" >> "$tmpfile" 

    # 
    # Append the call to the function 
    echo -e "\n$_funcname_ \"\${params[@]}\"\n" >> "$tmpfile" 


    # 
    # DONE: EXECUTE THE TEMPORARY FILE WITH SUDO 
    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
    sudo bash "$tmpfile" 
    rm "$tmpfile" 
} 



Ejemplo de uso:
ejecutando el siguiente fragmento de

#!/bin/bash 

function exesudo() 
{ 
    # copy here the previous exesudo function !!! 
} 

test_it_out() 
{ 
    local params=("[email protected]") 
    echo "Hello "$(whoami)"!" 
    echo "You passed the following params:" 
    printf "%s\n" "${params[@]}" ## print array 
} 

echo "1. calling without sudo" 
test_it_out "first" "second" 

echo "" 
echo "2. calling with sudo" 
exesudo test_it_out -n "john done" -s "done" 

exit 



salida Will

  1. llamadas sin sudo
    Hola, tu nombre!
    usted pasó los siguientes parametros:
    primera
    segundo

  2. llamando con sudo
    Hola raíz!
    usted pasó los siguientes parametros:
    -n
    John Donne
    -s
    foo



Si necesita utilizar esto en una cáscara de llamar a una función que es definido en su bashrc, como se le preguntó con una pregunta similar en serverfault por otro usuario, entonces usted tiene que poner la función exesudo anterior en el mismo bashrc archivo, así, como la siguiente:

function yourfunc() 
{ 
echo "Hello "$(whoami)"!" 
} 
export -f yourfunc 

function exesudo() 
{ 
    # copy here 
} 
export -f exesudo 



entonces usted tiene que iniciar sesión otra vez o utilizar

source ~/.bashrc 



Finalmente, puede utilizar exesudo de la siguiente manera:

$ yourfunc 
Hello yourname! 

$ exesudo yourfunc 
Hello root! 
+2

¡Esto es todo un trabajo! Gran trabajo. Por cierto, lo único que modifiqué es la parte de invocación sudo. Agregué la propagación del código de salida, así que puedo tener pequeñas funciones ingeniosas bajo el arnés de prueba. Este es mi modificación: De esta: $ sudo fiesta "$ tmpfile" $ rm "$ tmpfile" a esto: $ sudo fiesta "$ tmpfile" $ EXIT_CODE = $? $ rm "$ tmpfile" $ return $ EXIT_CODE – LavaScornedOven

+0

Esto se siente como una gran cantidad de magia:/ – sleepycal

+1

¿No puedes simplemente generar el archivo temporal con '{echo '#!/Bin/bash'; declarar -f "$ 1" | cabeza -n -1 | cola -n +3); }> "$ tmpfile" ', y en lugar de empaquetar los argumentos en el script, ¿pasarlos como parte del sudo? Reduciría el cuerpo a 5 líneas – jbo5112

12

Cada vez que ejecuta sudo, bifurca y ejecuta una nueva copia del shell, ejecutándose como root. Ese shell no hereda funciones de su shell (no puede) y no hereda funciones de ejecuciones previas. Deberá escribir un archivo que contenga la definición de función y la invocación y sudo la invocación de eso.

+1

No solo no puede, probablemente nunca deba hacerlo por razones de seguridad. –

+0

Runnig bash solo también ejecutará una nueva copia de bash, pero aún puede heredar funciones del shell primario. Además, sudo de alguna manera es capaz de preservar las variables de bash exportadas, así que pensé que podría haber algún truco para hacer lo mismo con las funciones. Entiendo que podría haber algunas razones de seguridad para esto, pero ¿qué sucede si estoy usando sudo para ejecutar la función en un usuario más restringido? – Miroslav

+0

Las variables viajan a través del mecanismo del entorno de proceso. No hay tal cosa para las funciones. – bmargulies

1

Para abreviar y simple cosas que no tiene comillas simples en el mismo, esto funciona para mí:

export code=' 
function whoAmI() { 
    echo `whoami` 
} 

whoAmI 
' 
sudo bash -c "$code" 

# output: root 
9

Usted puede hacer que el uso de declare -f, como en el siguiente ejemplo:

function ttt() { 
    whoami 
    echo First parameter is $1 
} 

ttt dd 
DECL=`declare -f ttt` 

sudo bash -c "$DECL; ttt asd" 
3

Una alternativa a llamar a su función con sudo es simplemente mover las llamadas "sudo" dentro de su función. Por ejemplo, quería establecer una función de acceso directo en OS X para reenviar el servidor local a un puerto en particular.

function portforward() { 
    echo "y" | sudo ipfw flush; 
    sudo ipfw add 100 fwd 127.0.0.1,$1 tcp from any to any 80 in; 
    echo "Forwarding localhost to port $1!"; 
} 

La función acciona sudo y solicita mi contraseña. (Luego canaliza "y" a un aviso de ipfw, no relacionado con esta pregunta). Después de eso, sudo se almacena en la memoria caché para que el resto de la función se ejecute sin necesidad de ingresar una contraseña.

Esencialmente, esto sólo funciona como:

portforward 8000 
Password: 
Forwarding localhost to port 8000! 

y llena mi necesidad, porque sólo tendrá que introducir la contraseña de una vez y de cuidado. Aunque es un poco feo si no ingresas la contraseña la primera vez. Puntos extra para detectar si el primer sudo tuvo éxito y salir de la función si no.

0

Todo lo que tiene que hacer es comprobar si se encuentra la raíz, si es así, ejecute la función, si no es así, llame a la secuencia de comandos con sudo:

#!/bin/bash 
# script name: 'foo.sh' 

function foo(){ 
    whoami 
} 

DIR=$(cd "$(dirname "$0")" && pwd) # get script dir 
if [ "$UID" -ne 0 ]; then    # check if you are root 
    sudo $DIR/foo.sh      # NOT: run script with sudo 
else 
    foo         # YES: run function 
fi 
Cuestiones relacionadas