我有一个包含大约 5000 个.ts
文件的文件夹,我想将它们合并为一个。它们遵循chunk_n
so 的命名格式,例如chunk_50
,chunk_51
等chunk_52
。当我像这样运行 windows 复制命令时:
copy /b *.ts final.ts
它不会以正常的数字顺序遍历它们。它将按以下顺序开始连接它们:
chunk_100
, chunk_1000
, chunk_1001
, chunk_1002
, chunk_1003
, chunk_1004
, chunk_1005
, chunk_1006
, chunk_1007
, chunk_1008
, chunk_1009
,chunk_101
等等。
它似乎在迭代它们时不会查看整个文件名。即使我chunk_
事先删除了它们并将它们全部命名为 1 到 5000,问题仍然存在。这是预期的行为吗?有没有更好的方法来连接所有这些文件?
在大多数程序中,数字顺序从来都不是正常顺序。直到最近文件管理器(例如 Windows 资源管理器)才开始使用“自然排序”对文件进行排序,但
copy
命令和命令并*
没有相应更改。相反,
*
扩展按文件名的 ASCII字节顺序对文件名进行排序,分别比较每个字符。例如,对于“chunk_1000.ts”与“chunk_101.ts”(扩展名是名称的一部分),前 8 个字节 (chunk_10
) 相等,然后你有0
<1
(零是 ASCII 48,一个是 ASCII 49) ,因此chunk_100(0.ts)
<chunk_101(.ts)
- 即使后面有更多数字,它们也根本不被视为整个“数字”的一部分。(字节顺序是对名称进行排序的最直接的方法——所需要的只是直接比较两个内存位置。当 RAM 以千字节为单位时,这种简单性很重要。现在已经有 50 年了,所以这几乎是不可能的现在要更改,因为许多脚本依赖于现有行为。)最简单的解决方法是将所有数字填充到固定宽度,例如
chunk0050
chunk0100
.在更高级的 shell(如 PowerShell 或 Bash)中可用的另一种解决方法是将输入列表作为单独的步骤获取和排序。例如,在 Linux 上,您将使用外部 'sort' 或 'natsort' 命令来获得所需的顺序(它们不完全是 Bash 的一部分,但通常出现在 Bash 存在的任何地方):
在 PowerShell 中,
Sort-Object
可以给定一个自定义比较器;“自然”(数字)订单没有预定义的订单,但从这里你可以借用一个单行:文件名是名称,而不是数字,因此它们作为文本字符串进行比较,使用字典顺序。
chunk_1
之前 之前chunk_10
之前chunk_10a
之前chunk_1z
之前 之前chunk_2
.如果zsh可用,则可以使用以下命令:
这利用了 zsh 功能的组合:
n
应用整数部分的数字排序而不是默认的字典顺序,因此chunk9.ts
在chunk10.ts
.如果还可以使用基本的类 Unix 实用程序,则可以使用以下
cat
命令连接文件:Zsh 可通过WSL (
sudo apt-get install zsh
)、Cygwin和可能的其他 Windows 自由软件发行版获得。Windows
copy
命令是一个CMD.EXE
内置命令。它根本不对其通配符扩展进行排序。这种行为至少可以追溯到 MS-DOS 6.22COMMAND.COM
(我没有任何更早的方便测试的东西)。碰巧您的 NTFS 文件系统将文件名存储在 B 树结构中,该结构具有以近似词法排序顺序的顺序枚举它们的效果。更多信息可在旧新事物博客上找到:
为什么 NTFS 和资源管理器在文件名排序上存在分歧?
如果没有指定排序顺序,DIR 命令排列文件的顺序是什么?
TBH 我认为所有这些离题的东西,比如字节排序等等,都在某种程度上搅乱了局面。这是每个人都应该了解的关于计算机排序的最重要、最基本的一点:
默认情况下(意思是,除非应用更高级的算法来对输入进行更复杂的解释),计算机将通过逐个字符比较字符串数据(如文件名),从第一个开始。(这可能是最左边的,尽管不一定是因为存在 RTL 语言。)
这对于文本字符串的排序是正确的,即使它们的长度不同。我们大多数人都希望名称列表按如下方式排序,例如:
在排序时,字符串中最重要的字母是第一个字母,长度无关紧要,只是总长度较短的字符串排在包含相同初始文本的较长字符串之前。
问题是,这对于数字来说是完全错误的,它们应该与对齐的最后一位数字进行比较。当我们谈论数字时,长度是最关键的因素:较长的值总是比较短的值大,因为它们有更多的有效数字。(假设我们在这里讨论的是整数;小数点会使事情进一步复杂化。)长度比值更重要的事实是我们通常右对齐数字列表的原因。
字母排序有时可以产生与数字排序相同的结果,但仅当数字表示为相等长度的字符串时。这就是为什么用 s 填充
0
解决了你的问题。不过,排序仍然没有进行正确的数字比较。(它将数字作为字符串,从左到右逐个字符进行比较。但事实证明,从左到右逐位比较正是您比较两个n位数字的方式。最重要的数字在左边,最低位在右边。)排序问题也是为什么计算行业中有这么多人(包括我自己)大力支持将日期表示为 YYYY-MM-DD 的 ISO-8601 标准。与数字一样,这种形式的日期恰好按“哑”字母排序正确排序,因为组件从左到右从最重要到最不重要排序,这正是字母排序的工作原理。排序不会将字符串YYYY-MM-DD 解释为日期,也不会将其解释为数字......但这没关系,因为它仍然会正确排序。
这是另一种PowerShell方法:
别名: