匿名传递代码块。功能


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
}

(我意识到您为每个块创建了一个函数或文件,但是我发现在某些情况下此选项更清晰,更易于阅读。)

whiledo/donefunction{ multiple lines }。我知道BASH没有匿名函数,但是是否可以将多个命令传递给另一个函数,就像定义一个函数时可以做到的那样while


您是说要装饰(以Python的说法),即从函数返回函数吗?从语法上讲,您的示例甚至不是BASH:wrap_this应该是函数还是函数调用?
梅尔·博伊斯

我不清楚您想做什么。正如梅尔指出的那样,您编写的内容在语法上甚至是有效的,但我不清楚您编写的内容与匿名函数之间的关系。
克里斯·

Answers:


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
                                               

它不是 lambda,但是非常非常接近。只有几个多余的字符。

但是,据我所知,以下任何方便缩写都不起作用:

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

不幸的bash是,即使它的某些功能具有非常实用的风格,也不太适合这种风格。


bash的功能有功能风格吗?唯一具有远程功能的是bash(几乎所有shell语言)通过将一个命令的输出管道化为另一个命令的输入来支持某种形式的功能。不过,这更多是Unix总体设计的功能,而不是bash本身。
kyrill

4

我设法做到了你想做的事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

你可以把代码中的字符串,并将其传递到evalsh或根本插它。

perform () {
  "$@"
}

perform echo "moo"

但是,您可能很快遇到深层报价问题。


1
实际上,我正在寻找perform { echo "moo" \n echo "moo moo" \n echo "moo moo moo" }。我已经知道您可以传递一个命令。但是,我正在寻找多行,而不仅仅是一条命令或一行。不过感谢您的尝试。
dgo.a 2013年

3

不,bash没有匿名功能。但是,可以将函数名称和参数作为字符串传递并让bash调用它。

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

wrap do_something with_arguments

但是,这是有限的。处理报价可能会成为问题。传递多个命令也是一种复杂情况。


1
不是名字,您只需更改$*"$@"
glenn jackman

多线功能主体应使用此方法正常工作。

格伦,我忘了那个表格了,谢谢。但是它不能完全解决报价问题。埃文(Evan),将一条命令包裹在多行上会很好用,我的意思是根据问题中的示例,在体内有多个命令。我将更新我的答案以解决这两个评论。
David Baggerman 2013年

这种形式的工作非常如果你照顾定义采取简单参数的函数,然后传递给那些函数作为参数调用好。即wrap my_func "$arg1" "$arg2"。我对这个示例有个疑问,就是丢失了“ $ @”的返回值,但这很容易解决。可能需要用“ $ @”包装()以确保环境变化不会泄漏,但会降低性能。
Michael Mol
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.