Come posso eseguire una funzione bash con sudo?


29

Ho una funzione bash definita in una bashrc globale, che richiede i privilegi di root per funzionare. Come posso eseguirlo con sudo, ad es sudo myfunction. Di default dà un errore:

sudo: myfunction: comando non trovato


2
Mai provato, ma questo post sul blog sembra gestirlo: w00tbl0g.blogspot.com/2007/05/…
Grizly

l'installazione dello script sopra richiede 'set alias sudo = sudowrap' che non è raccomandabile imho. Si prega di consultare la mia risposta per una soluzione che non richiede nulla per funzionare.
Luca Borrione,

Questa è una delle molte ragioni per cui le funzioni della shell sono cattive. Le funzioni della shell dovrebbero essere limitate ai comandi che si desidera modificare l'ambiente. Usa gli script effettivi per il resto. Qual è il vantaggio di una funzione? (OK, "L'uso eccessivo" è malvagio, non funziona da solo. E scommetto che ci sono molte altre buone ragioni. Ma dovrebbero essere l'eccezione, non la regola, quando si scrivono gli script.)
Jeff Learman,

Risposte:


4

Luca mi ha gentilmente indicato questa domanda, ecco il mio approccio: espandere la funzione / alias prima della chiamata a sudo e passarla nella sua interezza a sudo, non sono necessari file temporanei.

Spiegato qui sul mio blog . C'è un sacco di gestione delle quotazioni :-)

# Wrap sudo to handle aliases and functions
# Wout.Mertens@gmail.com
#
# Accepts -x as well as regular sudo options: this expands variables as you not root
#
# Comments and improvements welcome
#
# Installing: source this from your .bashrc and set alias sudo=sudowrap
#  You can also wrap it in a script that changes your terminal color, like so:
#  function setclr() {
#   local t=0               
#   SetTerminalStyle $1                
#   shift
#   "$@"
#   t=$?
#   SetTerminalStyle default
#   return $t
#  }
#  alias sudo="setclr sudo sudowrap"
#  If SetTerminalStyle is a program that interfaces with your terminal to set its
#  color.

# Note: This script only handles one layer of aliases/functions.

# If you prefer to call this function sudo, uncomment the following
# line which will make sure it can be called that
#typeset -f sudo >/dev/null && unset sudo

sudowrap () 
{
    local c="" t="" parse=""
    local -a opt
    #parse sudo args
    OPTIND=1
    i=0
    while getopts xVhlLvkKsHPSb:p:c:a:u: t; do
        if [ "$t" = x ]; then
            parse=true
        else
            opt[$i]="-$t"
            let i++
            if [ "$OPTARG" ]; then
                opt[$i]="$OPTARG"
                let i++
            fi
        fi
    done
    shift $(( $OPTIND - 1 ))
    if [ $# -ge 1 ]; then
        c="$1";
        shift;
        case $(type -t "$c") in 
        "")
            echo No such command "$c"
            return 127
            ;;
        alias)
            c="$(type "$c")"
            # Strip "... is aliased to `...'"
            c="${c#*\`}"
            c="${c%\'}"
            ;;
        function)
            c="$(type "$c")"
            # Strip first line
            c="${c#* is a function}"
            c="$c;\"$c\""
            ;;
        *)
            c="\"$c\""
            ;;
        esac
        if [ -n "$parse" ]; then
            # Quote the rest once, so it gets processed by bash.
            # Done this way so variables can get expanded.
            while [ -n "$1" ]; do
                c="$c \"$1\""
                shift
            done
        else
            # Otherwise, quote the arguments. The echo gets an extra
            # space to prevent echo from parsing arguments like -n
            while [ -n "$1" ]; do
                t="${1//\'/\'\\\'\'}"
                c="$c '$t'"
                shift
            done
        fi
        echo sudo "${opt[@]}" -- bash -xvc \""$c"\" >&2
        command sudo "${opt[@]}" bash -xvc "$c"
    else
        echo sudo "${opt[@]}" >&2
        command sudo "${opt[@]}"
    fi
}
# Allow sudowrap to be used in subshells
export -f sudowrap

Lo svantaggio di questo approccio è che espande solo la funzione che stai chiamando, non eventuali funzioni extra a cui fai riferimento da lì. L'approccio di Kyle probabilmente lo gestisce meglio se si fa riferimento a funzioni caricate nel proprio bashrc (a condizione che venga eseguito durante la bash -cchiamata).


