diff options
author | jilles <jilles@FreeBSD.org> | 2010-12-28 21:27:08 +0000 |
---|---|---|
committer | jilles <jilles@FreeBSD.org> | 2010-12-28 21:27:08 +0000 |
commit | 74d9b02bb00830c58e7572986e81ef97d2948ed6 (patch) | |
tree | 938df618f5e6962d48e61103d210b5c6d2101fa1 /bin/sh/eval.c | |
parent | d20b96d0f2ef605d9476d5c27ffe395c0e237253 (diff) | |
download | FreeBSD-src-74d9b02bb00830c58e7572986e81ef97d2948ed6.zip FreeBSD-src-74d9b02bb00830c58e7572986e81ef97d2948ed6.tar.gz |
sh: Don't do optimized command substitution if expansions have side effects.
Before considering to execute a command substitution in the same process,
check if any of the expansions may have a side effect; if so, execute it in
a new process just like happens if it is not a single simple command.
Although the check happens at run time, it is a static check that does not
depend on current state. It is triggered by:
- expanding $! (which may cause the job to be remembered)
- ${var=value} default value assignment
- assignment operators in arithmetic
- parameter substitutions in arithmetic except ${#param}, $$, $# and $?
- command substitutions in arithmetic
This means that $((v+1)) does not prevent optimized command substitution,
whereas $(($v+1)) does, because $v might expand to something containing
assignment operators.
Scripts should not depend on these exact details for correctness. It is also
imaginable to have the shell fork if and when a side effect is encountered
or to create a new temporary namespace for variables.
Due to the $! change, the construct $(jobs $!) no longer works. The value of
$! should be stored in a variable outside command substitution first.
Diffstat (limited to 'bin/sh/eval.c')
-rw-r--r-- | bin/sh/eval.c | 16 |
1 files changed, 15 insertions, 1 deletions
diff --git a/bin/sh/eval.c b/bin/sh/eval.c index 596a86c..903f783 100644 --- a/bin/sh/eval.c +++ b/bin/sh/eval.c @@ -94,6 +94,7 @@ static void evalsubshell(union node *, int); static void evalredir(union node *, int); static void expredir(union node *); static void evalpipe(union node *); +static int is_valid_fast_cmdsubst(union node *n); static void evalcommand(union node *, int, struct backcmd *); static void prehash(union node *); @@ -565,6 +566,19 @@ evalpipe(union node *n) +static int +is_valid_fast_cmdsubst(union node *n) +{ + union node *argp; + + if (n->type != NCMD) + return 0; + for (argp = n->ncmd.args ; argp ; argp = argp->narg.next) + if (expandhassideeffects(argp->narg.text)) + return 0; + return 1; +} + /* * Execute a command inside back quotes. If it's a builtin command, we * want to save its output in a block obtained from malloc. Otherwise @@ -590,7 +604,7 @@ evalbackcmd(union node *n, struct backcmd *result) exitstatus = 0; goto out; } - if (n->type == NCMD) { + if (is_valid_fast_cmdsubst(n)) { exitstatus = oexitstatus; savehandler = handler; if (setjmp(jmploc.loc)) { |