Is it possible to create something analogous to an anonymous function whose value can be assigned to an array element and later called? I can't seem to find a way to do this in a bash script but perhaps there's a workaround.
7 Answers
Short answer: No.
Long answer: Nooooooooooooo.
Complete answer: Functions in bash are not first-class objects, therefore there can be no such thing as an anonymous function in bash.
7 Comments
eval the array element when you want to call the function.It is possible; I wrote a library to do exactly this, though it's a very strange project. The source code is available at http://github.com/spencertipping/bash-lambda. Using this library:
$ my_array=()
$ my_array[0]=$(fn x 'echo $((x + 1))')
$ my_array[1]=$(fn x 'echo $((x + 2))')
$ ${my_array[0]} 5
6
$ ${my_array[1]} 5
7
$
The trick is to have the fn function create a file containing the body of the function, chmod +x that file, then return its name. This causes stray files to accumulate, which is why the library also implements an asynchronous mark/sweep garbage collector.
3 Comments
some_command; ${my_array[0]} 5, or some_command && ${my_array[0]} 5, both expressions will only run "${my_array[0]} 5 after running some_command.If you really need array to store the functions, you can define named functions and store just their names. You can then call the function as ${array[n]}. Or, you can name them func1 .. funcN and then just call func$n.
3 Comments
declare -n reference_name - not anonymous, but name can be abstracted, e.g.: declare -n aref; a() { echo "function a"; }; aref=a then $aref will execute a.echo ${!aref}, then ${!aref}well bash is turing complete, soo thats perfectly possible ;)
but aside from this its not really worth the consideration.
you could simulate such behaviour though with something along this line:
echo myval ; ( foocmd "$_" && barcmd "$_" )
but why?!?
1 Comment
I came across this question because I was looking for how to do something analogous to
[1,2,3,4,5].map(x => x*2).filter(x => x < 7);
i.e. Being able to manipulate previous results in a pipeline in a flexible way.
xargs
A nice way I had heard of to handle this on the command line is through the xargs command. My first impression was it wasn't very flexible
$ printf "one two\nthree\tfour" | xargs echo "args> "
# => args> one two three four
$ printf "one two three four" | xargs echo "args> "
# => args> one two three four
By default it splits standard input by any whitespace into separate arguments, then feeds those arguments to the command (it'll be clearer with the examples below)
I just learned from this extremely helpful gist Brainiarc7 wrote how xargs can be really flexible and useful. Definitely read/skim that, it's really good with more explanation. I included examples and things I gleaned from it below though, hopefully it's helpful.
1. -n: Split up args into n-sized chunks
# Chunk args into groups of two
$ printf "one two\nthree\tfour" | xargs -n 2 echo "args> "
# =>
# args> one two
# args> three four
Using -n 1 will probably be very useful
2. -I: Interpolate args into a command using a placeholder
An example probably best illustrates how this one works.
# Indicate we want `%` to be the placeholder, split args into groups of two,
# then interpolate them in the command at the placeholder
printf 'one two three four' | xargs -I % -n 2 echo "Chunk of args> " % " <in the middle of a command"
# =>
# Chunk of args> one two <in the middle of a command
# Chunk of args> three four <in the middle of a command
You give -I the string you want to use as a placeholder. The placeholder can be whatever text you like (e.g. my_unique_placeholder), but % is conventional.
3. -0: Chunk args by the null character (UTF+0000)
This isn't as convenient, but you can use it to split by an arbitrary character via tr.
# Split arg chunks by tab character:
$ printf 'one two\tthree\tfour' | tr '\t' "\0" | xargs -0 -n 1 echo "args> "
# =>
# args> one two
# args> three
# args> four
4. -L: split args on n lines into a chunk
# Take all args on every 2 lines as a chunk
$ printf 'one two\nthree\nfour' | xargs -L 2 echo "args> "
# =>
# args> one two three
# args> four
https://gist.github.com/Brainiarc7/133fd582e124981c08cbafca98455ee9 https://shapeshed.com/unix-xargs/
3 Comments
({arg}) => {body} is what's happening in your code?{} similar to how a manpage would, but that doesn't make sense in a javascript context! Hopefully the edit is clearer.(arg) => body is what I was looking for. The edit makes sense. Thank you!Create the fn file in your PATH
#!/bin/sh
printusage () {
printf "Create anonymous function, for example\n"
printf "fn 'echo "$1 $2"'"
exit 1
}
[ "$#" = "1" ] || printusage
fun=$1
[ "$fun" = "" ] && printusage
fun_file="$(mktemp /tmp/fun_XXXXXX)"
echo "#!/bin/sh" > "$fun_file"
echo "" >> "$fun_file"
echo "$fun" >> "$fun_file"
chmod u+x "$fun_file"
echo "$fun_file"
You can then do :
foo=$(fn 'echo $1')
${foo} "bar"