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

mal*_*lat 8 shell sort uniq

我正在尝试一个天真的:

$ cat * | sort -u > /tmp/bla.txt
Run Code Online (Sandbox Code Playgroud)

失败了:

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

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

$ find . -type f -exec cat {} >> /tmp/unsorted.txt \;
$ cat /tmp/unsorted.txt | sort -u > /tmp/bla.txt
Run Code Online (Sandbox Code Playgroud)

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

$ cat proc.sh
#!/bin/sh
old=/tmp/old.txt
tmp=/tmp/tmp.txt
cat $old "$1" | sort -u > $tmp
mv $tmp $old
Run Code Online (Sandbox Code Playgroud)

接着是:

$ touch /tmp/old.txt
$ find . -type f -exec /tmp/proc.sh {} \;
Run Code Online (Sandbox Code Playgroud)

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

ilk*_*chu 11

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

printf "%s\0" * | xargs -0 cat | sort -u > /tmp/bla.txt
Run Code Online (Sandbox Code Playgroud)

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

  • @LarsH,`find -exec {} +` 每次执行都会将多个文件打包在一起。使用`find -exec \;`,每个文件就是一只猫。 (4认同)

Kus*_*nda 9

find . -maxdepth 1 -type f ! -name ".*" -exec cat {} + | sort -u -o /path/to/sorted.txt
Run Code Online (Sandbox Code Playgroud)

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

  • 如果内存要求需要,@malat `sort` 将进行核外排序。相比之下,管道的左侧将消耗很少的内存。 (2认同)

Sté*_*las 9

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

printf '%s\0' * | sort -u --files0-from=- > output
Run Code Online (Sandbox Code Playgroud)

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

在这种情况下解决它的另一种方法是编写它:

printf '%s\0' * | sort -u --files0-from=- -o output
Run Code Online (Sandbox Code Playgroud)

这样一来,它的sort开放output写入和(在我的测试),它已收到文件前的完整列表,它不会做(这么长时间后,水珠已扩大)。output如果没有任何输入文件可读,它也将避免破坏。

另一种写法zshbash

sort -u --files0-from=<(printf '%s\0' *) -o output
Run Code Online (Sandbox Code Playgroud)

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

sort -u --files0-from <(printf '%s\0' *) -o output
Run Code Online (Sandbox Code Playgroud)

请注意,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
Run Code Online (Sandbox Code Playgroud)

另请注意,sort在语言环境 ( strcollate()) 中使用整理算法进行排序,并sort -u报告按该算法排序的每组行中的一个,而不是字节级别的唯一行。如果您只关心行在字节级别是唯一的,而不太关心它们的排序顺序,您可能希望将语言环境修复为 C,其中排序基于字节值(memcmp();这可能会加快事情显着):

printf '%s\0' * | LC_ALL=C sort -u --files0-from=- -o output
Run Code Online (Sandbox Code Playgroud)

  • @malat,你总是可以将一个 `print0` 函数定义为 `print0() { [ "$#" -eq 0 ] || printf '%s\0' "$@";}` 然后是 `print0 * | 排序...` (2认同)