It isn't clear what you want to do. $($@)
doesn't make sense, but what do you want to do instead?
What $($@)
does:
- Take the arguments of the function. This is a list of strings.
- Split each piece further where they contain whitespace¹.
- Take each split piece and interpret it as a wildcard pattern. If the pattern matches any file, replace the piece by the list of matching file names.
- Use the first piece as the name of a command to execute (function, shell builtin or executable file) and pass the other pieces as arguments.
- Take the output of the command and split it where it contains whitespace.
- Take each split piece and interpret it as a wildcard pattern. If the pattern matches any file, replace the piece by the list of matching file names.
- Use the first piece as the name of a command to execute (function, shell builtin or executable file) and pass the other pieces as arguments.
If that sounds complicated, that's because it is.
If you want execute_cmd export MY_VAR=my_val
to execute export MY_VAR=my_val
, why are you even bothering with execute_cmd
?
There are two ways this could be interpreted sensibly.
You want to pass a command with parameters to a function. A command with parameters is a list of strings, the first being a function name, shell builtin or executable file. Then the syntax to invoke this command on the supplied parameters is "$@"
.
execute_cmd () {
"$@"
}
execute_cmd export MY_VAR=my_val
The double quotes avoid the splitting and wildcard expansion steps I mentioned above. Always use double quotes around variable substitutions.
Also note the dual nature of the export
keyword/builtin. While export MY_VAR=$(seq 10)
works at assigning the output of seq 10
to $MY_VAR
as the shell parses MY_VAR=$(seq 10)
because of the presence of the export
keyword, the shell wouldn't parse MY_VAR=$(seq 10)
as an assignment in execute_cmd export MY_VAR=$(seq 10)
, because the command is not export
here, but execute_cmd
, so that MY_VAR=$(seq 10)
is parsed as in any argument to any normal command, and split+glob is performed upon $(seq 10)
, so you need: execute_cmd export MY_VAR="$(seq 10)"
.
You want to run a shell snippet. A shell snippet is a single string, to be passed as a single argument. To run a string containing shell code, use the eval
builtin.
execute_cmd () {
eval "$1"
}
execute_cmd 'export MY_VAR=my_val'
¹ Assuming the default IFS
. If you know about that you don't need to read this paragraph.
e.g.
– DuckCowMooQuack Aug 28 '17 at 17:52eval $@ 2>&1 | tee -a ${logfile}
eval echo "Hello World!" > log.txt
, orexecute_cmd echo "Hello World" > log.txt
. Note the latter redirects the output ofexecute_cmd
instead of passing those characters to the function. To redirect inexecute_cmd
pass the redirection as string:execute_cmd 'echo "Hello World" > log.txt'
. – sebasth Aug 28 '17 at 17:59eval $@
doesn't make sense. It should probably be either"$@"
oreval "$1"
, or possiblyeval "$@"
(which makesexecute_cmd
redundant since it's equivalent toeval
). – Gilles 'SO- stop being evil' Aug 28 '17 at 22:40