Передача кодового блока как анона. функция


9

Можно ли рассматривать блок команд как анонимную функцию?

function wrap_this {
   run_something
   # Decide to run block or maybe not.
   run_something else
}

wrap_this {
   do_something
   do_somthing else
}

# Do something else

wrap_this {
   do_something_else_else
   do_something_else_else_else
}

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

whileделает это с do/doneи functionделает это с { multiple lines }. Я понимаю, что BASH не имеет анонимных функций, но возможно ли передать несколько команд другой функции, как вы можете сделать при определении функции или while?


Вы имеете в виду, что хотите украсить (на языке Python) - т.е. вернуть функцию из функции? Ваш пример, синтаксически, даже не BASH: должен ли wrap_this быть функцией или вызовом функции?
— Мел Бойс

Мне неясно, что вы хотите сделать. Как указал Мел, то, что вы написали, даже синтаксически допустимо, но мне неясно, как то, что вы написали, относится к анонимным функциям.
— Крис Даун

Ответы:


2

Это самое короткое решение, о котором я мог подумать:

Учитывая эти функции:

# List processing
map() { while IFS='' read -r x; do "$@" "$x"; done; }
filter() { while IFS='' read -r x; do "$@" "$x" >&2 && echo "$x"; done; }
foldr() { local f="$1"; local result="$2"; shift 2;  while IFS='' read -r x; do result="$( "$f" "$@" "$x" "$result" )"; done; echo "$result"; }
foldl() { local f="$1"; local result="$2"; shift 2;  while IFS='' read -r x; do result="$( "$f" "$@" "$result" "$x" )"; done; echo "$result"; }

# Helpers
re() { [[ "$2" =~ $1 ]]; }

Примеры:

# Example helpers
toLower() { tr '[:upper:]' '[:lower:]'; }
showStructure() { [[ "$1" == "--curly" ]] && echo "{$2; $3}" || echo "($1, $2)"; }

# All lib* directories, ignoring case, using regex
ls /usr | map toLower | filter re 'lib.*'

# All block devices. (Using test, for lack of a full bash [[ … ]].)
cd /dev; ls | filter test -b

# Show difference between foldr and foldl
$ ls / | foldr showStructure '()'
(var/, (usr/, (tmp/, (sys/, (sbin/, (run/, (root/, (proc/, (opt/, (mnt/, (media/, (lost+found/, (lib64/, (lib32/, (lib@, (home/, (etc/, (dev/, (daten/, (boot/, (bin/, ())))))))))))))))))))))
$ ls / | foldr showStructure '{}' --curly
{var/; {usr/; {tmp/; {sys/; {sbin/; {run/; {root/; {proc/; {opt/; {mnt/; {media/; {lost+found/; {lib64/; {lib32/; {lib@; {home/; {etc/; {dev/; {daten/; {boot/; {bin/; {}}}}}}}}}}}}}}}}}}}}}}

(Эти примеры, конечно, являются просто примерами использования, и в действительности этот стиль имеет смысл только для более сложных вариантов использования.)

Как правило, всегда можно использовать следующий стиль:

f() { something "$@"       ; }; someList    | map    f
g() { something "$1" "$2" …; }; someCommand | filter g
⋮                              ⋮            ⋮     ⋮

Это не совсем лямбда, но это очень, очень близко. Всего несколько лишних символов.

Но ни одно из следующих сокращений удобства не работает, насколько я могу сказать:

λ() { [[ $@ ]]; } # fails on spaces
λ() { [[ "${@:-1}" ${@:1:-1} ]]; } # syntax error
alias λ=test # somehow ignored

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


Особенности bash имеют функциональный стиль? Единственное, что удаленно функционирует, - это то, что bash (как и почти любой язык оболочки) поддерживает форму композиции функций, направляя вывод одной команды на вход другой. Это скорее особенность общего дизайна Unix, а не bash как такового.
— Кирилл

4

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

wrap_this(){
    run_something
    eval "$(cat /dev/stdin)"
    run_something_else
}

Это позволяет запускать код следующим образом:

wrap_this << EOF
    my function
EOF

Не совсем идеально, поскольку внутренний блок является строкой, но создает повторное использование.


Это круто! Я бы просто добавил кавычки вокруг первого, !!чтобы предотвратить подстановку в heredoc. stackoverflow.com/questions/27920806/…
— Saintali

3

Вы можете поместить код в строку и передать его evalили shили просто интерполировать его.

perform () {
  "$@"
}

perform echo "moo"

Тем не менее, вы можете быстро обернуться неприятностями.


1
На самом деле я искал perform { echo "moo" \n echo "moo moo" \n echo "moo moo moo" }. Я уже знал, что вы можете передать одну команду. Но я ищу несколько строк, а не только одну команду или одну строку. Спасибо за попытку.
— dgo.a

3

Нет, Bash не имеет анонимных функций. Тем не менее, можно передать имя функции и аргументы в виде строк и заставить ее вызывать bash.

function wrap() {
    do_before
    "$@"
    do_after
}

wrap do_something with_arguments

Это, однако, несколько ограничено. Работа с цитированием может стать проблемой. Передача более чем одной команды также является осложнением.


1
Не nightname, все , что вам нужно сделать , это изменение $*в"$@"
— Glenn Джекман

Многолинейные тела функций должны нормально работать с этим методом.

Гленн, я забыл об этой форме, спасибо. Это не полностью решает проблему цитирования. Эван, единственная команда, заключенная в несколько строк, будет работать нормально, я имел в виду иметь более одной команды в теле в соответствии с примерами в вопросе. Я обновлю свой ответ на оба комментария.
— Дэвид Баггерман

Эта форма работает очень хорошо, если вы позаботитесь об определении функций, которые принимают простые аргументы, а затем передаете вызовы этих функций в качестве аргументов. То есть wrap my_func "$arg1" "$arg2". Одно из моих замечаний в этом примере заключается в том, что возвращаемое значение «$ @» потеряно, но это легко исправить. Может быть желательно обернуть "$ @", ()чтобы избежать изменений в окружающей среде при некоторых затратах на производительность.
— Майкл Мол
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.