Issue
The command printf %q
(from GNU coreutils or bash) can be used to quote an array of arguments including whitespaces, newlines and quotes. For example:
$ main() { printf '%q ' "${@}"; } && main "'a'" '"b"' 'c d' $'e\nf'
\'a\' \"b\" c\ d $'e\nf'
Is it possible to reverse this operation, i.e. create an argument array from a string created by printf %q
- In a POSIX shell?
- In Bash?
- Using additional tools?
Use case for this question
An "argument provider" uses printf %q "${@}"
to wrap a list of arguments in a single string. The arguments may hold arbitrary content, including but not limited to: quotes, quoted strings with newlines, strings that have been created by printf %q
. A shell script should be used to unwrap the string into an argument array. Unwrapping should avoid eval
and unquoted variables. The string may be handed over either as command line argument or by stdout/stdin.
Insights
xargs
does not handle quoted newlines.zsh
can do this for a string stored in variableargs
:"${(Q@)${(z)args}}"
Solution
use declare
:
quoted=$(main "'a'" '"b"' 'c d' $'e\nf')
declare -a "myvar=($quoted)"
declare -p myvar
outputs
declare -a myvar=([0]="'a'" [1]="\"b\"" [2]="c d" [3]=$'e\nf')
This can't be used to evaluate commands:
$ declare -a "myvar=($(main ls -l sensitive files))"
$ declare -p myvar
declare -a myvar=([0]="ls" [1]="-l" [2]="sensitive" [3]="files")
One thing to note: if you use declare
in a function, the variable becomes local to that function (unless you use declare -g
in which case it's global)
Answered By - glenn jackman