Làm thế nào tôi có thể thực thi một hàm bash với sudo?


29

Tôi có một hàm bash được định nghĩa trong bashrc toàn cục, yêu cầu quyền root để hoạt động. Làm thế nào tôi có thể chạy nó với sudo, ví dụ sudo myfunction. Theo mặc định, nó đưa ra một lỗi:

sudo: myfactor: không tìm thấy lệnh


2
Chưa bao giờ thử, nhưng bài đăng trên blog này dường như xử lý nó: w00tbl0g.blogspot.com/2007/05/ mẹo
Grizly

việc cài đặt tập lệnh trên yêu cầu 'set alias sudo = sudowrap' không được khuyến nghị imho. Xin vui lòng xem câu trả lời của tôi cho một giải pháp không yêu cầu bất cứ điều gì để làm việc.
Luca Borrione

Đây là một trong nhiều lý do mà các hàm shell là xấu. Các hàm Shell nên được giới hạn trong các lệnh mà bạn muốn thay đổi môi trường của mình. Sử dụng các kịch bản thực tế cho phần còn lại. Lợi thế của chức năng là gì? (OK, "Lạm dụng" là ác, không phải chức năng bản thân Và tôi đặt cược có rất nhiều lý do chính đáng khác Nhưng họ nên là ngoại lệ, không phải là quy tắc, khi viết kịch bản...)
Jeff Learman

Câu trả lời:


4

Luca vui lòng chỉ cho tôi câu hỏi này, đây là cách tiếp cận của tôi: Mở rộng chức năng / bí danh trước khi gọi đến sudo và chuyển toàn bộ nó sang sudo, không cần tệp tạm thời.

Giải thích ở đây trên blog của tôi . Có rất nhiều trích dẫn xử lý :-)

# 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

Một nhược điểm của phương pháp này là nó chỉ mở rộng chức năng bạn đang gọi chứ không phải bất kỳ chức năng bổ sung nào bạn đang tham khảo từ đó. Cách tiếp cận của Kyle có thể xử lý tốt hơn nếu bạn tham khảo các chức năng được tải trong bashrc của mình (miễn là nó được thực thi trong bash -ccuộc gọi).


Trên ServerFault, bạn nên hiển thị mã mong muốn cũng như liên kết với trang bên ngoài, vì vậy người dùng không cần phải bấm vào để lấy thông tin họ muốn và do đó thông tin sẽ thoát khỏi cái chết tiềm tàng của các trang bên ngoài.
Trình biên dịch dễ thấy

15

Bạn có thể exportthực hiện chức năng của mình để làm cho nó có sẵn cho một bash -clớp con hoặc tập lệnh mà bạn muốn sử dụng nó.

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

Chỉnh sửa

Điều này hoạt động cho các mạng con trực tiếp, nhưng dường như sudokhông chuyển tiếp các hàm (chỉ các biến). Thậm chí sử dụng kết hợp khác nhau của setenv, env_keepvà phủ nhận env_resetkhông có vẻ để được giúp đỡ.

Chỉnh sửa 2

Tuy nhiên , có vẻ như su hỗ trợ các chức năng xuất khẩu.

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

2
+1, tôi sẽ nói đây là câu trả lời đúng.
Kyle Brandt

1
Liệu phương pháp này có hiệu quả không ?? Trong trường hợp của tôi thì không.
pradeepchhetri

@pradeepchhetri Bạn có thể muốn cung cấp thêm thông tin, chẳng hạn như những gì bạn đang cố gắng chính xác, bạn sử dụng hệ vỏ nào và hệ điều hành nào bạn sử dụng.
Legolas

@Legolas: Tôi đang thử kịch bản tương tự đã viết trong kịch bản trên. Tôi nhận được lỗi bash: your_function: command not found. Tôi đang sử dụng Ubuntu 11.04bash shell.
pradeepchhetri

@pradeepchhetri gì xảy ra nếu bạn sử dụng sudo -E bash -c 'your_function'?
Legolas

4

Có lẽ bạn có thể làm:

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

Điều này sẽ làm việc và tiết kiệm cho bạn từ gõ sudo trên dòng lệnh.


1
Tùy thuộc vào hệ thống của bạn ... điều này sẽ nhắc bạn cho mỗi cuộc gọi của lệnh sudo để nhập mật khẩu ... hoặc nhắc bạn một lần & lưu trữ nó. Sẽ tốt hơn nếu phát hiện nếu bạn đang chạy bằng root và nếu không ... hãy gọi lại tập lệnh bash bằng sudo một lần.
TheCompWiz

Tôi chưa gặp phải một hệ thống không lưu mật khẩu sudo: mặc định cho timestamp_timeout là 5. Nếu bạn đặt nó thành 0, bạn luôn được yêu cầu nhập mật khẩu, nhưng đó sẽ là cài đặt tùy chỉnh.
wzzrd

3

Nếu bạn cần gọi một hàm trong ngữ cảnh của sudo, bạn muốn sử dụng declare:

#!/bin/bash

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

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

Điều này hoạt động, miễn là chức năng của bạn không gọi các chức năng khác.
modiX

Đối với trường hợp sử dụng của tôi, đây là câu trả lời tốt nhất.
Jim

2

Tôi sẽ thực thi một shell mới bằng cách sudo tự thực thi shell, sau đó hàm sẽ chạy với quyền root. Ví dụ như một cái gì đó như:

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

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

Bạn thậm chí có thể đi để tạo một bí danh cho sudo bashdòng là tốt.


2
#!/bin/bash

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

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

2

Như Legolas đã chỉ ra trong các bình luận về câu trả lời của Dennis Williamson, bạn nên đọc câu trả lời của bmargulies về một câu hỏi tương tự được đăng trên stackoverflow.

Bắt đầu từ đó tôi đã viết một chức năng để đề cập đến vấn đề này, về cơ bản hiện thực hóa ý tưởng của 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"
}



Ví dụ về cách sử dụng:
chạy đoạn mã sau

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



Sẽ xuất

  1. gọi mà không sudo
    Xin chào tên của bạn!
    Bạn đã vượt qua các thông số sau: giây
    đầu tiên

  2. gọi bằng sudo
    Xin chào root!
    Bạn đã vượt qua các thông số sau:
    -n
    john xong
    -s
    foo



Nếu bạn cần sử dụng hàm này trong shell gọi một hàm được xác định trong bashrc của bạn, như bạn đã hỏi, thì bạn cũng phải đặt hàm exesudo trước đó trên cùng một tệp bashrc , như sau:

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

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



Sau đó, bạn phải đăng xuất và đăng nhập lại hoặc sử dụng

source ~/.bashrc



Cuối cùng, bạn có thể sử dụng exesudo như sau:

$ yourfunc
Hello yourname!

$ exesudo yourfunc
Hello root!

Nó trở lại /dev/shm/22481: No such file or directory.
modiX
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.