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


Ich habe eine Bash-Funktion in einem globalen bashrc definiert, die Root-Berechtigungen benötigt, um zu arbeiten. Wie kann ich es mit Sudo, z. sudo myfunction. Standardmäßig gibt es einen Fehler:

sudo: myfunction: Befehl nicht gefunden


25
2017-09-03 12:05


Ursprung


Niemals versucht, aber dieser Blogeintrag scheint damit umzugehen: w00tbl0g.blogspot.com/2007/05/... - Grizly
Die Installation des obigen Skripts erfordert 'set alias sudo = sudowrap', was imho nicht zu empfehlen ist. Bitte sehen Sie meine Antwort für eine Lösung, die nichts zu arbeiten erfordert. - Luca Borrione


Antworten:


Luca hat mich freundlicherweise auf diese Frage hingewiesen, hier ist meine Herangehensweise: Erweitern Sie die Funktion / den Alias ​​vor dem Aufruf von sudo und übergeben Sie sie vollständig an sudo, es werden keine temporären Dateien benötigt.

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

# 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

Der einzige Nachteil dieses Ansatzes besteht darin, dass er nur die Funktion erweitert, die Sie aufrufen, und keine zusätzlichen Funktionen, auf die Sie von dort verweisen. Kyles Ansatz behandelt das wahrscheinlich besser, wenn Sie auf Funktionen verweisen, die in Ihrem bashrc geladen sind (vorausgesetzt, es wird auf dem Server ausgeführt bash -c Anruf).


4
2017-09-03 13:23



Auf ServerFault wird bevorzugt, dass Sie den gewünschten Code anzeigen und auf die externe Website verlinken, sodass Benutzer nicht auf die gewünschten Informationen klicken müssen, damit die Informationen den potenziellen Tod externer Websites überstehen. - Conspicuous Compiler
@ConspicuousCompiler danke für den Hinweis, fertig. - w00t


Sie können export Ihre Funktion, um sie einem zu Verfügung zu stellen bash -c Subshell oder Skripts, 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, aber anscheinend sudo leitet keine Funktionen weiter (nur Variablen). Auch mit verschiedenen Kombinationen von setenv, env_keep und negieren env_reset scheint nicht zu helfen.

Bearbeiten 2

jedoch, anscheinend su  tut unterstützt exportierte Funktionen.

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

14
2017-09-03 14:21



+1, ich würde sagen, das ist die richtige Antwort. - Kyle Brandt♦
ob diese Methode funktioniert? In meinem Fall ist es nicht. - pradeepchhetri
@pradeepchhetri Vielleicht möchten Sie weitere Informationen geben, z. B. was Sie genau versuchen, welche Shell Sie verwenden und welches Betriebssystem Sie verwenden. - Legolas
@Legolas: Ich versuche das gleiche, was das Skript im obigen Skript geschrieben hat. Ich erhalte den Fehler bash: your_function: command not found. ich benutze Ubuntu 11.04 und bash shell. - pradeepchhetri
@pradeepchhetri was, wenn Sie verwenden sudo -E bash -c 'your_function'? - Legolas


Vielleicht kannst du:

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

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


4
2017-09-03 12:41



Abhängig von Ihrem System ... werden Sie bei jedem Aufruf des Befehls sudo aufgefordert, das Passwort einzugeben ... oder Sie werden einmalig aufgefordert, es zu cachen. Es wäre besser zu erkennen, ob Sie als root laufen, und wenn nicht ... rufen Sie das Bash-Skript erneut mit sudo auf. - TheCompWiz
Ich muss noch auf ein System stoßen, das das sudo-Passwort nicht zwischenspeichert: Der Standardwert für timestamp_timeout ist 5. Wenn Sie ihn auf 0 setzen, werden Sie immer nach einem Passwort gefragt, aber das wäre eine benutzerdefinierte Einstellung. - wzzrd


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

Du könntest dann auch mal einen Alias ​​für den machen sudo bash Linie auch.


2
2017-09-03 13:14





#!/bin/bash

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

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

2
2018-06-18 16:46





Wie Legolas in den Kommentaren der Antwort von Dennis Williamson Sie sollten die Antwort von lesen Bmargulies auf einer ähnlichen Frage, die auf stackoverflow gepostet wird.

Ausgehend davon habe ich eine Funktion geschrieben, die diesen Sachverhalt abdeckt und die Idee von bmargulies im Grunde realisiert.

# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ #
# 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:
das folgende Snippet ausführen

#!/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. Anrufen ohne Sudo
      Hallo Ihr Name!
      Du hast folgende Params bestanden:
      zuerst
      zweite

  2. mit Sudo anrufen
      Hallo Wurzel!
      Du hast folgende Params bestanden:
      -n
      John fertig
      -s
      foo



Wenn Sie dies in einer Shell verwenden müssen, die eine Funktion aufruft, die in Ihrer bashrc definiert ist, müssen Sie die vorherige exesudo-Funktion auf dieselbe setzen Bashrc Datei wie folgt:

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

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



Dann müssen Sie sich abmelden und erneut anmelden oder verwenden

source ~/.bashrc



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

$ yourfunc
Hello yourname!

$ exesudo yourfunc
Hello root!

2
2017-09-01 18:14



Es kehrt zurück /dev/shm/22481: No such file or directory. - modiX


Wenn Sie eine Funktion im Kontext eines Sudos aufrufen möchten, möchten Sie dies verwenden declare:

#!/bin/bash

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

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

1
2018-03-01 15:33



Dies funktioniert, solange Ihre Funktion keine anderen Funktionen aufruft. - modiX