我有一个文件夹,里面有大约 5000 个.ts
文件,我想将它们合并为一个。它们遵循命名格式,例如,chunk_n
等等chunk_50
。当我运行 Windows 复制命令时,如下所示:chunk_51
chunk_52
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,问题仍然存在。这是预期的行为吗?有没有更好的方法来连接所有这些文件?
答案1
在大多数程序中,数字顺序从来没有正常顺序。直到最近,文件管理器(例如 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 所在的任何地方):
(printf '%s\n' chunk*.ts | sort -V | xargs -d'\n' cat) > allchunks.ts
在 PowerShell 中,Sort-Object
可以指定自定义比较器;没有针对“自然”(数字)顺序的预定义比较器,但是从这里你可以借用一句话:
$ToNatural = { [regex]::Replace($_, '\d+', { $args[0].Value.PadLeft(20) }) }
$filenames = Get-ChildItem chunk*.ts | Sort-Object $ToNatural
$filenames = $filenames -join "+"
cmd /c "copy $filenames allchunks.ts"
答案2
文件名为名称,而不是数字,因此将它们作为文本字符串进行比较,使用字典顺序。chunk_1
在...之前chunk_10
在...之前在...chunk_10a
之前在...之前chunk_1z
在...之前chunk_2
。
如果嘚可用,您可以使用以下命令:
<*.ts(n) >final.ts
这利用了 zsh 的功能组合:
- 无需命令的重定向将输入复制到输入。
- 和多个输入重定向,输入被连接在一起。
- 通配符模式后的括号包含glob 限定符控制匹配哪些文件以及如何对它们进行排序。 glob 限定符
n
适用整数部分的数值排序而不是默认的字典顺序,所以chunk9.ts
在 之前排序chunk10.ts
。
如果还有基本的类 Unix 实用程序可用,则可以使用cat
连接文件的命令:
cat *.ts(n) >final.ts
Zsh 可通过西弗吉尼亚海岸(sudo apt-get install zsh
),赛格威以及可能的其他适用于 Windows 的免费软件发行版。
答案3
Windowscopy
命令是CMD.EXE
内置命令。它根本不对通配符扩展进行排序。此行为至少可以追溯到 MS-DOS 6.22 COMMAND.COM
(我手头没有任何更早的版本可以测试)。碰巧的是,您的 NTFS 文件系统将文件名存储在 B 树结构中,其效果是按照接近词汇排序顺序的顺序枚举它们。
更多信息请参阅 Old New Thing 博客:
答案4
这是另一个电源外壳方法:
Get-ChildItem *.cs |
Sort-Object { [Int]( $_.BaseName -replace ('\D+','') ) } |
Get-Content |
Add-Content ConCat.cs
别名:
gci *.cs | Sort {[Int]($_.BaseName -replace ('\D+',''))} | gc | ac ConCat.cs