我有大约 15,000 个名为file_1.pdb
,file_2.pdb
等的文件。我可以通过以下操作按顺序排列其中的几千个:
cat file_{1..2000}.pdb >> file_all.pdb
但是,如果我对 15,000 个文件执行此操作,则会收到错误消息
-bash: /bin/cat: Argument list too long
我已经看到通过这样做解决了这个问题,find . -name xx -exec xx
但这不会保留文件加入的顺序。我怎样才能做到这一点?
使用
find
,sort
和xargs
:该
find
命令找到所有相关文件,然后将它们的路径名打印出来,以sort
执行“版本排序”以使它们以正确的顺序排列(如果文件名中的数字已被零填充到我们不需要的固定宽度-V
)。xargs
获取这个排序路径名列表,并cat
尽可能大批量地在这些路径名上运行。即使文件名包含奇怪的字符(例如换行符和空格),这也应该有效。我们使用
-print0
withfind
给出以sort
nul 结尾的名称进行排序,并sort
使用-z
. 也使用其标志xargs
读取以 nul 结尾的名称。-0
请注意,我将结果写入名称与模式不匹配的文件
file_*.pdb
。上述解决方案对某些实用程序使用了一些非标准标志。这些实用程序的 GNU 实现以及至少 OpenBSD 和 macOS 实现都支持这些功能。
使用的非标准标志是
-maxdepth 1
,find
只进入最顶层目录,不进入子目录。POSIXly,使用find . ! -name . -prune ...
-print0
, 以find
输出以 nul 结尾的路径名(POSIX 考虑过但拒绝了)。可以-exec printf '%s\0' {} +
改用。-z
, 制作sort
以 nul 结尾的记录。没有 POSIX 等价性。-V
, 进行sort
排序,例如200
在 之后3
。没有 POSIX 等价性,但如果文件名具有固定前缀,则可以用文件名特定部分的数字排序替换。-0
, 以xargs
读取以 nul 结尾的记录。没有 POSIX 等价性。POSIXly,需要以xargs
.如果路径名表现良好,并且目录结构是扁平的(没有子目录),那么可以不用这些标志,除了
-V
withsort
。使用
zsh
(该{1..15000}
运算符来自哪里):file_<digits>.pdb
或按数字顺序排列所有文件:(其中
<x-y>
是一个匹配十进制数 x 到 y 的全局运算符。没有x
nory
,它是任何十进制数。等效于extendedglob
's[0-9]##
或kshglob
's+([0-9])
(一个或多个数字))。使用
ksh93
, 使用其内置cat
命令(因此不受execve()
系统调用限制的影响,因为没有执行):使用
bash
//zsh
(ksh93
支持zsh
's{x..y}
并printf
内置):在 GNU 系统或兼容系统上,您还可以使用
seq
:对于
xargs
基于 - 的解决方案,必须特别注意包含空格、单引号或双引号或反斜杠的文件名。像 for 一样
-It's a trickier filename - 12.pdb
,使用:for 循环是可能的,而且非常简单。
缺点是你调用
cat
了很多次。但是,如果您不记得到底如何做这些事情,find
并且调用开销在您的情况下还不错,那么值得牢记。前提
对于具有特定名称格式[ 1 , 2 ]的15k 文件,您不应该出现该错误。
如果您从另一个目录运行该扩展并且您必须添加每个文件的路径,那么您的命令的大小会更大,当然它可能会发生。
解决方案从该目录运行命令。
最佳解决方案如果相反,我猜错了,而您从文件所在的目录运行它...
恕我直言,最好的解决方案是Stéphane Chazelas 的解决方案:
使用 printf 或 seq;对 15k 个文件进行了测试,只有它们的编号在预缓存中,它甚至是更快的文件(目前,除了文件所在目录中的 OP 文件)。
多说几句
您应该能够更长时间地传递给您的 shell 命令行。
您的命令行长度为 213914 个字符,包含 15003 个单词
cat file_{1..15000}.pdb " > file_all.pdb" | wc
...即使为每个字添加 8 个字节,也远远低于内核 3.13.0 上报告的 2097142 (2.1M)或报告为“我们实际上可以执行的最大命令长度”
ARG_MAX
的稍小 2088232的 333 938 字节 (0.3M)使用“通过xargs --show-limits
看看你的系统的输出
懒惰引导解决方案
在这种情况下,我更喜欢使用块,即使通常会出现一个省时的解决方案。
逻辑(如果有的话)是我懒得写 1...1000 1001..2000 等等...
所以我要求一个脚本为我做。
只有在我检查了输出的正确性之后,我才会将其重定向到脚本。
...但懒惰是一种心态。
由于我对
xargs
(我真的应该在xargs
这里使用)过敏并且我不想检查如何使用它,所以我按时完成重新发明轮子,如下面的示例(tl; dr)。请注意,由于文件名是受控的(没有空格、换行符...),您可以轻松地使用下面的脚本。
tl;博士
版本 1:作为可选参数传递第一个文件号,最后一个,块大小,输出文件
版本 2
调用 bash 进行扩展(在我的测试中慢了约 20%)。
当然,您可以继续前进并完全摆脱
seq
[ 3 ](来自 coreutils)并直接使用 bash 中的变量,或者使用 python,或者编译 ac 程序来完成它[ 4 ] ...另一种方法可能是