Баш: передать функцию в качестве параметра


мне нужно передать функцию в качестве параметра в Bash. Например, следующий код:

function x() {
  echo "Hello world"
}

function around() {
  echo "before"
  eval 
  echo "after"
}

around x

следует вывод:

before
Hello world
after

Я знаю eval не правильно в этом контексте, но это просто пример :)

есть идеи?

7 63

7 ответов:

Если вам не нужно ничего необычного, как задержка оценки имени функции или ее аргументов, вам не нужно eval:

function x()      { echo "Hello world";          }
function around() { echo before; ; echo after; }

around x

делает то, что вы хотите. Вы даже можете передать функцию и ее аргументы таким образом:

function x()      { echo "x(): Passed  and ";  }
function around() { echo before; "$@"; echo after; }

around x 1st 2nd

печать

before
x(): Passed 1st and 2nd
after

нет необходимости использовать eval

function x() {
  echo "Hello world"
}

function around() {
  echo "before"
  var=$()
  echo "after $var"
}

around x

Я не думаю, что кто-то ответил на вопрос. Он не спросил, Может ли он Эхо-строки в порядке. Скорее автор вопроса хочет знать, может ли он имитировать поведение указателя функции.

есть несколько ответов, которые очень похожи на то, что я бы сделал, и я хочу расширить его другим примером.

от автора:

function x() {
  echo "Hello world"
}

function around() {
  echo "before"
  ()                   <------ Only change
  echo "after"
}

around x

чтобы расширить это, у нас будет функция X echo "Hello world:$1", чтобы показать, когда выполнение функции действительно происходит. Мы передадим строку, которая является именем функции "x":

function x() {
  echo "Hello world:"
}

function around() {
  echo "before"
  ( HERE)                   <------ Only change
  echo "after"
}

around x

чтобы описать это, строка " x "передается в функцию around (), которая Эхо" до", вызывает функцию x (через переменную $1, первый параметр, переданный around), передавая аргумент" здесь", наконец, Эхо после.

кроме того, это методология использования переменных в качестве имен функций. Переменные фактически содержат строку, которая является именем функции и ($переменная arg1 arg2 ...) вызывает функцию, передающую аргументы. Смотрите ниже:

function x(){
    echo         <== just rearrange the order of passed params
}

Z="x"        # or just Z=x

($Z  10 20 30)

дает: 30 10 20, где мы выполнили функцию с именем "x", сохраненную в переменной Z и передали параметры 10 20 и 30.

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

в bash это не указатели на функции, а переменные, которые ссылаются на имена функций, которые вы позже используете.

вы не можете передать ничего функции, кроме строк. Процесс замены может вроде как подделать его. Bash имеет тенденцию удерживать открытый FIFO до тех пор, пока команда не будет расширена до завершения.

вот быстрый глупый один

foldl() {
    echo $(($(</dev/stdin)))
} < <(tr '\n' "" <)

# Sum 20 random ints from 0-999
foldl + 0 <(while ((n=RANDOM%999,x++<20)); do echo $n; done)

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

(
    id() {
        "$@"
    }

    export -f id
    exec bash -c 'echowrap() { echo ""; }; id echowrap hi'
)

id еще только возвращает строку, которая является именем функции (автоматически импортируется из сериализации в среде) и ее args.

Pumbaa80 на другой ответ тоже хорош (eval $(declare -F "")), но его главным образом полезно для массивов, а не функций, так как они всегда глобальны. Если бы вы запустили это в функции, все, что он будет делать, это переопределить его, так что нет никакого эффекта. Он не может использоваться для создания замыканий или частичных функций или "экземпляров функций", зависящих от того, что происходит быть связанным в текущей области. В лучшем случае это можно использовать для хранения определения функции в строке, которая переопределяется в другом месте, но эти функции также могут быть жестко закодированы, если, конечно,eval используется

в основном Bash не может быть использован таким образом.

лучший подход заключается в использовании локальных переменных в ваших функциях. Проблема тогда становится, как вы получаете результат к вызывающему абоненту. Один из механизмов заключается в использовании подстановки команд:

function myfunc()
{
    local  myresult='some value'
    echo "$myresult"
}

result=$(myfunc)   # or result=`myfunc`
echo $result

здесь результат выводится в stdout, и вызывающий объект использует подстановку команд для захвата значения в переменной. Затем переменную можно использовать по мере необходимости.

вы должны иметь что-то вроде:

function around()
{
  echo 'before';
  echo ``;
  echo 'after';
}

затем вы можете позвонить around x

eval, вероятно, единственный способ достичь этого. Единственным реальным недостатком является аспект безопасности, так как вам нужно убедиться, что ничего вредоносного не передается, и будут вызываться только функции, которые вы хотите вызвать (наряду с проверкой того, что в нем нет неприятных символов, таких как";").

Так что если вы тот, кто вызывает код, то eval, вероятно, единственный способ сделать это. Обратите внимание, что есть и другие формы eval, которые, вероятно, тоже будут работать с участием подкоманды ($() и`), но они не безопаснее и дороже.