Linux 仅保留唯一行,搜索整个目录

Linux 仅保留唯一行,搜索整个目录,linux,bash,shell,sh,Linux,Bash,Shell,Sh,背景信息 在我的路由器上工作,所以只有60mb的可用ram。 我欢迎在bash中找到答案,但是sh会让我开心。 如果有一个二进制文件可以做到这一点,我不介意编译它在我的路由器上运行 TL;DR 我有一个包含文本文件的目录 如何确保在所有文件之间没有重复的行 例如,如果a.txt、b.txt和c.txt包含行apple,则它应该只保留在其中一行中(无论是哪一行) 如果需要详细信息 我有一个脚本正在下载dnsmasq使用的同一目录中的多个主机文件 我不想将所有文件合并为一个文件,但要保留它们,以删除

背景信息

在我的路由器上工作,所以只有60mb的可用ram。
我欢迎在
bash
中找到答案,但是
sh
会让我开心。
如果有一个二进制文件可以做到这一点,我不介意编译它在我的路由器上运行

TL;DR

我有一个包含文本文件的目录

如何确保在所有文件之间没有重复的行

例如,如果
a.txt
b.txt
c.txt
包含行
apple
,则它应该只保留在其中一行中(无论是哪一行)

如果需要详细信息

我有一个脚本正在下载dnsmasq使用的同一目录中的多个
主机
文件

我不想将所有文件合并为一个文件,但要保留它们,以删除重复的主机名

例如,在

y.txt

127.0.0.1    google.com
127.0.0.1    yahoo.com

包含域
yahoo.com
的行应从其中一个域中删除

如果这一行也存在于另一个文件中,也应该从中删除

我尝试过的

1-将所有文件连接到一个文件中,排序并仅保留唯一行。
在这里,只有一个大文件不是理想的解决方案

2-迭代文件,将每个文件与剩余文件进行比较。
将结果保存在临时文件中,排序,删除重复的行,最后重命名临时文件以替换原始文件。
看来我在浪费资源

#!/usr/bin/awk -f
!p[$0]++
或:


你能试试这个吗。它不会在文件中替换。它只是从所有文件中获取第二列的唯一记录

awk '!A[$NF]++' *.txt

使用
sed
和一个临时文件,您可以非常轻松地完成您试图完成的任务。只需将其中一个文件(例如
z.txt
)复制到临时文件。然后读取另一个文件中的每一行(例如
y.txt
),并将其分为
ip
host
。然后调用
sed-i
就地编辑临时文件,删除具有匹配
$host
名称的任何行。完成后,用临时文件替换
z.txt

例如:

cp -a z.txt z.tmp                   ## copy z.txt to temp file
while read -r ip host; do           ## read each line in y.txt
    sed -i "/.*$host/d" z.tmp       ## delete $host from temp
done < y.txt
mv -f z.tmp z.txt                   ## replace z.txt with temp

使用
awk
或简单地使用
grep
可能还有其他几种方法来完成相同的任务,但是grep需要读取两个文件。

您需要两次遍历所有文件。在第一步中,构建一个关联数组(hash),其中domain作为键,filename作为值。在散列中只存储域一次,以便忽略重复项。在第二步中,从文件中读取域,并查找哈希,以查看哈希中的文件名是否与当前文件匹配。如果是这样,请保留域,否则请删除它。您也可以使用文件(例如,all_domains.txt)而不是存储域和文件名的哈希。在第二步中,您可以执行
grep domain all_domains.txt | head-1
以查看该域属于哪个文件。如果它不属于当前正在处理的文件,您可以从文件中删除该域。@codeforester nice!我很喜欢你的想法。不幸的是,我不能使用阵列,因为我正在使用我的路由器(只有60mb的可用ram)。关于
all_domains
文件,它有点违背了拥有几个小文件的目的,因为
dnsmasq
可以从大
all_domains
文件中读取。我建议使用该解决方案,因为您不想合并所有的小文件。@codeforester我知道。将所有文件合并到一个大文件中是一种浪费资源的行为,而这个大文件不会被使用,或者只会被拆分成小文件。我认为这是一个非常优雅的解决方案,但OP在一篇评论中指出,将所有文件保存在内存中超出了它们可以使用的60 MB。非常好!如果我决定使用“一个大文件”方法,我肯定会使用这个!
awk
也能输出排序结果吗?或者我必须通过管道将其排序到
排序
?@BenjaminW.-我认为6000万就足够了;这只存储唯一的主机名,因此不存储IP或重复的主机名。我不确定dnsmasq是否能够处理那么多条目,而不会有自己的缓存问题。@RASG–这将按每个主机的第一次出现排序。要想做得更好,你必须通过
sort-k2
左右的管道进行排序。因此,如果我有4个文件,我怎么能将每个文件与剩余的文件进行一次比较?而不是单独对
z.tmp
运行
sed
,而是为所有
3
其他文件创建
tmp
文件(例如
w.tmp
x.tmp
z.tmp
),然后简单地
sed-i/.*$host/d”*.tmp
。这将从任何
tmp
文件中删除
y.txt
中找到的任何主机。完成后,只需将所有3个
tmp
文件移回
.txt
文件。因此,如果您有4个文件,您可以一次迭代其中一个文件并从其他3个文件中删除。您必须进行循环并迭代所有4个文件(一个和另外三个——每个文件)以确保消除所有重复。
awk '!A[$NF]++' *.txt
cp -a z.txt z.tmp                   ## copy z.txt to temp file
while read -r ip host; do           ## read each line in y.txt
    sed -i "/.*$host/d" z.tmp       ## delete $host from temp
done < y.txt
mv -f z.tmp z.txt                   ## replace z.txt with temp
$ cat z.txt
0.0.0.0    apple.com