foo='foo(){ echo "Inside Function"; }' bash -c "$foo; foo"
Inside Function
A function, after all, is simply a named, pre-parsed, static command string stored in the shell's memory. And so the only real way to do it is by evaluating strings as code, which is exactly what the shell does each time you call your function.
It never was very different before, and it still isn't. The devs setup convenience methods for doing so, and they try to safeguard possible security holes as they might, but the fact of the matter is that the more convenient a thing becomes, the more likely it is you know less about it than you should.
There are many options available to you when it comes to importing data from parent shells to subshells. Grow familiar with them, become confident enough that you can identify their potential uses and their potential weaknesses, and use them sparingly but effectively.
I suppose I can elaborate on one or two of these while I'm at it. Probably the best way to pass static commands from one shell to another (whether it be up or down a subshelled command chain) is alias
. alias
is useful in this capacity because it is POSIX-specified to report its defined commands safely:
The format for displaying aliases (when no operands or only name operands are specified) shall be:
The value string shall be written with appropriate quoting so that it is suitable for reinput to the shell. See the description of shell quoting in Quoting.
For example:
alias foo="foo(){ echo 'Inside Function'; }" alias foo
foo='foo(){ echo '\''Inside Function'\''; }'
See what it does with the quotes, there? The exact output is shell dependent, but, in general, you can rely on a shell reporting its alias
definitions in a manner that is eval
safe for reinput to the same shell. Mixing and matching source/destination shells might be iffy in some cases, though.
To demonstrate a cause for exercising such caution:
ksh -c 'alias foo=" foo(){ echo \"Inside Function\" }" alias foo'
foo=$'\nfoo(){\n\techo "Inside Function"\n}'
Also note that the alias
namespace represents one of three independent namespaces you can generally expect to find fully fleshed out in practically any modern shell. Typically shells provide you these three namespaces:
Variables: name=value
outside of lists
- These may also be defined with some builtins such as
export
, set
, readonly
in some cases.
Functions: name() compound_command <>any_redirects
in command position
- The shell is specified not to expand or interpret any portion of the function definition it might find in the command at definition time.
- This prohibition does not include aliases, though, as these are expanded during the parsing process of a shell's command read, and so
alias
values, if the alias
is found in correct context, are expanded into the function definition command when found.
- As mentioned, the shell saves the parsed value of the definition command as a static string in its memory, and it performs all expansions and redirections found within the string only when the function name is later found post-parse as called.
Aliases: alias name=value
in command position
- Like functions,
alias
definitions are interpreted when the definition names are later found in command position. - Unlike functions, though, as their definitions are arguments to the
alias
command, valid shell expansions found within are also interpreted at define time. And so alias
definitions are always twice interpreted. - Also unlike functions,
alias
definitions are not parsed at all at define time, but, rather, it is the shell's parser that expands their values pre-parse into a command string when they are found.
You've probably noticed what I consider to be the primary difference between an alias
or a function above, which is the shell's expansion point for each. The differences, actually, can make their combination quite useful.
alias foo=' foo(){ printf "Inside Function: %s\n" "$@" unset -f foo }; foo "$@" '
That is an alias that will define a function that unsets itself when called, and so it affects one namespace by the application of another. It then calls the function. The example is naive; it isn't very useful in a regular context, but the following example might demonstrate some other limited usefulness it might offer in other contexts:
sh -c " alias $(alias foo) foo \' " -- \; \' \" \\
Inside Function: ; Inside Function: ' Inside Function: " Inside Function: \ Inside Function: '
You should always call an alias
name on an input line other than the one on which the definition is found - again, this is to do with the parse order of the command. When combined, aliases and functions can be used together to portably, safely, and effectively move information and parameter arrays in and out of subshelled contexts.
Almost unrelated, but here's another fun one:
alias mlinesh='"${SHELL:-sh}" <<"" '
Run that at an interactive prompt and any arguments you pass to it on the same execution line as the line on which you call it will be interpreted as arguments to a subshell. All lines thereafter until the first occurring blank line, though, are read-in by the current shell to pass as stdin to the subshell it prepares to call, and none of these are interpreted in any way at all.
$ mlinesh -c '. /dev/fd/0; foo' > foo(){ echo 'Inside Function'; } >
Inside Function
...but just...
$ mlinesh > foo(){ echo 'Inside Function'; } > foo >
...is easier...
foo() { echo "Inside function"; }; foo
?$ function bar() { echo "Inside function"; } $ foo="bar" $ ${foo} Inside function $