This is what I use:
_shlexjoin() {
echo ${*@Q}
}
Creating shell command strings
Useful for joining multiple arguments into a single string when you want to run them through a sh -c command.
Before:
cat 'aw ful".txt'
# Output: Hello world!
After:
command_string=$(_shlexjoin cat 'aw ful".txt')
sh -c "${command_string}"
# Output: Hello world!
Printing runnable commands or writing commands to scripts
Before printing ${command_string} to your terminal or writing it to a script, you'll need to escape it again, which you can do by running it through the same function:
inception=$(_shlexjoin "${command_string}")
echo "sh -c ${inception}"
# Output: sh -c ''\''cat'\'' '\''aw ful".txt'\'''
Ugly. Reliable. Just how I like it. :-)
Debugging
If, on the other hand, you just need to see how the arguments are being split, it's way easier to use something like this:
_dumpargs() {
printf '[%s]' "$@"
printf '\n'
} >&2
_dumpargs one two three # [one][two][three]
_dumpargs one 'two three' # [one][two three]
_dumpargs one two\ three # [one][two three]
_dumpargs cat 'aw ful".txt' # [cat][aw ful".txt]
_dumpargs "$(_shlexjoin cat 'aw ful".txt')" # ['cat' 'aw ful".txt']
# One arg, looks like two ^
Non-Bash alternatives
You mention bash, but if you want a functionally equivalent solution that is not bash-specific and you don't mind creating processes, you can use printf's %q format:
_shlexjoin_pr() {
printf ' %q' "$@" | cut -b2-
}
Or if python's handy, you could do this:
_shlexjoin_py() {
python3 -c 'import sys,shlex;print(shlex.join(sys.argv[1:]))' "$@"
}
They each work differently, but they are functionally equivalent:
_shlexjoin one 'two three' # Output: 'one' 'two three'
_shlexjoin_pr one 'two three' # Output: one two\ three
_shlexjoin_py one 'two three' # Output: one 'two three'
echo a bb ccc dddd | tr ' ' '\n'paste.