The xtrace (set -x) output in ash is a bit of a pain, because arguments containing whitespace aren't quoted. This can make it impossible to tell which bit belongs to which argument:
$ ash -x -c 'somevar="one two" printf %s\\n one "two three" four' + somevar=one two printf %s\n one two three four one two three four Another disadvantage is you cannot simply copy and paste the commands from this xtrace output to repeat them for debugging purposes. I wrote the attached patch which fixes this. It's based on one I wrote for dash a few weeks ago. That version has gone through a few iterations, with input from Harald van Dijk, whose suggestions are incorporated here as well. The patch changes the following: (1) Since we don't want every command name and argument quoted but only those needed for safe re-input into the shell, single_quote() has acquired an extra argument indicating whether quoting should be conditional (1) or unconditional (0). Conditional quoting quotes the string only if it contains characters that are not shell-safe, or is identical to a shell keyword (reserved word), or is empty. Shell-safe characters are defined as this set: 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz%+,./:@_^- (2) Printing of variable assignments preceding commands is handled specially; in order for the output to be suitable for re-entry into the shell, only the part after the "=" is quoted. (3) While we're at it, the values in the output of "set" (list of all variables and their values) are quoted conditionally like in bash. After the patch: $ ash -x -c 'somevar="one two" printf %s\\n one "two three" four' + somevar='one two' printf '%s\n' one 'two three' four one two three four For further background, see the thread on the dash list: https://www.mail-archive.com/dash@vger.kernel.org/#01317 Hope this is useful, - Martijn
diff --git a/shell/ash.c b/shell/ash.c index 983f7b1..c952749 100644 --- a/shell/ash.c +++ b/shell/ash.c @@ -1742,15 +1742,28 @@ number(const char *s) return atoi(s); } +/* Characters that don't need quoting before re-entry into the shell. */ +#define SHELLSAFECHARS "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz%+,./:@_^-" + +/* Pre-declare this so single_quote() can use it. */ +static const char *const * findkwd(const char *); + /* * Produce a possibly single quoted string suitable as input to the shell. - * The return string is allocated on the stack. + * If 'conditional' is nonzero, quoting is only done if the string contains + * non-shellsafe characters, or is identical to a shell keyword (reserved + * word); if it is zero, quoting is always done. + * If quoting was done, the return string is allocated on the stack, + * otherwise a pointer to the original string is returned. */ static char * -single_quote(const char *s) +single_quote(const char *s, int conditional) { char *p; + if (conditional && s[0] != '\0' && s[strspn(s, SHELLSAFECHARS)] == '\0' && ! findkwd(s)) + return (char *)s; + STARTSTACKSTR(p); do { @@ -3299,7 +3312,7 @@ rmaliases(void) static void printalias(const struct alias *ap) { - out1fmt("%s=%s\n", ap->name, single_quote(ap->val)); + out1fmt("%s=%s\n", ap->name, single_quote(ap->val, 0)); } /* @@ -9663,19 +9676,31 @@ evalcommand(union node *cmd, int flags) /* Print the command if xflag is set. */ if (xflag) { - int n; const char *p = " %s" + 1; fdprintf(preverrout_fd, p, expandstr(ps4val())); + sp = varlist.list; - for (n = 0; n < 2; n++) { - while (sp) { - fdprintf(preverrout_fd, p, sp->text); - sp = sp->next; - p = " %s"; - } - sp = arglist.list; + while (sp) { + int i = 0; + + if (p[0] == ' ') + fdprintf(preverrout_fd, " "); + while (sp->text[i] != '=' && sp->text[i] != '\0') + fdprintf(preverrout_fd, "%c", sp->text[i++]); + if (sp->text[i] == '=') + fdprintf(preverrout_fd, "=%s", single_quote(sp->text+i+1, 1)); + sp = sp->next; + p = " %s"; } + + sp = arglist.list; + while (sp) { + fdprintf(preverrout_fd, p, single_quote(sp->text, 1)); + sp = sp->next; + p = " %s"; + } + safe_write(preverrout_fd, "\n", 1); } @@ -10687,7 +10712,7 @@ showvars(const char *sep_prefix, int on, int off) p = strchrnul(*ep, '='); q = nullstr; if (*p) - q = single_quote(++p); + q = single_quote(++p, 1); out1fmt("%s%s%.*s%s\n", sep_prefix, sep, (int)(p - *ep), *ep, q); } return 0; @@ -12953,7 +12978,7 @@ trapcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) * then it prints short signal names. * We are printing short names: */ out1fmt("trap -- %s %s\n", - single_quote(tr), + single_quote(tr, 0), get_signame(signo)); /* trap_ptr != trap only if we are in special-cased `trap` code. * In this case, we will exit very soon, no need to free(). */
_______________________________________________ busybox mailing list busybox@busybox.net http://lists.busybox.net/mailman/listinfo/busybox