path: root/shell
diff options
authorGravatar Denys Vlasenko <vda.linux@googlemail.com>2009-11-15 19:58:19 +0100
committerGravatar Denys Vlasenko <vda.linux@googlemail.com>2009-11-15 19:58:19 +0100
commit647553a4fcbbc169b4390d9ef8e4657f0ffe1a5f (patch)
tree974dde9bc45566319f8b9750b6397fbafe94ad43 /shell
parentff1822aed159e1c1b5a92dc5c1fd1648b026f8f4 (diff)
hush: wait for `cmd` to complete, and immediately store its exitcode in $?
function old new delta expand_vars_to_list 2129 2197 +68 Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
Diffstat (limited to 'shell')
4 files changed, 70 insertions, 18 deletions
diff --git a/shell/brace.txt b/shell/brace.txt
new file mode 100644
index 000000000..664861b08
--- /dev/null
+++ b/shell/brace.txt
@@ -0,0 +1,50 @@
+Brace Expansion
+Brace expansion is a mechanism by which arbitrary strings may be gener-
+ated. This mechanism is similar to pathname expansion, but the file-
+names generated need not exist. Patterns to be brace expanded take the
+form of an optional preamble, followed by either a series of comma-sep-
+arated strings or a sequence expression between a pair of braces, fol-
+lowed by an optional postscript. The preamble is prefixed to each
+string contained within the braces, and the postscript is then appended
+to each resulting string, expanding left to right.
+Brace expansions may be nested. The results of each expanded string
+are not sorted; left to right order is preserved. For example,
+a{d,c,b}e expands into `ade ace abe'.
+A sequence expression takes the form {x..y}, where x and y are either
+integers or single characters. When integers are supplied, the expres-
+sion expands to each number between x and y, inclusive. When charac-
+ters are supplied, the expression expands to each character lexico-
+graphically between x and y, inclusive. Note that both x and y must be
+of the same type.
+Brace expansion is performed before any other expansions, and any char-
+acters special to other expansions are preserved in the result. It is
+strictly textual. Bash does not apply any syntactic interpretation to
+the context of the expansion or the text between the braces.
+A correctly-formed brace expansion must contain unquoted opening and
+closing braces, and at least one unquoted comma or a valid sequence
+expression. Any incorrectly formed brace expansion is left unchanged.
+A { or , may be quoted with a backslash to prevent its being considered
+part of a brace expression. To avoid conflicts with parameter expan-
+sion, the string ${ is not considered eligible for brace expansion.
+This construct is typically used as shorthand when the common prefix of
+the strings to be generated is longer than in the above example:
+ mkdir /usr/local/src/bash/{old,new,dist,bugs}
+ chown root /usr/{ucb/{ex,edit},lib/{ex?.?*,how_ex}}
+Brace expansion introduces a slight incompatibility with historical
+versions of sh. sh does not treat opening or closing braces specially
+when they appear as part of a word, and preserves them in the output.
+Bash removes braces from words as a consequence of brace expansion.
+For example, a word entered to sh as file{1,2} appears identically in
+the output. The same word is output as file1 file2 after expansion by
+bash. If strict compatibility with sh is desired, start bash with the
++B option or disable brace expansion with the +B option to the set com-
diff --git a/shell/hush.c b/shell/hush.c
index 891d87be7..6f394d1d5 100644
--- a/shell/hush.c
+++ b/shell/hush.c
@@ -2287,7 +2287,7 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg, char
* expanded result may need to be globbed
* and $IFS-splitted */
debug_printf_subst("SUBST '%s' first_ch %x\n", arg, first_ch);
- process_command_subs(&subst_result, arg);
+ G.last_exitcode = process_command_subs(&subst_result, arg);
debug_printf_subst("SUBST RES '%s'\n", subst_result.data);
val = subst_result.data;
goto store_val;
@@ -3904,7 +3904,7 @@ static NOINLINE int run_pipe(struct pipe *pi)
/* if someone gives us an empty string: `cmd with empty output` */
if (!argv_expanded[0]) {
- return 0;
+ return 0; // or G.last_exitcode? see emptytick.tests
x = find_builtin(argv_expanded[0]);
@@ -5202,10 +5202,10 @@ static int fetch_heredocs(int heredoc_cnt, struct parse_context *ctx, struct in_
-static FILE *generate_stream_from_string(const char *s)
+static FILE *generate_stream_from_string(const char *s, pid_t *pid_p)
- FILE *pf;
- int pid, channel[2];
+ pid_t pid;
+ int channel[2];
# if !BB_MMU
char **to_free;
# endif
@@ -5291,6 +5291,7 @@ static FILE *generate_stream_from_string(const char *s)
/* parent */
+ *pid_p = pid;
//bb_error_msg("[%d] fork in generate_stream_from_string: G.count_SIGCHLD:%d G.handled_SIGCHLD:%d", getpid(), G.count_SIGCHLD, G.handled_SIGCHLD);
@@ -5300,8 +5301,8 @@ static FILE *generate_stream_from_string(const char *s)
# endif
- pf = fdopen(channel[0], "r");
- return pf;
+//TODO: libbb: fdopen_or_die?
+ return fdopen(channel[0], "r");
/* Return code is exit status of the process that is run. */
@@ -5309,9 +5310,10 @@ static int process_command_subs(o_string *dest, const char *s)
FILE *pf;
struct in_str pipe_str;
- int ch, eol_cnt;
+ pid_t pid;
+ int status, ch, eol_cnt;
- pf = generate_stream_from_string(s);
+ pf = generate_stream_from_string(s, &pid);
if (pf == NULL)
return 1;
@@ -5331,16 +5333,14 @@ static int process_command_subs(o_string *dest, const char *s)
o_addQchr(dest, ch);
- debug_printf("done reading from pipe, pclose()ing\n");
- /* Note: we got EOF, and we just close the read end of the pipe.
- * We do not wait for the `cmd` child to terminate. bash and ash do.
- * Try these:
- * echo `echo Hi; exec 1>&-; sleep 2` - bash waits 2 sec
- * `false`; echo $? - bash outputs "1"
- */
+ debug_printf("done reading from `cmd` pipe, closing it\n");
- debug_printf("closed FILE from child. return 0\n");
- return 0;
+ /* We need to extract exitcode. Test case
+ * "true; echo `sleep 1; false` $?"
+ * should print 1 */
+ safe_waitpid(pid, &status, 0);
+ debug_printf("child exited. returning its exitcode:%d\n", WEXITSTATUS(status));
+ return WEXITSTATUS(status);
#endif /* ENABLE_HUSH_TICK */
diff --git a/shell/hush_test/hush-psubst/tick5.right b/shell/hush_test/hush-psubst/tick5.right
new file mode 100644
index 000000000..d00491fd7
--- /dev/null
+++ b/shell/hush_test/hush-psubst/tick5.right
@@ -0,0 +1 @@
diff --git a/shell/hush_test/hush-psubst/tick5.tests b/shell/hush_test/hush-psubst/tick5.tests
new file mode 100755
index 000000000..bd160c9fc
--- /dev/null
+++ b/shell/hush_test/hush-psubst/tick5.tests
@@ -0,0 +1 @@
+true; echo `false` $?