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

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

function x() {
  echo "Hello world"
}

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

around x

Должен выводиться:

before
Hello world
after

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

Есть идеи?


person cd1    schedule 15.04.2011    source источник


Ответы (7)


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

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

around x

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

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

around x 1st 2nd

отпечатки

before
x(): Passed 1st and 2nd
after
person Idelic    schedule 15.04.2011
comment
Если у меня есть другая функция y(), могу ли я сделать что-то вроде x 1st 2nd y 1st 2nd? Откуда он знает, что x и y являются аргументами примерно, а 1st и 2nd являются аргументами x и y? - person techguy2000; 06.02.2019
comment
И вы можете опустить функциональное слово. - person jasonleonhard; 19.09.2019
comment
Однако тогда они не будут разделены именами. т. е. если у вас есть методы внутри методов, и вы сохраняете слово function, вы не можете получить доступ к этим внутренним методам, пока не запустите метод верхнего уровня. - person jasonleonhard; 19.09.2019

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

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

От автора:

function x() {
  echo "Hello world"
}

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

around x

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

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

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

around x

Чтобы описать это, строка «x» передается в функцию around(), которая выводит «before», вызывает функцию x (через переменную $1, первый параметр, переданный в round), передавая аргумент «HERE», наконец выводит после .

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

function x(){
    echo $3 $1 $2      <== 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 это не указатели на функции, а переменные, ссылающиеся на имена функций, которые вы позже будете использовать.

person uDude    schedule 18.10.2015
comment
Этот ответ потрясающий. Я сделал bash-скрипты всех примеров и запустил их. Мне также очень нравится, как вы сделали создателей только изменений, которые очень помогли. В вашем предпоследнем абзаце опечатка: Aabove - person twitchdotcom slash KANJICODER; 13.09.2017
comment
Опечатка исправлена. Спасибо @J MADISON - person uDude; 14.09.2017
comment
почему вы заключаете его в (), разве это не запускает подоболочку? - person horseyguy; 12.01.2018

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

function x() {
  echo "Hello world"
}

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

around x
person kurumi    schedule 15.04.2011

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

Вот быстрый глупый

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

# 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 "$1"; }; id echowrap hi'
)

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

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

В принципе, Bash нельзя использовать таким образом.

person ormaaj    schedule 21.04.2012

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

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

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

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

person Anand Thangappan    schedule 15.04.2011

У вас должно получиться что-то вроде:

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

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

person Tim O    schedule 15.04.2011

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

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

person Wes Hardaker    schedule 15.04.2011
comment
eval - единственный способ сделать это. - person Wes; 15.04.2011
comment
Вы можете легко проверить, будет ли eval $1 вызывать функцию, используя if declare -F "$1" >/dev/null; then eval $1; fi - person user123444555621; 15.04.2011
comment
...или еще лучше: eval $(declare -F "$1") - person user123444555621; 15.04.2011