How can I preserve quotes in printing a Bash script’s arguments?

The quotes are removed before the arguments are passed to your script, so it’s too late to preserve them. But you can preserve their effect when passing the arguments to the inner command, and reconstruct an equivalent quoted/escaped version of the arguments for printing.

For passing the arguments to the inner command "$@"—with the double quotes, $@ preserves the original word breaks, meaning that the inner command receives exactly the same argument list that your script did.

For printing, you can use the %q format in Bash’s printf command to reconstruct the quoting. Note that this won’t always reconstruct the original quoting, but it will construct an equivalent quoted/escaped string. For example, if you passed the argument 'uptime ; uname -a' it might print uptime\ \;\ uname\ -a or "uptime ; uname -a" or any other equivalent (see William Pursell’s answer for similar examples).

Here’s an example of using these:

printf "Running command:"
printf " %q" innercmd "$@" # note the space before %q -- this inserts spaces between arguments
printf "\n"
innercmd "$@"

If you have Bash version 4.4 or later, you can use the @Q modifier on parameter expansions to add quoting. This tends to prefer using single quotes (as opposed to printf %q‘s preference for escapes). You can combine this with $* to get a reasonable result:

echo "Running command: innercmd ${*@Q}"
innercmd "$@"

Note that $* mashes all arguments together into a single string with whitespace between them, which is normally not useful, but in this case each argument is individually quoted so the result is actually what you (probably) want. (Well, unless you changed IFS, in which case the “whitespace” between arguments will be the first character of $IFS, which may not be what you want.)

Leave a Comment