Su ServerFault, è preferibile mostrare il codice desiderato e collegarlo al sito esterno, quindi gli utenti non devono fare clic per ottenere le informazioni che desiderano e quindi le informazioni sopravvivono alla potenziale morte di siti esterni.
Conspicuous Compiler

15

È possibile la exportpropria funzione per renderlo disponibile a una bash -csubshell o agli script in cui si desidera utilizzarlo.

your_function () { echo 'Hello, World'; }
export -f your_function
bash -c 'your_function'

modificare

Funziona con i subshells diretti, ma apparentemente sudonon inoltra le funzioni (solo variabili). Anche l'uso di varie combinazioni di setenv, env_keepe negando env_reset, non sembrano aiutare.

Modifica 2

Tuttavia , sembra che su does funzioni di supporto esportato.

your_function () { echo 'Hello, World'; }
export -f your_function
su -c 'your_function'

2
+1, direi che questa è la risposta corretta.
Kyle Brandt,

1
se questo metodo funziona ?? Nel mio caso non lo è.
pradeepchhetri,

@pradeepchhetri Potresti voler fornire ulteriori informazioni, come ad esempio cosa stai provando con precisione, quale shell usi e quale sistema operativo usi.
Legolas,

@Legolas: sto provando la stessa cosa scritta nello script sopra. Ricevo l'errore bash: your_function: command not found. Sto usando Ubuntu 11.04e bash shell.
pradeepchhetri,

@pradeepchhetri cosa succede se si utilizza sudo -E bash -c 'your_function'?
Legolas,

4

Forse puoi fare:

function meh() {
    sudo -v
    sudo cat /etc/shadow
}

Questo dovrebbe funzionare e ti evita di scrivere sudo sulla riga di comando.


1
A seconda del tuo sistema ... questo ti chiederà per ogni chiamata del comando sudo di inserire la password ... o ti chiederà una volta e memorizzalo nella cache. Sarebbe meglio rilevare se stai eseguendo come root, e in caso contrario ... chiama nuovamente lo script bash con sudo una volta.
TheCompWiz

Devo ancora incontrare un sistema che non memorizza nella cache la password sudo: il valore predefinito per timestamp_timeout è 5. Se lo imposti su 0, ti verrà sempre chiesto una password, ma sarebbe un'impostazione personalizzata.
wzzrd,

3

Se devi chiamare una funzione nel contesto di un sudo, vuoi usare declare:

#!/bin/bash

function hello() {
  echo "Hello, $USER"
}

sudo su another_user -c "$(declare -f hello); hello"

Funziona, purché la tua funzione non chiami altre funzioni.
modiX

per il mio caso d'uso, questa è la risposta migliore.
Jim,

2

Eseguirò una nuova shell facendo eseguire sudo la shell stessa, quindi la funzione verrà eseguita con i privilegi di root. Ad esempio qualcosa del tipo:

vim myFunction
#The following three lines go in myFunction file
function mywho {
    sudo whoami
}

sudo bash -c '. /home/kbrandt/myFunction; mywho'
root

Potresti anche andare a creare un alias anche per la sudo bashlinea.


2
#!/bin/bash

function smth() {
    echo "{{"
    whoami
    echo "}}"
}

if [ $(whoami) != "root" ]; then
    whoami
    echo "i'm not root"
    sudo $0
else
    smth
fi

2

Come sottolineato da Legolas nei commenti della risposta di Dennis Williamson , dovresti leggere la risposta di bmargulies su una domanda simile pubblicata su StackOverflow.

A partire da quello ho scritto una funzione per coprire questo problema, che in pratica realizza l'idea di bmargulies.

# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ #
# 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=( "$@" )               ## 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:
esecuzione del seguente frammento

#!/bin/bash

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

test_it_out ()
{
    local params=( "$@" )
    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



Verrà emesso

  1. chiamando senza sudo
    Ciao il tuo nome!
    Hai superato i seguenti parametri:
    primo
    secondo

  2. chiamando con sudo
    Ciao root!
    Hai superato i seguenti parametri:
    -n
    john done
    -s
    foo



Se è necessario utilizzarlo in una shell che chiama una funzione definita in bashrc, come richiesto, è necessario inserire anche la funzione exesudo precedente nello stesso file bashrc , come il seguente:

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

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



Quindi è necessario disconnettersi e accedere nuovamente o utilizzare

source ~/.bashrc



Finalmente puoi usare exesudo come segue:

$ yourfunc
Hello yourname!

$ exesudo yourfunc
Hello root!

Ritorna /dev/shm/22481: No such file or directory.
modiX
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.