我想对所有子目录中的所有文件进行排序。我在 256 个目录中有 65536 个文件,每个文件一行包含一个单词,并且每个文件都包含重复项。
我想要的是使用 -u 选项对这些进行排序(我不知道为什么,但如果我将排序传递给 uniq -u 命令,它实际上会删除重复的唯一行,这很奇怪,但无论如何),但我不这样做想要任何输出文件,我想排序读取内存中的文件,然后覆盖它们。我尝试了 -o 选项,但它需要一个文件名。
有没有办法递归地执行此操作?
谢谢 :)
答案1
那可以只是:
find . -type f -size +1c -exec sort -uo {} {} ';'
(这里跳过小于 2 字节大的文件,因为您至少需要 3 个字节来制作两个不同的行,或者可能需要 2 个"\nx"
字节,其中空行后面跟着一个不定界的行1)。
请注意,默认排序顺序sort
基于区域设置的排序规则算法。
两行即使字节与字节不相同,也可以进行相同的排序,特别是当这些行包含不形成有效字符的字节序列时,并且在 GNU 系统(例如 Debian)上,对排序顺序不相同的字符进行排序定义的。
你可以做:
LC_ALL=C find . -type f -exec sort -uo {} {} ';'
相反,在基于 ASCII 的系统(例如适用于所有体系结构和内核的 Debian)上,将按字节值而不是语言环境排序顺序(或 IOW,C 语言环境的排序规则基于字节值)对行进行排序,并且应该保证两个字节不同的行不会排序相同。
sort
这对每个文件运行一次调用。如果文件相当短,为了加快速度,您可以这样做zsh
:
zmodload zsh/mapfile
for f (**/*(N.)) print -rC1 -v 'mapfile[$f]' - ${(fou)mapfile[$file]}
sort
这避免了多次运行外部命令,而是使用其o
参数u
扩展标志来对行进行排序和唯一。请注意,它会删除输入中的空行(如果有),并跳过隐藏文件(D
如果需要,请添加 glob 限定符)。
与 GNU 相反sort -u
,zsh
不会将字节与字节不相同的两个字符串视为重复(即使它们排序相同),因此您不需要将语言环境修复为 C 。
$ 区域设置标题字符映射 英国的英语语言环境 UTF-8 $a=(
答案2
$ find . -type f -exec perl -MList::MoreUtils=uniq -i -0 -n -e \
'print join("\n", uniq split /\n/), "\n"' {} +
这用于find
将文件名传递给perl
使用-i
就地编辑选项的脚本,并且列表::更多实用工具uniq
为perl提供一个函数(perl有sort
内置的,但没有uniq
)。 perl 的选项与的选项-n
几乎执行相同的操作- 它迭代每个输入记录,但默认情况下不打印任何内容(即,它只打印显式打印的内容)。sed
-n
该脚本将输入记录分隔符设置为 NUL(使用该-0
选项),以便它可以一次性吞入每个文件。然后它按换行符分割每个输入文件,并以唯一的排序顺序打印它们。
List::MoreUtils
位于liblist-moreutils-perl
Debian(以及 Ubuntu、Mint 等)上的软件包中,并且在 Fedora、Centos 甚至 RHEL 上可能有类似的软件包名称。其他发行版可能也打包了它。否则从 CPAN 安装它。
如果您不想(或不能)安装List::MoreUtils
,您可以使用关联数组(也称为“哈希”)。例如
$ find . -type f -exec perl -i -0 -n -e \
'%uniq = map {$_ => 1} split /\n/;
print join("\n", sort keys %uniq), "\n"' {} +
我会首先在一些垃圾文件上运行其中任何一个,以确保它能够满足您的要求。和/或在没有-i
选项的情况下测试它,以便它只打印到标准输出。我在一个名为的文件上测试了它junk
,其内容如下:
6
5
5
4
3
2
1
1
运行上述任一版本后,junk
包含:
1
2
3
4
5
6
如果您希望它们按相反的顺序排列,您可以在or函数reverse
之前添加。例如。sort
uniq
print join("\n", reverse sort keys %uniq),"\n"
或者,如果您有海绵从 Joey Hess 的moreutils
安装中,您可以使用find
、shell
、sort -u
和sponge
:
find . -type f -exec sh -c \
'for f in "$@"; do sort -u "$f" | sponge "$f" ; done' find-sh {} +
这将比 perl 版本慢得多,因为必须sh
forksort
一次sponge
对于每个输入文件,而不是perl
仅分叉一次(*)来find
处理所有输入文件。
(*) 假设所有文件名都适合一个命令行。在 Linux 上,这大约相当于 200 万个字符。如果所有文件名组合起来都比这个长,则可能需要 fork perl 两次或三次(或者可能更多)。
这同样适用于sh
由 分叉,它可能需要分叉多次 - 但与分叉和find
的成本相比,分叉 sh(或 perl)几次的成本可以忽略不计。sort
sponge
每个文件一次。节省时间来自于使用+
withfind ... -exec ... {}
而不是\;
。