Wie kann ich eine Bash-Funktion mit sudo ausführen?

29

Ich habe eine Bash-Funktion in einem globalen Bash-Code definiert, für deren Ausführung Root-Rechte erforderlich sind. Wie kann ich es mit sudo ausführen, z sudo myfunction. Standardmäßig gibt es einen Fehler:

sudo: myfunction: befehl nicht gefunden

Eugene Yarmash
quelle
2
Nie ausprobiert, aber dieser Blog-Beitrag scheint damit klar zu kommen: w00tbl0g.blogspot.com/2007/05/…
Grizly
Die Installation des obigen Skripts erfordert 'set alias sudo = sudowrap', was imho nicht empfehlenswert ist. Bitte sehen Sie meine Antwort für eine Lösung, die nichts zum Arbeiten erfordert.
Luca Borrione
Dies ist einer der vielen Gründe, warum Shell-Funktionen böse sind. Shell-Funktionen sollten auf Befehle beschränkt sein, die Sie in Ihrer Umgebung ändern möchten. Verwenden Sie für den Rest die aktuellen Skripte. Was ist der Vorteil einer Funktion? (OK, "Überbeanspruchung" ist böse, funktioniert nicht selbst. Und ich wette, es gibt viele andere gute Gründe. Aber sie sollten die Ausnahme sein, nicht die Regel, wenn Skripte geschrieben werden.)
Jeff Learman

Antworten:

4

Luca hat mich freundlich auf diese Frage hingewiesen. Hier ist mein Ansatz: Erweitern Sie die Funktion / den Alias ​​vor dem Aufruf von sudo und übergeben Sie sie vollständig an sudo, ohne dass temporäre Dateien erforderlich sind.

Erklärt hier auf meinem Blog . Es gibt eine Menge Angebotsabwicklung :-)

# Wrap sudo to handle aliases and functions
# [email protected]
#
# 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

Der einzige Nachteil dieses Ansatzes besteht darin, dass nur die aufgerufene Funktion erweitert wird, keine zusätzlichen Funktionen, auf die Sie von dort aus verweisen. Kyles Ansatz geht damit wahrscheinlich besser um, wenn Sie auf Funktionen verweisen, die in Ihrem bashrc geladen sind (vorausgesetzt, sie werden beim bash -cAufruf ausgeführt).

w00t
quelle
In ServerFault wird bevorzugt, dass Sie den gewünschten Code anzeigen und eine Verknüpfung mit der externen Website herstellen, damit Benutzer nicht auf die gewünschten Informationen klicken müssen und die Informationen den potenziellen Tod externer Websites überleben.
Auffälliger Compiler
15

Sie können exportIhre Funktion verwenden, um es einer bash -cSubshell oder Skripten zur Verfügung zu stellen, in denen Sie es verwenden möchten.

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

Bearbeiten

Dies funktioniert für direkte Subshells, leitet aber anscheinend sudokeine Funktionen weiter (nur Variablen). Selbst unter Verwendung von verschiedenen Kombinationen von setenv, env_keepund negiert env_resetnicht zu helfen scheint.

Bearbeiten 2

Allerdings scheint es , dass su tut Unterstützung exportierten Funktionen.

your_function () { echo 'Hello, World'; }
export -f your_function
su -c 'your_function'
Bis auf weiteres angehalten.
quelle
2
+1, ich würde sagen, das ist die richtige Antwort.
Kyle Brandt
1
ob diese methode funktioniert ?? In meinem Fall ist es nicht.
Pradeepchhetri
@pradeepchhetri Möglicherweise möchten Sie weitere Informationen angeben, z. B. was Sie genau versuchen, welche Shell Sie verwenden und welches Betriebssystem Sie verwenden.
Legolas
@Legolas: Ich versuche das gleiche Skript, das im obigen Skript geschrieben wurde. Ich erhalte den Fehler bash: your_function: command not found. Ich benutze Ubuntu 11.04und bash shell.
Pradeepchhetri
@pradeepchhetri was ist, wenn Sie verwenden sudo -E bash -c 'your_function'?
Legolas
4

Vielleicht können Sie tun:

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

Dies sollte funktionieren und erspart Ihnen die Eingabe von sudo in der Befehlszeile.

wzzrd
quelle
1
Abhängig von Ihrem System ... werden Sie bei jedem Aufruf des Befehls sudo aufgefordert, das Kennwort einzugeben ... oder werden einmal aufgefordert, das Kennwort zwischenzuspeichern. Es ist besser zu erkennen, ob Sie als root ausgeführt werden, und wenn nicht, rufen Sie das Bash-Skript mit sudo noch einmal auf.
TheCompWiz
Ich habe noch kein System gefunden, das das sudo-Passwort nicht zwischenspeichert: Der Standardwert für timestamp_timeout ist 5. Wenn Sie den Wert auf 0 setzen, werden Sie immer nach einem Passwort gefragt. Dies wäre jedoch eine benutzerdefinierte Einstellung.
Wzzrd
3

Wenn Sie eine Funktion im Kontext eines Sudo aufrufen müssen, möchten Sie Folgendes verwenden declare:

#!/bin/bash

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

sudo su another_user -c "$(declare -f hello); hello"
ferdy
quelle
Dies funktioniert, solange Ihre Funktion keine anderen Funktionen aufruft.
modiX
Für meinen Anwendungsfall ist dies die beste Antwort.
Jim
2

Ich würde eine neue Shell ausführen, indem sudo die Shell selbst ausführt, dann wird die Funktion mit root-Rechten ausgeführt. Zum Beispiel so etwas wie:

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

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

Sie können auch dann gehen, um einen Alias ​​für die sudo bashLeitung zu erstellen.

Kyle Brandt
quelle
2
#!/bin/bash

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

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

Wie Legolas in den Kommentaren der Antwort von Dennis Williamson hervorhob, sollten Sie die Antwort von bmargulies auf eine ähnliche Frage lesen, die auf stackoverflow veröffentlicht wurde.

Ausgehend davon habe ich eine Funktion geschrieben, die dieses Thema behandelt und die Idee der Margulies im Grunde verwirklicht.

# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ #
# 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"
}



Anwendungsbeispiel:
Ausführen des folgenden Snippets

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



Wird ausgegeben

  1. ohne sudo
    telefonieren Hallo dein Name!
    Sie haben folgende Parameter bestanden:
    erste
    Sekunde

  2. mit sudo telefonieren
    Hallo root!
    Sie haben die folgenden Parameter übergeben:
    -n
    john done
    -s
    foo



Wenn Sie dies in einer Shell verwenden möchten, die eine in Ihrem bashrc definierte Funktion aufruft, müssen Sie die vorherige exesudo-Funktion ebenfalls in dieselbe bashrc- Datei einfügen , wie im Folgenden beschrieben:

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

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



Dann musst du dich abmelden und wieder einloggen oder benutzen

source ~/.bashrc



Schließlich können Sie exesudo wie folgt verwenden:

$ yourfunc
Hello yourname!

$ exesudo yourfunc
Hello root!
Luca Borrione
quelle
Es kehrt zurück /dev/shm/22481: No such file or directory.
modiX