Bash: передать функцию в качестве параметра - PullRequest
72 голосов
/ 15 апреля 2011

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

function x() {
  echo "Hello world"
}

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

around x

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

before
Hello world
after

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

Есть идеи?

Ответы [ 7 ]

104 голосов
/ 15 апреля 2011

Если вам не нужно ничего необычного, например, отложить оценку имени функции или ее аргументов, вам не нужно 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

print

before
x(): Passed 1st and 2nd
after
20 голосов
/ 18 октября 2015

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

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

От автора:

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, первый параметр, переданный в), передавая аргумент «ЗДЕСЬ», наконец, выводится после.

В качестве еще одного отступления, это методология использования переменных в качестве имен функций.Переменные фактически содержат строку, которая является именем функции, и ($ 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 это не указатели на функции, а переменные, которые ссылаются на имена функций, которые вы позже используете.

16 голосов
/ 15 апреля 2011

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

function x() {
  echo "Hello world"
}

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

around x
5 голосов
/ 21 апреля 2012

Вы не можете передавать ничего в функцию, кроме строк. Процесс замены может как бы подделать его. 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 нельзя использовать таким образом.

2 голосов
/ 15 апреля 2011

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

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

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

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

1 голос
/ 15 апреля 2011

У вас должно быть что-то вроде:

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

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

0 голосов
/ 15 апреля 2011

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...