Bagaimana saya bisa menjalankan fungsi bash dengan sudo?

29

Saya memiliki fungsi bash yang didefinisikan dalam bashrc global, yang membutuhkan hak akses root untuk bekerja. Bagaimana saya bisa menjalankannya dengan sudo, mis sudo myfunction. Secara default itu memberikan kesalahan:

sudo: myfunction: perintah tidak ditemukan

Eugene Yarmash
sumber
2
Belum pernah mencoba, tetapi posting blog ini sepertinya menanganinya: w00tbl0g.blogspot.com/2007/05/…
Grizly
instalasi skrip di atas memerlukan 'set alias sudo = sudowrap' yang tidak direkomendasikan. Silakan lihat jawaban saya untuk solusi yang tidak memerlukan apa pun untuk bekerja.
Luca Borrione
Ini adalah salah satu dari banyak alasan mengapa fungsi shell jahat. Fungsi Shell harus dibatasi pada perintah yang ingin Anda ubah lingkungan Anda. Gunakan skrip aktual untuk sisanya. Apa keuntungan dari suatu fungsi? (Oke, "Penggunaan berlebihan" itu jahat, tidak berfungsi sendiri. Dan saya bertaruh ada banyak alasan bagus lainnya. Tetapi mereka harus menjadi pengecualian, bukan aturan, saat menulis skrip.)
Jeff Learman

Jawaban:

4

Luca dengan ramah menunjuk saya ke pertanyaan ini, inilah pendekatan saya: Perluas fungsi / alias sebelum panggilan ke sudo dan berikan secara keseluruhan ke sudo, tidak perlu file temp.

Dijelaskan di sini di blog saya . Ada banyak penanganan kutipan :-)

# 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

Satu kelemahan dari pendekatan ini adalah hanya memperluas fungsi yang Anda panggil, bukan fungsi tambahan yang Anda rujuk dari sana. Pendekatan Kyle mungkin menangani itu lebih baik jika Anda mereferensikan fungsi-fungsi yang dimuat di bashrc Anda (asalkan dijalankan pada bash -cpanggilan).

w00t
sumber
Di ServerFault, lebih disukai Anda menunjukkan kode yang diinginkan serta menautkan ke situs eksternal, sehingga pengguna tidak perlu mengklik untuk mendapatkan informasi yang mereka inginkan, dan informasi tersebut bertahan dari potensi kematian situs eksternal.
Compiler yang mencolok
15

Anda dapat exportfungsi Anda untuk membuatnya tersedia untuk bash -csubkulit atau skrip tempat Anda ingin menggunakannya.

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

Edit

Ini berfungsi untuk subshell langsung, tetapi tampaknya sudotidak meneruskan fungsi (hanya variabel). Bahkan menggunakan berbagai kombinasi setenv, env_keepdan meniadakan env_resetsepertinya tidak membantu.

Edit 2

Namun , tampak bahwa su tidak mendukung fungsi diekspor.

your_function () { echo 'Hello, World'; }
export -f your_function
su -c 'your_function'
Dijeda sampai pemberitahuan lebih lanjut.
sumber
2
+1, saya akan mengatakan ini adalah jawaban yang benar.
Kyle Brandt
1
apakah metode ini berfungsi ?? Dalam kasus saya tidak.
pradeepchhetri
@pradeepchhetri Anda mungkin ingin memberikan informasi lebih lanjut, seperti apa yang Anda coba, shell yang Anda gunakan, dan OS apa yang Anda gunakan.
Legolas
@ Legolas: Saya mencoba skrip hal yang sama ditulis dalam skrip di atas. Saya mendapatkan kesalahan bash: your_function: command not found. Saya menggunakan Ubuntu 11.04dan bash shell.
pradeepchhetri
@pradeepchhetri bagaimana jika Anda menggunakan sudo -E bash -c 'your_function'?
Legolas
4

Mungkin Anda bisa:

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

Ini akan berhasil dan menyelamatkan Anda dari mengetikkan sudo pada commandline.

wzzrd
sumber
1
Bergantung pada sistem Anda ... ini akan meminta Anda untuk setiap panggilan perintah sudo untuk memasukkan kata sandi ... atau meminta Anda sekali & menyimpannya. Akan lebih baik untuk mendeteksi jika Anda menjalankan sebagai root, dan jika tidak ... panggil skrip bash lagi dengan sudo sekali.
TheCompWiz
Saya belum menemukan sistem yang tidak men-cache kata sandi sudo: default untuk timestamp_timeout adalah 5. Jika Anda menetapkannya ke 0, Anda selalu dimintai kata sandi, tetapi itu akan menjadi pengaturan khusus.
wzzrd
3

Jika Anda perlu memanggil fungsi dalam konteks sudo, Anda ingin menggunakan declare:

#!/bin/bash

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

sudo su another_user -c "$(declare -f hello); hello"
Ferdy
sumber
Ini berfungsi, selama fungsi Anda tidak memanggil fungsi lain.
modiX
untuk kasus penggunaan saya, ini adalah jawaban terbaik.
Jim
2

Saya akan mengeksekusi shell baru dengan memiliki sudo mengeksekusi shell itu sendiri, maka fungsinya akan berjalan dengan hak akses root. Misalnya sesuatu seperti:

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

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

Anda bahkan kemudian dapat membuat alias untuk sudo bashsaluran juga.

Kyle Brandt
sumber
2
#!/bin/bash

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

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

Seperti yang ditunjukkan oleh Legolas dalam komentar jawaban Dennis Williamson Anda harus membaca jawaban bmargulies pada pertanyaan serupa yang diposting di stackoverflow.

Mulai dari itu saya menulis fungsi untuk membahas masalah ini, yang pada dasarnya mewujudkan gagasan 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"
}



Contoh penggunaan:
menjalankan cuplikan berikut

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



Akan menghasilkan

  1. menelepon tanpa sudo
    Halo namamu!
    Anda melewati params berikut: detik
    pertama

  2. menelepon dengan sudo
    Halo root!
    Anda melewati params berikut:
    -n
    john done
    -s
    foo



Jika Anda perlu menggunakan ini dalam shell memanggil fungsi yang didefinisikan dalam bashrc Anda, seperti yang Anda minta, maka Anda harus meletakkan fungsi exesudo sebelumnya pada file bashrc yang sama juga, seperti berikut:

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

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



Maka Anda harus keluar dan masuk lagi atau menggunakan

source ~/.bashrc



Akhirnya Anda dapat menggunakan exesudo sebagai berikut:

$ yourfunc
Hello yourname!

$ exesudo yourfunc
Hello root!
Luca Borrione
sumber
Ia kembali /dev/shm/22481: No such file or directory.
modiX