con-f-use
con-f-use

Reputation: 4028

bash: How do I create function from variable?

How do I create a function named from the contents a variable? I want to write a template script that defines a function named after the script’s own file name. Something like this (which of course doesn't work):

#!/bin/bash
bname="$(basename $0)" # filename of the script itself

someprefix_${bname}() { # function's name created from $bname variable
    echo test
}

So if the script's filename is foo.sh, then it should define a function named someprefix_foo that will echo "test".

Upvotes: 31

Views: 11584

Answers (6)

Golar Ramblar
Golar Ramblar

Reputation: 123

To avoid uncertainty of what gets expanded when, I used to put all the stuff I want to make in an auxiliary function with a fixed name and just call that from a function with a dynamic name:

_function_main() {
  date
  printf '%s\n' "$@"
}

eval "function_${dynamicname}() { _function_main \"\$@\"; }"

Upvotes: 1

dimo414
dimo414

Reputation: 48824

I use the following syntax, which avoids /dev/stdin but still lets you use heredocs:

eval "$(cat <<EOF
  $function_name() {
    commands go here
    but "\$you \$have \$to \$escape $variables"
  }
EOF
)"

(notice $variables will be expanded in the function body, the other vars will not be).

Upvotes: 2

L&#233;a Gris
L&#233;a Gris

Reputation: 19555

You can disable expansion of a Here Document like that:

eval "$(cat <<EOF
  $function_name() {
    commands go here
    and "$you $do $not $have $to $escape variables"
  }
EOF
)"

See: How to cat <<EOF >> a file containing code?

Bash Manual: 3.6.6 Here Documents / Here Document

No parameter and variable expansion, command substitution, arithmetic expansion, or filename expansion is performed on word. If any part of word is quoted, the delimiter is the result of quote removal on word, and the lines in the here-document are not expanded. If word is unquoted, all lines of the here-document are subjected to parameter expansion, command substitution, and arithmetic expansion, the character sequence \newline is ignored, and \ must be used to quote the characters \, $, and `.

Upvotes: -2

mpersico
mpersico

Reputation: 833

The 'eval and 'source' solutions work IFF you do not have anything in the function that could be evaluated at create time that you actually want delayed until execution time.

source /dev/stdin << EOF
badfunc () 
{
    echo $(date)
}
EOF

$ date;badfunc;sleep 10;date;badfunc
Tue Dec  1 12:34:26 EST 2015 ## badfunc output not current
Tue Dec  1 12:23:51 EST 2015
Tue Dec  1 12:34:36 EST 2015 ## and not changing
Tue Dec  1 12:23:51 EST 2015

Yes, it's a contrived example (just call date and don't echo a subshell call of it), but using the original answers, I tried something a more elaborate that required a run time evaluation and got similar results - evaluation at "compile" time, not run time.

The way that I solved the problem was to build a temp file with the function and then source that. By the judicious use of single and double quotes surrounding the test I print into the file, I can completely control what gets evaluated and when.

tmpsh=$(mktemp)
echo "goodfunc ()" > $tmpsh
echo '{' >> $tmpsh
echo 'echo $(date)' >> $tmpsh
echo '}' >> $tmpsh
. $tmpsh
rm -f $tmpsh

and then

$ date;goodfunc;sleep 10;date;goodfunc
Tue Dec 1 12:36:42 EST 2015 ## current
Tue Dec 1 12:36:42 EST 2015
Tue Dec 1 12:36:52 EST 2015 ## and advancing
Tue Dec 1 12:36:52 EST 2015

Hopefully, this will be of more use to people. Enjoy.

Upvotes: 2

Indeed, it is instructive that the aforementioned construction derails the possibility of having embedded quotation marks there within and is thus potentially bugulant. However, the following construction does proffer similar functionality whilst not having the constraints of the former answer.

For your review:

source /dev/stdin <<EOF
function someprefix_${bname}()
{
     echo "test";
};
EOF

Upvotes: 20

Eugene Yarmash
Eugene Yarmash

Reputation: 149796

You can use eval:

eval "someprefix_${bname}() { echo test; }"

Bash even allows "."s in function names :)

Upvotes: 33

Related Questions