屏蔽问题:我需要从单个大文本文件(input.txt,100 + MB)中找到并屏蔽(即,用“XXX”替换)某些术语(单词/表达式)。我需要找到的术语(10K +)保存在单个文件(to_mask.txt)中。如何以有效的方式执行此操作?
我正在考虑分两步执行此操作:首先找到实际包含术语
的行grep -Ff to_mask.txt -o -n input.txt
接下来浏览输出并进行实际替换(术语 - >“XXX”)。
这看起来有点乏味,可以用更聪明的方式完成吗?
欢迎任何基本命令组合(grep,sed,awk,one-line-perl)!
更新
MarcoS,Kenavoz,Ed Morton和Sobrique都提供了有效的解决方案,谢谢! 我选择Sobrique的解决方案作为我接受的解决方案,因为它比我的数据快得多。它可能无法处理某些特殊情况,但我确信它可以扩展到能够这样做,而且它可以完成当前工作中的工作。
UPDATE2:
供参考,以下是Kenavoz提供的解决方案:
sed -f <(sed 's~^~s\~~;s~$~\~XXX\~~' to_mask.txt) input.txt
答案 0 :(得分:1)
你可以尝试:
while read mask; do sed -i "s/$mask/XXX/g" input.txt; done < to_mask.txt
可能不是世界上最有效的解决方案,但它应该做的工作...... :-)
作为奖励,它只使用shell和sed
命令......
<强>更新强>:
这是一个更快的解决方案(它只写一次大input.txt
个文件)。
它首先构建一个fullmask
变量,作为由|
(OR
运算符)分隔的所有掩码的串联。
虽然,我发现它不如第一个发布的解决方案明确......: - )
fullmask=""; cat to_mask.txt | while read mask; do fullmask="$fullmask|$mask"; done && sed -i "s/$fullmask/XXX/g" input.txt
请不要我没有测试过这个解决方案,它可能包含一些问题...
更重要的是,它假定to_mask.txt
不包含任何|
或任何/
个字符......
更新2 :
抱歉! sed
不支持正则表达式,替换...... :-(
我使用perl
提出了这个解决方案,更加丑陋,但绝对有效(只是在一个简单的用例上测试过):
fullmask="("; while read mask; do if [ "$fullmask" != "(" ]; then fullmask="$fullmask|$mask"; else fullmask="$fullmask$mask"; fi; done < to_mask.txt; fullmask="$fullmask)"; perl -p -i -e "s/$fullmask/XXX/g" input.txt
答案 1 :(得分:1)
我想我会像这样解决它:
#!/usr/bin/perl
use strict;
use warnings;
#read the mask file into memory.
open ( my $mask, '<', "to_mask.txt" ) or die $!;
chomp ( my @terms = <$mask> );
close ( $mask );
#build a really big regex
#map quotemeta means handling metachars.
#if you _know_ there are none, then you can omit this.
#or if you actually want to be able to use regex in your terms file.
my $mega_regex = join "|", map { quotemeta } @terms;
#compile it into a non-capturing regex, and use \b to anchor on word boundaries.
#You don't want to be filtering out Scunthorpe ...
$mega_regex = qr/\b(?:$mega_regex)\b/;
#<> means iterate 'stdin' or 'files specified on command line'.
#just like how grep/sed/awk does it
while ( <> ) {
s/$mega_regex/XXX/g;
print; #to STDOUT
}
答案 2 :(得分:1)
你只需要1个命令:
awk 'NR==FNR{t=(t?t"|":"")$0;next} {gsub(t,"XXX")} 1' to_mask.txt input.txt