开发者

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 f开发者_JS百科rom $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".


You can use eval:

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

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


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


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.


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).


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 \"\$@\"; }"


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 `.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