如何在 shell 中反转 `printf %q`?

How can `printf %q` be reversed in a shell?

命令 printf %q(来自 GNU coreutils 或 bash)可用于引用一组参数,包括空格、换行符和引号。例如:

$ main() { printf '%q ' "${@}"; } && main "'a'" '"b"' 'c d' $'e\nf'
\'a\' \"b\" c\ d $'e\nf' 

是否可以反转此操作,即从 printf %q

创建的字符串创建参数数组
  1. 在 POSIX shell?
  2. 在Bash?
  3. 使用其他工具?
这个问题的用例

“参数提供者”使用 printf %q "${@}" 将参数列表包装在单个字符串中。参数可以包含任意内容,包括但不限于:引号、带换行符的引号字符串、由 printf %q 创建的字符串。应使用 shell 脚本将字符串解包到参数数组中。展开应避免 eval 和未加引号的变量。该字符串可以作为命令行参数或通过 stdout/stdin.

传递
见解
main() { printf '%q ' "${@}"; }
quoted=$(main "'a'" '"b"' 'c d' $'e\nf')

In a POSIX shell?

真的很简单。使用以下命令重新设置位置参数:

eval "set $quoted"

In Bash?

请参阅 Glenn 对 declare 的回答。

Using additional tools?

或者编写一个字符串解析器,输出“安全”正确引用的字符串,以 eval 为来源。就像 GNU getopt 所做的那样。在 POSIX 兼容的 shell 中这样做将是一个挑战,在 POSIX C 中可能会导致错误,在 python 和 shlex 中这样做可能是微不足道的。

使用declare:

quoted=$(main  "'a'" '"b"' 'c d' $'e\nf')
declare -a "myvar=($quoted)"
declare -p myvar

产出

declare -a myvar=([0]="'a'" [1]="\"b\"" [2]="c d" [3]=$'e\nf')

这不能用于计算命令:

$ declare -a "myvar=($(main  ls -l sensitive files))"
$ declare -p myvar
declare -a myvar=([0]="ls" [1]="-l" [2]="sensitive" [3]="files")

需要注意的一件事:如果您在函数中使用 declare,变量将成为该函数的 local(除非您在其中使用 declare -g如果它是全球性的)