假设我是一个精神病患者,我将我的文件命名为:
[-] `{title: "Non-Metadata", id: "s4a4ji"}`{.JSON5}.dir
这是我尽力做出最丑陋、最合理的文件名的尝试 —— 它使用 Pandoc Markdown 和 JSON5。
POSIX
如果我将其输入到 中ls
,它会为我提供一个转义版本,这样任何符合 POSIX 标准的 shell(当然是 sh,但bash
在 Fedora 40 中也是 )都可以毫无问题地解释它:
-
ls "$PWD"
-
' [-]'$'\t''`{title: "Non-Metadata",'$'\t''id: "s4a4ji"}`{.JSON5}.dir'
八位字节
但是,tree
似乎file
用一种八位字节转义表示形式替换非 ASCII 字符,而这种表示形式似乎无法以上述方式使用:
-
tree "$PWD"
-
. └── [-]\011`{title: "Non-Metadata",\011id: "s4a4ji"}`{.JSON5}.dir 2 directories, 0 files
UTF-8
当然,PowerShell CoreGet-ChildItem
只输出完整的 UTF-8 表示形式:
-
Get-ChildItem -LiteralPath "$PWD" | Select-Object -ExpandProperty 'Name'
-
[-] `{title: "Non-Metadata", id: "s4a4ji"}`{.JSON5}.dir
这些操作方式为何不同?我期望ls
、tree
和file
至少操作方式相同,因为据我所知,这些都是 GNU CoreUtils。此外,PowerShell 似乎表明,没有什么从根本上强制这些工具转义非 ASCII 字符,那么它们为什么要这样做呢?
据我所知,在您的版本
ls
发布时这不是 POSIX 语法;它$'...'
直到上周才成为 POSIX 2024 的一部分;在此之前它只是一个被广泛采用的 ksh 语法功能。这是八进制表示,使用模仿 C 字符串文字的语法。(它确实支持十六进制,但八进制在某种程度上是“传统的”。)我认为这是 libbsd
strvis()
默认使用的样式。此外,这些不是“非 ASCII”字符:制表符是 ASCII 的重要组成部分,涵盖了从 0 到 127 的所有字节值。高于 127(0x7F、0177)的字节值将是非 ASCII。
它们是三个不同的程序,就这么简单。没有“输出文件名”的通用函数——每个程序在将其写入 stdout 之前都会应用它想要的任何转义。(这也是 GNU 和 BSD 风格不同的地方。)
不是。只有
ls
GNU coreutils 是 GNU 的 – 其他两个根本不属于 GNU 范畴。程序也可以在没有“从根本上被强迫”的情况下执行操作。例如,许多程序会故意转义至少 ASCII C0 控制字符,以便它们不会被终端(错误)解释。
Coreutils
ls
一直选择--quoting-style
s 是为了使“无效”字符更容易区分,并且(如果我没记错的话,从 9.0 版本开始)故意默认使用 Bash 样式的引用,以便可以将此类文件名直接复制/粘贴到 GNU shell 中。(另请参阅历史上的 Unix
dsw
工具,以及人们用来删除无法输入文件名的文件的各种其他技巧。)另一方面,PowerShell 甚至不认为字符串是文件名:它只是某个对象的字符串值属性。当整个对象( Get-ChildItem 的结果)被格式化为 stdout 时,它会应用一些转义,但各个属性并不知道它们的位置。
tree
只是做了基本的努力来防止 ASCII 控制字符对您的终端产生不良影响,但并没有真正针对可复制粘贴性。