高效地合并/排序/独特的大量文本文件

高效地合并/排序/独特的大量文本文件

我正在尝试一个天真的:

$ cat * | sort -u > /tmp/bla.txt

失败并显示:

-bash: /bin/cat: Argument list too long

因此,为了避免像(创建一个巨大的临时文件)这样愚蠢的解决方案:

$ find . -type f -exec cat {} >> /tmp/unsorted.txt \;
$ cat /tmp/unsorted.txt | sort -u > /tmp/bla.txt

我虽然可以使用以下方法逐个处理文件(这应该减少内存消耗,并且更接近流机制):

$ cat proc.sh
#!/bin/sh
old=/tmp/old.txt
tmp=/tmp/tmp.txt
cat $old "$1" | sort -u > $tmp
mv $tmp $old

接下来是:

$ touch /tmp/old.txt
$ find . -type f -exec /tmp/proc.sh {} \;

是否有一个更简单、更unix风格的替代方案:cat * | sort -u当文件数量达到时MAX_ARG?为这样一个常见的任务编写一个小的 shell 脚本感觉很尴尬。

答案1

一个简单的修复,至少在 Bash 中有效,因为它printf是内置的,并且命令行参数限制不适用于它:

printf "%s\0" * | xargs -0 cat | sort -u > /tmp/bla.txt

echo * | xargs也可以工作,除了处理带有空格等的文件名。)

答案2

使用 GNUsort和内置的 shell printf(现在除了 的某些变体之外,所有类似 POSIX 的 shell pdksh):

printf '%s\0' * | sort -u --files0-from=- > output

现在,一个问题是,因为该管道的两个组件是同时且独立运行的,所以当左边的组件扩展 glob 时*,右边的组件可能已经创建了output文件,这可能会导致问题(可能不是-u这里) asoutput既是输入文件又是输出文件,因此您可能希望将输出转到另一个目录(> ../output例如),或者确保 glob 与输出文件不匹配。

在这种情况下解决这个问题的另一种方法是这样写:

printf '%s\0' * | sort -u --files0-from=- -o output

这样,它就可以sort打开output进行写入(在我的测试中),在收到完整的文件列表之前(在 glob 扩展后很长时间内)它不会执行此操作。output如果没有一个输入文件可读,它也可以避免破坏。

另一种写法是用zshor来写bash

sort -u --files0-from=<(printf '%s\0' *) -o output

这是使用进程替换(其中<(...)替换为引用printf正在写入的管道的读取端的文件路径)。该功能来自ksh,但ksh坚持将单独的参数扩展<(...)为命令,因此您不能将其与语法一起使用--option=<(...)。不过,它可以使用以下语法:

sort -u --files0-from <(printf '%s\0' *) -o output

cat请注意,如果文件不以换行符结尾,您会发现与在文件上提供 的输出的方法有所不同:

$ printf a > a
$ printf b > b
$ printf '%s\0' a b | sort -u --files0-from=-
a
b
$ printf '%s\0' a b | xargs -r0 cat | sort -u
ab

另请注意,sort使用区域设置 ( ) 中的排序规则算法进行排序strcollate(),并sort -u报告按该算法排序的每组行中的一个,而不是字节级别的唯一行。如果您只关心行在字节级别上是唯一的,并且不太关心它们的排序顺序,那么您可能需要将语言环境修复为 C,其中排序基于字节值(memcmp();这可能会加快速度情况显着改善):

printf '%s\0' * | LC_ALL=C sort -u --files0-from=- -o output

答案3

find . -maxdepth 1 -type f ! -name ".*" -exec cat {} + | sort -u -o /path/to/sorted.txt

这将连接当前目录中的所有非隐藏常规文件,并将它们的组合内容(同时删除重复行)排序到文件中/path/to/sorted.txt

答案4

就像@ilkkachu,但是 cat(1) 是不必要的:

printf "%s\0" * | xargs -0 sort -u

另外,如果数据很长,也许您想使用 sort(1) 选项 - 平行线=

什么时候是您计算机的 CPU 数量

相关内容