以正确的顺序将大量文件集中在一起

sod*_*ate 25 find brace-expansion files cat

我有大约 15,000 个名为file_1.pdbfile_2.pdb等的文件。我可以通过执行以下操作来按顺序对其中的几千个文件进行分类:

cat file_{1..2000}.pdb >> file_all.pdb
Run Code Online (Sandbox Code Playgroud)

但是,如果我对 15,000 个文件执行此操作,则会出现错误

-bash: /bin/cat: Argument list too long
Run Code Online (Sandbox Code Playgroud)

我已经看到通过这样做解决了这个问题,find . -name xx -exec xx但这不会保留文件加入的顺序。我怎样才能做到这一点?

Kus*_*nda 51

使用find,sortxargs

find . -maxdepth 1 -type f -name 'file_*.pdb' -print0 |
sort -zV |
xargs -0 cat >all.pdb
Run Code Online (Sandbox Code Playgroud)

find命令查找所有相关文件,然后将它们的路径名打印出来,然后sort执行“版本排序”以按正确的顺序排列它们(如果文件名中的数字已零填充到固定宽度,我们将不需要-V)。xargs获取此排序路径名列表并cat尽可能大批量地运行这些路径名。

即使文件名包含奇怪的字符(如换行符和空格),这也应该有效。我们使用-print0withfind来给出以sort空字符结尾的名称进行排序,并sort使用-z. xargs也用它的-0标志读取以空字符结尾的名字。

请注意,我将结果写入名称与模式不匹配的文件file_*.pdb


上述解决方案对某些实用程序使用了一些非标准标志。这些实用程序的 GNU 实现以及至少 OpenBSD 和 macOS 实现都支持这些。

使用的非标准标志是

  • -maxdepth 1, 使find只进入最顶层目录而不进入子目录。POSIXly,使用find . ! -name . -prune ...
  • -print0, 使find输出以 nul 结尾的路径名(这被 POSIX 考虑但被拒绝)。可以-exec printf '%s\0' {} +改用。
  • -z, 制作sort以空结尾的记录。没有 POSIX 等价。
  • -Vsort例如200在之后进行排序3。没有 POSIX 等效项,但如果文件名具有固定前缀,则可以用文件名特定部分的数字排序替换。
  • -0, 使xargs读取以空字符结尾的记录。没有 POSIX 等价。POSIXly,需要以xargs.

如果路径名很乖,如果目录结构是平的(无子目录),那么可以凑合没有这些标志,除了-Vsort

  • 您也可以使用提问者的规范更简洁地将其写为 `printf 'file_%d.pdb\0' {1..15000} | xargs -0 cat`,甚至凯文的观点,`echo file_{1..15000}.pdb | xargs 猫`。`find` 解决方案的开销要大得多,因为它必须在文件系统中搜索这些文件,但在某些文件可能不存在时它更有用。 (6认同)
  • @Kevin 虽然你说的是真的,但可以说最好有一个适用于更一般情况的答案。在接下来的一千人中有这个问题,其中一些人的文件名中可能会有空格或其他任何内容。 (4认同)

Sté*_*las 14

zsh(该{1..15000}运算符来自哪里):

autoload zargs # best in ~/.zshrc
zargs file_{1..15000}.pdb -- cat > file_all.pdb
Run Code Online (Sandbox Code Playgroud)

或者对于file_<digits>.pdb按数字顺序排列的所有文件:

zargs file_<->.pdb(n) -- cat > file_all.pdb
Run Code Online (Sandbox Code Playgroud)

(其中<x-y>是一个匹配十进制数 x 到 y 的 glob 运算符。没有xnor y,它是任何十进制数。相当于extendedglob's[0-9]##kshglob's +([0-9])(一个或多个数字))。

随着ksh93,利用其内置cat的命令(所以不会受的该限制execve()系统调用,因为没有执行):

command /opt/ast/bin/cat file_{1..15000}.pdb > file_all.pdb
Run Code Online (Sandbox Code Playgroud)

bash/ zsh/ ksh93(支持zsh's{x..y}并具有printf内置):

printf '%s\n' file_{1..15000}.pdb | xargs cat > file_all.pdb
Run Code Online (Sandbox Code Playgroud)

在 GNU 系统或兼容系统上,您还可以使用seq

seq -f 'file_%.17g.pdb' 15000 | xargs cat > file_all.pdb
Run Code Online (Sandbox Code Playgroud)

对于xargs基于 - 的解决方案,必须特别注意包含空格、单引号或双引号或反斜杠的文件名。

像 for 一样-It's a trickier filename - 12.pdb,使用:

seq -f "\"./-It's a trickier filename - %.17g.pdb\"" 15000 |
  xargs cat > file_all.pdb
Run Code Online (Sandbox Code Playgroud)


小智 12

for 循环是可能的,而且非常简单。

for i in file_{1..15000}.pdb; do cat $i >> file_all.pdb; done
Run Code Online (Sandbox Code Playgroud)

缺点是你调用cat了很多次。但是,如果您不能确切地记住如何使用这些东西find并且调用开销在您的情况下不算太糟糕,那么值得记住。