【问题标题】:How can `printf %q` be reversed in a shell? [closed]如何在 shell 中反转 `printf %q`? [关闭]
【发布时间】:2021-12-26 22:59:12
【问题描述】:

命令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 和未加引号的变量。该字符串可以作为命令行参数或标准输出/标准输入传递。

见解
  • xargs 不处理带引号的换行符。
  • zsh can do this 用于存储在变量 args 中的字符串:"${(Q@)${(z)args}}"

【问题讨论】:

  • 这对我来说听起来很XY。你实际上想要完成什么?上下文是什么?
  • 谢谢。由于缺少上下文,我最初的问题似乎不清楚。我添加了一个部分来解释我的用例,并希望现在提供足够的上下文。

标签: bash shell arguments posix quoting


【解决方案1】:

使用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,则该变量将成为该函数的本地(除非您使用declare -g,在这种情况下它是全局的)

【讨论】:

  • 感谢您的回答!不幸的是,我最初的问题缺乏背景。 printf %q 在第一个脚本中完成,反向操作应该在第二个脚本中完成。您的解决方案是否适用于这种情况?如果是,我如何在第二个 shell 脚本中安全地使用 declare 语句,而不使用 eval 或未引用的变量?
  • 回答我这个问题:第一个脚本如何将字符串传递给第二个脚本?
  • 字符串被发送到标准输出。
  • 然后,假设您通过管道将 script1 导入 script2,则 script2 将执行 quoted=$(cat)declare -a "myvar=($quoted)" ,如图所示。
  • 谢谢。我可以确认它有效:quoted=$(cat) && declare -a "args=($quoted)" && printf '>%s< ' "${args[@]}" 使用引号、空格和换行符正确打印 4 个值。
【解决方案2】:
main() { printf '%q ' "${@}"; }
quoted=$(main "'a'" '"b"' 'c d' $'e\nf')

在 POSIX shell 中?

这真的很简单。使用以下方法重新设置位置参数:

eval "set $quoted"

在 Bash 中?

使用声明查看 Glenn 的回答。

使用其他工具?

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

【讨论】:

  • 感谢您的回答。由于安全隐患,我想避免eval,例如str="; rm -fr /"
  • e.g. str="; rm -fr /" 但是这样的字符串不是来自printf %q,所以它不是你问题的一部分。例如printf "%q " "; rm -fr /" 可以正常工作。那么您的字符串是否来自%q?您问的是“如何将printf %q 反转”而不是“如何取消引用用户的双引号字符串”-这些是需要不同方法的单独问题。如果是后者,问一个单独的问题可能是个好主意。
  • 感谢您指出这一点。我通过将我的用例添加到问题中来提供更多上下文。 printf %q 在第一个脚本中完成,反向操作应该在第二个脚本中完成。所以,我希望字符串来自printf %q,但第二个脚本也必须在误用时是安全的。我实际上 asked 你提到的“单独的问题”。
  • 你知道避免eval的POSIX解决方案吗?
  • write a parser of the string that would output "safe" properly quoted string to be sourced
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-11-28
  • 2023-02-20
相关资源
最近更新 更多