ฉันจะใช้ฟังก์ชัน bash ด้วย sudo ได้อย่างไร


29

ฉันมีฟังก์ชั่นทุบตีที่กำหนดไว้ในระดับโลก bashrc ซึ่งต้องใช้สิทธิ์รูทในการทำงาน ฉันจะเรียกใช้ด้วย sudo sudo myfunctionได้อย่างไร โดยค่าเริ่มต้นจะให้ข้อผิดพลาด:

sudo: myfunction: ไม่พบคำสั่ง


2
ไม่เคยลอง แต่โพสต์บล็อกนี้น่าจะจัดการได้: w00tbl0g.blogspot.com/2007/05/…
Grizly

การติดตั้งสคริปต์ด้านบนจำเป็นต้องมี 'set alias sudo = sudowrap' ซึ่งไม่แนะนำให้ใช้ โปรดดูคำตอบของฉันสำหรับวิธีแก้ปัญหาที่ไม่ต้องการอะไรให้ทำงาน
Luca Borrione

นี่เป็นหนึ่งในหลาย ๆ เหตุผลที่ฟังก์ชั่นของเชลล์นั้นชั่วร้าย ฟังก์ชั่นของเชลล์ควร จำกัด เฉพาะคำสั่งที่คุณต้องการเปลี่ยนสภาพแวดล้อมของคุณ ใช้สคริปต์จริงสำหรับส่วนที่เหลือ ประโยชน์ของฟังก์ชั่นคืออะไร? (ตกลง "การใช้มากเกินไป" เป็นสิ่งที่ชั่วร้ายไม่ได้ทำหน้าที่ตัวเองและฉันพนันได้ว่ามีเหตุผลดีๆอีกมากมาย แต่พวกเขาควรได้รับการยกเว้นไม่ใช่กฎเมื่อเขียนสคริปต์)
Jeff Learman

คำตอบ:


4

Luca ได้โปรดชี้แนะให้ฉันสำหรับคำถามนี้นี่คือวิธีการของฉัน: ขยายฟังก์ชั่น / นามแฝงก่อนที่จะเรียกร้องให้ sudo และส่งต่อให้ sudo ทั้งหมดโดยไม่ต้องใช้ไฟล์ temp

อธิบายที่นี่บนบล็อกของฉัน มีการจัดการกับคำพูดมากมาย :-)

# 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

ข้อเสียอย่างหนึ่งของวิธีนี้คือมันขยายฟังก์ชั่นที่คุณโทรเท่านั้นไม่ใช่ฟังก์ชั่นพิเศษใด ๆ ที่คุณอ้างอิงจากที่นั่น วิธีการของ Kyle น่าจะจัดการได้ดีกว่าถ้าคุณอ้างอิงฟังก์ชั่นที่โหลดใน bashrc ของคุณ (หากได้รับการเรียกใช้เมื่อมีการbash -cเรียก)


ใน ServerFault ขอแนะนำให้คุณแสดงรหัสที่ต้องการรวมถึงเชื่อมโยงไปยังไซต์ภายนอกดังนั้นผู้ใช้ไม่จำเป็นต้องคลิกออกไปเพื่อรับข้อมูลที่ต้องการและเพื่อให้ข้อมูลรอดพ้นจากการเสียชีวิตของเว็บไซต์ภายนอก
Compiler ที่เห็นได้ชัดเจน

15

คุณสามารถexportใช้ฟังก์ชันของคุณเพื่อให้พร้อมใช้งานสำหรับbash -csubshell หรือสคริปต์ที่คุณต้องการใช้

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

แก้ไข

สิ่งนี้ใช้ได้กับ subshells โดยตรง แต่ดูเหมือนsudoจะไม่ส่งต่อฟังก์ชั่น (ตัวแปรเท่านั้น) แม้ใช้ชุดต่างๆของsetenv, env_keepและกวนenv_resetดูเหมือนจะไม่ช่วยเหลือ

แก้ไข 2

แต่ก็ปรากฏว่าsu ไม่สนับสนุนการส่งออกฟังก์ชั่น

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

2
+1 ฉันจะบอกว่านี่เป็นคำตอบที่ถูกต้อง
Kyle Brandt

1
ไม่ว่าจะเป็นวิธีการนี ​​?? ในกรณีของฉันมันไม่ได้เป็น
pradeepchhetri

