[英]awk - Filter a file with 2 other files
$ cat arquivo1.txt
6|1000|121|999
1|1000|2000|3001
2|1000|2000|3001
3|2000|11|11
4| 100|22|1
5|1000|2000|4000
1000|10|11|12
$ cat arquivo2.txt
5
1000
7
$ cat arquivo3.txt
20
我想从arquivo1.txt输出所有行,其中第二个字段(arquivo1.txt)不在arquivo2.txt中,第二个字段(arquivo1.txt)的子字符串(前2个字符)不在arquivo3.txt中。
在此示例中,输出为:
4| 100|22|1
1000|10|11|12
因此,我做了arquivo2.txt的过滤器:
$ awk -F'|' 'FNR==NR { a[$1]; next } !($2 in a)' arquivo2.txt arquivo1.txt
然后我做了arquivo3.txt的过滤器:
$ awk -F'|' 'FNR==NR { a[$1]; next } !(substr($2,1,2) in a)' arquivo3.txt arquivo1.txt
是否可以将这些命令放在一行代码中?
我需要的只是性能,因为这些文件很大(arquivo1.txt有100万行,arquivo2.txt和arquivo3.txt各自有20万行),这是实现最佳响应时间的最佳方法吗?
$ cat tst.awk
BEGIN{ FS="|" }
ARGIND==1 { f2[$0]; next }
ARGIND==2 { f3[$0]; next }
$2 in f2 { next }
substr($2,1,2) in f3 { next }
{ print }
$ awk -f tst.awk arquivo2.txt arquivo3.txt arquivo1.txt
4| 100|22|1
1000|10|11|12
上面的代码将GNU awk用于ARGIND-如果您没有gawk,请在顶部添加一行FNR==1{++ARGIND}
。
为了便于阅读,我将条件分开放置,以防万一您需要添加更多或以后更改它们。 如果您要隐式打印而不是显式打印,则可以将它们组合在一起并取反。
我有一种解决方案,但它适用于gawk (本文结尾处的awk解决方案)。 也许可以用。
使用散列是在恒定时间内进行搜索的好主意。
awk -F\| '
ARGIND == 1 {a[$1]=1;next}
ARGIND == 2 {b[$1]=1;next}
!($2 in a) && !(substr($2,1,2) in b)
' arquivo2.txt arquivo3.txt arquivo1.txt
输出:
4| 100|22|1
1000|10|11|12
我做了一些测量。 我使用以下awk脚本生成了3个文件:
time awk ' BEGIN {
for(i=0;i<1000000;++i) print i"|"i"|1000|123">"arquivo1.txt"
for(i=0;i<200000;++i) print (i*10)>"arquivo2.txt"
for(i=0;i<200000;++i) print (i*10+5)>"arquivo3.txt"
}' || exit 1
然后,我测量了运行第二个脚本所需的time
在awk之前添加time
然后将输出重定向到/dev/null
而不测量筛选。 这是三个独立运行的结果:
$./test.sh
real 0m2.880s
user 0m2.816s
sys 0m0.044s
$./test.sh
real 0m2.931s
user 0m2.892s
sys 0m0.032s
$./test.sh
real 0m2.924s
user 0m2.864s
sys 0m0.040s
(表的创建在1.5秒内完成)。 对于输入表中的100万行和筛选器表中的2x200_000行,它们在3秒内完成,并打印809_999行(至少对这两种条件都进行了多次评估)。
是您所期望的,还是对于运行时来说仍然很重要? 我的机器是带Pentium(R) Dual-Core CPU T4300 @ 2.10GHz
CPU的旧笔记本电脑。
添加
这是更快,更真实的awk解决方案:
awk -F\| '
BEGIN {
while((getline<"arquivo2.txt")>0) a[$0];
while((getline<"arquivo3.txt")>0) b[$0];
}
!($2 in a) && !(substr($2,1,2) in b)
' arquivo1.txt
对于大型测试文件,运行时间为:
real 0m2.544s
user 0m2.452s
sys 0m0.048s
real 0m2.458s
user 0m2.420s
sys 0m0.032s
real 0m2.493s
user 0m2.448s
sys 0m0.036s
因此,此过程需要2.5秒。
我希望这个能有一点帮助!
FNR==NR
允许您区分“第一”文件和“非第一”文件。 如果要说出三个文件之间的区别,则需要一种识别它们的方法。 正如其他答案所建议的那样,您可以在gawk中使用ARGIND
来执行此操作(或根据Ed的答案使用伪造的ARGIND
)。
但是当然还有其他选择。 一种是在awk脚本中读取多个文件。 我不清楚您的arquivo3.txt
文件是否始终仅是一行,但是假设是这样,则可能可行:
awk -F'|' '
BEGIN {
while (getline < "arquivo2.txt") {
two[$1];
}
getline three < "arquivo3.txt";
}
!($2 in two) && $2 !~ three
' arquivo1.txt
当然,如果需要,您可以将所有内容全部放在一行上。
如果arquivo3.txt
文件可能包含多个字符串,所有这些字符串都需要检查,则可以遍历这些值来设置条件。 (除非您说这就是您要的内容,否则我不会写出来。)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.