2012-02-25 14 views
23

Ho provato ad esportare la funzione e quindi l'esecuzione con bash, ma questo non funziona:Come posso eseguire una funzione bash usando sudo?

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

Se provo a fare funzionare la funzione con bash senza sudo (bash -c 'my_func'), funziona .

Qualche idea?

+0

Perché è necessario eseguire in questo modo? –

+0

Poiché l'intero script viene convogliato per bash tramite stdin. Potrebbe venire da arricciare, o gatto, .. – Miroslav

+0

La stessa domanda su SF: http://serverfault.com/questions/177699/how-can-i-execute-a-bash-function-with-sudo/ –

risposta

13

A partire dalla risposta di bmargulies, ho scritto una funzione per coprire questo problema, che sostanzialmente realizza la sua 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" 
} 



Esempio di utilizzo:
eseguendo il seguente frammento

#!/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 



uscita sarà

  1. chiamando senza sudo
    Ciao yourname!
    Hai superato i seguenti: params
    primo
    secondo

  2. chiamando con sudo
    Ciao root!
    Hai superato i seguenti: params
    -n
    John Donne
    -s
    foo



Se è necessario utilizzare questo in un guscio di chiamata di una funzione che è definito nella tua bascrc, come richiesto con una domanda simile su serverfault da un altro utente, quindi devi mettere la precedente funzione exesudo sullo stesso bashrc file come bene, come il seguente:

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

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



poi si deve logout e login di nuovo o utilizzare

source ~/.bashrc 



Infine è possibile utilizzare exesudo come segue:

$ yourfunc 
Hello yourname! 

$ exesudo yourfunc 
Hello root! 
+2

Questo è un bel pezzo di lavoro! Ottimo lavoro. A proposito, l'unica cosa che ho modificato è la parte di invocazione sudo. Ho aggiunto la propagazione del codice di uscita, in modo da poter avere alcune piccole funzioni sotto test harness. Questa è la mia modifica: Da questo: $ sudo bash "$ tmpfile" $ rm "$ tmpfile" a questo: $ sudo bash "$ tmpfile" $ EXIT_CODE = $? $ rm "$ tmpfile" $ return $ EXIT_CODE – LavaScornedOven

+0

Questo sembra un sacco di magia:/ – sleepycal

+1

Non puoi semplicemente generare il tempfile con '{echo '#!/Bin/bash'; declare -f "$ 1" | testa -n -1 | coda -n +3); }> "$ tmpfile" ', e invece di impacchettare gli argomenti nello script, passarli come parte di sudo? Ridurrebbe il corpo a 5 linee – jbo5112

12

Ogni volta che si esegue sudo, forca ed esegue una nuova copia della shell, eseguita come root. Quella shell non eredita le funzioni dalla shell (non può) e non eredita le funzioni dalle precedenti esecuzioni. Dovrai scrivere un file contenente la definizione e l'invocazione della funzione e sudo l'invocazione di quello.

+1

Non solo non può, probabilmente non dovrebbe mai farlo a causa di motivi di sicurezza. –

+0

Runnig bash eseguirà solo una nuova copia di bash, tuttavia è ancora in grado di ereditare funzioni dalla shell genitore. Inoltre, sudo è in qualche modo in grado di conservare le variabili bash esportate, quindi ho pensato che ci potesse essere qualche trucco per fare lo stesso con le funzioni. Capisco che ci potrebbero essere alcuni motivi di sicurezza per questo, ma cosa succede se sto usando sudo per eseguire la funzione con un utente più limitato? – Miroslav

+0

Le variabili viaggiano attraverso il meccanismo dell'ambiente di processo. Non esiste una cosa del genere per le funzioni. – bmargulies

1

Per breve e semplice roba che non ha virgolette singole in esso, questo funziona per me:

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

whoAmI 
' 
sudo bash -c "$code" 

# output: root 
9

È possibile farlo usando declare -f, come nel seguente esempio:

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

ttt dd 
DECL=`declare -f ttt` 

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

Un'alternativa a chiamare la funzione con sudo è semplicemente spostare le chiamate "sudo" all'interno della tua funzione. Ad esempio, volevo impostare una funzione di collegamento in OS X per inoltrare localhost a una porta specifica.

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 funzione colpisce sudo e richiede la mia password. (Quindi pipe "y" a un prompt per ipfw, non correlato a questa domanda). Successivamente, sudo viene memorizzato nella cache in modo che il resto della funzione venga eseguito senza la necessità di inserire una password.

In sostanza, questo solo funziona come:

portforward 8000 
Password: 
Forwarding localhost to port 8000! 

e riempie il mio bisogno, perché ho solo bisogno di inserire la password una sola volta e che sia curato. Anche se è un po 'brutto se non si riesce a inserire la password la prima volta. Punti extra per rilevare se il primo sudo ha avuto successo ed uscire dalla funzione se non lo ha fatto.

0

Tutto quello che dovete fare è controllare se siete root, in caso affermativo, eseguire la funzione, se non, chiamare lo script 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 
Problemi correlati