@pradeepchhetri คุณอาจต้องการให้ข้อมูลเพิ่มเติมเช่นสิ่งที่คุณพยายามอย่างแม่นยำเปลือกที่คุณใช้และระบบปฏิบัติการที่คุณใช้
เลโกลัส

@ เลโกลัส: ฉันพยายามสคริปต์สิ่งเดียวกันที่เขียนไว้ในสคริปต์ข้างต้น bash: your_function: command not foundฉันได้รับข้อผิดพลาด ฉันใช้และUbuntu 11.04 bash shell
pradeepchhetri

@pradeepchhetri สิ่งที่ถ้าคุณใช้sudo -E bash -c 'your_function'?
เลโกลัส

4

บางทีคุณสามารถทำได้:

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

สิ่งนี้จะทำงานและช่วยคุณไม่ให้พิมพ์ sudo บน commandline


1
ขึ้นอยู่กับระบบของคุณ ... สิ่งนี้จะแจ้งให้คุณทุกครั้งที่มีการเรียกคำสั่ง sudo เพื่อป้อนรหัสผ่าน ... หรือแจ้งให้คุณ & แคชอีกครั้ง จะเป็นการดีกว่าถ้าตรวจพบว่าคุณกำลังใช้งานรูทอยู่หรือไม่ถ้า ... เรียกใช้สคริปต์ทุบตีอีกครั้งด้วย sudo อีกครั้ง
TheCompWiz

ฉันยังไม่ได้พบกับระบบที่ไม่ได้แคชรหัสผ่าน sudo: ค่าเริ่มต้นสำหรับ timestamp_timeout คือ 5 หากคุณตั้งค่าเป็น 0 คุณจะถูกถามรหัสผ่านเสมอ แต่นั่นเป็นการตั้งค่าแบบกำหนดเอง
wzzrd

3

หากคุณต้องการเรียกใช้ฟังก์ชันในบริบทของ sudo คุณต้องการใช้declare:

#!/bin/bash

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

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

ทำงานได้ตราบใดที่ฟังก์ชันของคุณไม่เรียกใช้ฟังก์ชันอื่น
modiX

สำหรับกรณีการใช้งานของฉันนี่คือคำตอบที่ดีที่สุด
Jim

2

ฉันจะรันเชลล์ใหม่โดยให้ sudo รันเชลล์เองจากนั้นฟังก์ชันจะรันด้วยสิทธิ์พิเศษรูต ตัวอย่างเช่น:

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

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

คุณสามารถทำนามแฝงให้กับsudo bashบรรทัดได้เช่นกัน


2
#!/bin/bash

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

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

2

เลโกลัสชี้ให้เห็นในความคิดเห็นของคำตอบของเดนนิสวิลเลียมสันคุณควรอ่านคำตอบของbmarguliesในคำถามที่คล้ายกันซึ่งโพสต์ไว้ใน stackoverflow

เริ่มต้นจากที่ฉันเขียนฟังก์ชั่นเพื่อครอบคลุมปัญหานี้ซึ่งโดยทั่วไปตระหนักถึงความคิดของ 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"
}



ตัวอย่างการใช้งาน:
เรียกใช้ข้อมูลโค้ดต่อไปนี้

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



จะส่งออก

  1. โทรโดยไม่มี sudo
    สวัสดี yourname!
    คุณผ่าน params ต่อไปนี้: วินาที
    แรก

  2. โทรด้วย sudo
    สวัสดีรูต!
    คุณผ่าน params ต่อไปนี้:
    -n
    john done
    -s
    foo



หากคุณต้องการใช้ฟังก์ชันนี้ในเชลล์ที่เรียกใช้ฟังก์ชันซึ่งกำหนดไว้ใน bashrc ของคุณตามที่คุณถามคุณต้องใส่ฟังก์ชั่น exesudo ก่อนหน้านี้ลงในไฟล์bashrcเดียวกันเช่นกันดังนี้

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

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



จากนั้นคุณต้องออกจากระบบและลงชื่อเข้าใช้อีกครั้งหรือใช้

source ~/.bashrc



ในที่สุดคุณสามารถใช้ exesudo ดังต่อไปนี้:

$ yourfunc
Hello yourname!

$ exesudo yourfunc
Hello root!

มันกลับ/dev/shm/22481: No such file or directoryมา
modiX
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.