Как я могу выполнить функцию bash с помощью sudo?

29

У меня есть функция bash, определенная в глобальном bashrc, для работы которой требуются привилегии root. Как я могу запустить его с sudo, например sudo myfunction. По умолчанию выдает ошибку:

sudo: myfunction: команда не найдена

Евгений Ярмаш
источник
2
Никогда не пробовал, но, похоже, это сообщение в блоге: w00tbl0g.blogspot.com/2007/05/…
Grizly
установка вышеуказанного скрипта требует 'set alias sudo = sudowrap', что не рекомендуется imho. Пожалуйста, смотрите мой ответ для решения, которое не требует ничего для работы.
Лука Боррионе
Это одна из многих причин, по которым функции оболочки являются злом. Функции оболочки должны быть ограничены командами, которые вы хотите изменить в своей среде. Используйте актуальные сценарии для отдыха. В чем преимущество функции? (Хорошо, «Чрезмерное использование» - это зло, а не само функционирование. И я держу пари, что есть много других веских причин. Но они должны быть исключением, а не правилом при написании сценариев.)
Джефф Лирман

Ответы:

4

Люка любезно указал мне на этот вопрос, вот мой подход: разверните функцию / псевдоним перед вызовом sudo и передайте его полностью в sudo, временные файлы не требуются.

Объясняется здесь, в моем блоге . Там много обработки цитат :-)

# 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

Недостатком этого подхода является то, что он расширяет только функцию, которую вы вызываете, а не любые дополнительные функции, на которые вы ссылаетесь. Подход Кайла, вероятно, справляется с этим лучше, если вы ссылаетесь на функции, которые загружены в ваш bashrc (при условии, что он выполняется при bash -cвызове).

w00t
источник
На ServerFault желательно, чтобы вы отображали нужный код, а также ссылки на внешний сайт, чтобы пользователям не приходилось щелкать, чтобы получить необходимую информацию, и эта информация переживала потенциальную гибель внешних сайтов.
Заметный компилятор
15

Вы можете сделать exportсвою функцию доступной для bash -cподоболочки или сценариев, в которых вы хотите ее использовать.

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

редактировать

Это работает для прямых подоболочек, но, очевидно sudo, не пересылает функции (только переменные). Даже при использовании различных комбинаций setenv, env_keepи отрицая , env_resetпохоже, не помочь.

Редактировать 2

Тем не менее , представляется , что su делает поддержку экспортируемых функций.

your_function () { echo 'Hello, World'; }
export -f your_function
su -c 'your_function'
Приостановлено до дальнейшего уведомления.
источник
2
+1, я бы сказал, это правильный ответ.
Кайл Брандт
1
работает ли этот метод ?? В моем случае это не так.
Pradeepchhetri
@pradeepchhetri Вы можете предоставить больше информации, например, что именно вы пытаетесь использовать, какую оболочку вы используете и какую ОС вы используете.
Леголас
@Legolas: я пытаюсь использовать тот же сценарий, который написан выше. Я получаю ошибку 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 в командной строке.

wzzrd
источник
1
В зависимости от вашей системы ... при каждом вызове команды sudo вам будет предложено ввести пароль ... или один раз запросить и кешировать его. Было бы лучше определить, работаете ли вы от имени пользователя root, а если нет ... снова вызовите скрипт bash с помощью 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
для моего варианта использования это лучший ответ.
Джим
2

Я бы запустил новую оболочку, выполнив sudo саму оболочку, после чего функция запустится с правами root. Например что-то вроде:

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
BURZ
источник
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
    Привет, твое имя!
    Вы прошли следующие параметры:
    первая
    секунда

  2. звонит с sudo
    Привет рут!
    Вы передали следующие параметры:
    -n
    Джон сделано
    -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