bash printf转义/引用功能背后的原理是什么?

时间:2019-05-12 22:06:21

标签: bash

使用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工作原理的理解,因为现在我只是不明白为什么需要当前语法。

1 个答案:

答案 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本身。