使用bash 4.4,如果我想将所有可能非文字字符转义的参数打印到脚本中,则可以使用:
printf '%q\n' "${@}"
如果我想引述参数,则可以使用:
printf '%s\n' "${@@Q}"
例如:
$ cat tst.sh
#!/usr/bin/env bash
printf 'Escaped: %q\n' "${@}"
echo '---'
printf 'Quoted: %s\n' "${@@Q}"
$ ./tst.sh 'foo bar' 'other .* args'
Escaped: foo\ bar
Escaped: other\ .\*\ args
---
Quoted: 'foo bar'
Quoted: 'other .* args'
为什么?为什么我们不简单地使用诸如%E
的{{1}}和Escaped
的{{1}}这样的格式说明符,而不是%Q
的格式说明符来表示{{1 }},然后是Quoted
的完全不同的,隐秘的语法,当与%q
结合使用时,其含义是Escaped
?
使用字母"${*@Q}"
而不是%s
来生成Escapes令人困惑,但我希望背后有一些历史原因,但我对为何其背后的技术原因更感兴趣在第二种情况下,他们不能使用简单的Quoted
或类似的printf修饰符来提高我对shell工作原理的理解,因为现在我只是不明白为什么需要当前语法。
答案 0 :(得分:4)
在编写使用任何这些功能的脚本时,唯一可以信任的保证是文档中提供的保证,如下所示:
对于printf '%q'
:
%q
-用可以作为shell输入重用的方式引用参数
对于@Q
参数转换:
扩展名是一个字符串,它是 parameter 的值,其引用格式可以用作输入。
值得注意的是,我们无法保证其中的 都将使用单引号,反斜杠或任何其他特定形式; only 保证是两者都会生成可重复用作输入的输出。
所有其他内容都是未记录的实现细节,因此易于更改。
此外,从问题中给出的描述中可以看出,这两种构造的行为在实践中可能会有所不同:
$ nl=$'\n'
$ printf '%q\n' "$nl" # in bash 5.0, emits $'\n', not a backslash followed by a newline
$ printf '%s\n' "${nl@Q}" # in bash 5.0, emits $'\n', not a newline in single-quotes
...这进一步证明了这两种构造都可以生成不能移植到baseline-POSIX shell的代码,因此“重用为shell输入”或“重用为输入”规范涉及评估通过bash本身。