Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/perl/10.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Bash 从大文件中查找和替换_Bash_Perl_Awk_Sed_Grep - Fatal编程技术网

Bash 从大文件中查找和替换

Bash 从大文件中查找和替换,bash,perl,awk,sed,grep,Bash,Perl,Awk,Sed,Grep,屏蔽问题:我需要从单个大文本文件(input.txt,100+MB)中查找并屏蔽(即替换为“XXX”)某些术语(单词/表达式)。我需要查找的术语(10K+)保存在单个文件中(to_mask.txt)。如何以有效的方式执行此操作 我考虑分两步来完成这项工作:首先定位实际包含术语的行 grep -Ff to_mask.txt -o -n input.txt 接下来检查输出并进行实际替换(术语->“XXX”) 这似乎有点乏味,可以用更聪明的方式来做吗 欢迎使用任何基本命令的组合(grep、sed、a

屏蔽问题:我需要从单个大文本文件(input.txt,100+MB)中查找并屏蔽(即替换为“XXX”)某些术语(单词/表达式)。我需要查找的术语(10K+)保存在单个文件中(to_mask.txt)。如何以有效的方式执行此操作

我考虑分两步来完成这项工作:首先定位实际包含术语的行

grep -Ff to_mask.txt -o -n input.txt
接下来检查输出并进行实际替换(术语->“XXX”)

这似乎有点乏味,可以用更聪明的方式来做吗

欢迎使用任何基本命令的组合(grep、sed、awk、单行perl)

更新:

马科斯、凯纳沃兹、埃德·莫顿和索布里克都给出了有效的解决方案,谢谢! 我选择Sobrique的解决方案作为我接受的解决方案,因为根据我的数据,它比其他解决方案快得多。它可能无法处理某些特殊情况,但我相信它可以扩展到能够处理这些情况,而且它可以在当前条件下完成手头的工作

更新2:

以下是Kenavoz提供的解决方案供参考:

sed -f <(sed 's~^~s\~~;s~$~\~XXX\~~' to_mask.txt) input.txt
sed-f您可以尝试:

while read mask; do sed -i "s/$mask/XXX/g" input.txt; done < to_mask.txt
请注意,我尚未测试此解决方案,它可能包含一些问题…
此外,它假定
to_mask.txt
不包含任何
或任何
/
字符

更新2

对不起<代码>sed
不支持正则表达式,在替换中…:-(
我使用
perl
提出了这个解决方案,虽然更难看,但确实有效(只是在一个简单的用例上进行了测试):

fullmask=“(”;读取掩码时;如果[“$fullmask”!=”(“];则fullmask=“$fullmask |$mask”;否则fullmask=“$fullmask$mask”;fi;done
我想我应该这样处理:

#!/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
}
!/usr/bin/perl
严格使用;
使用警告;
#将掩码文件读入内存。

打开(我的$mask,“您只需要一个命令:

awk 'NR==FNR{t=(t?t"|":"")$0;next} {gsub(t,"XXX")} 1' to_mask.txt input.txt

我认为您首先应该熟悉的概念,然后处理这个问题,不止一个班轮(特别是当其他最终用户涉及)。此外,屏蔽的过滤通常使用0 b 5 C u r 1+y进行,计算机很难发现,而人类往往能够阅读。感谢@Draw Sloan,我对垃圾邮件问题有点熟悉。但是,我需要使用尽可能少的库来实现这一点(它将在客户端运行)因此,为了简单起见,我可能会失去一些效率。只要to_mask.txt文件不包含需要反斜杠的特殊字符行,这将很好地工作……如果是这样,那么to_mask.txt可以事先进行操作,以便可以如上所述使用。@louigi600:没错……OP可以将分隔符更改为avoid
/
问题…但这只能通过了解
to_mask.txt
文件的内容,并使用不存在的字符(如果有的话)来完成。@Sobrique:你也是对的…ughh…:-)看到我更新的答案,刚刚发布了一个一次编写的解决方案…感谢@louigi600的努力,但正如Sobrique指出的,重点是效率,然后是简单性。我还考虑迭代屏蔽文件,它确实执行了任务。但由于grep已经相当有效地实现了Commentz Walter算法,我没有这样做想要重新发明轮子。@MarcoS:分隔符不是问题,我可以控制掩蔽术语,没有什么奇怪的东西会出现。非捕获正则表达式似乎不起作用,但如果我把它注释出来,它就像一个符咒。顺便问一下,如果我的掩蔽术语是“a”、“b”和“c”,这对非捕获$mega_正则表达式正确吗:(?^:\b(?:a| b | c)\b)Hrm,不确定哪一位不起作用-但是的,
(?:模式)
是一个非捕获组。在这种情况下,这并不重要,但它是多余的。我通过:
(?^:\b(?:fish | foo | moo)\b获得了合理的结果(数据集小得多)
也不确定,语法似乎很好。但正如您所说,这部分是多余的,所以您的解决方案工作得很好。谢谢您在上面的评论!
#!/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
}
awk 'NR==FNR{t=(t?t"|":"")$0;next} {gsub(t,"XXX")} 1' to_mask.txt input.txt