繁体   English   中英

awk-用2个其他文件过滤文件

[英]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}

为了便于阅读,我将条件分开放置,以防万一您需要添加更多或以后更改它们。 如果您要隐式打印而不是显式打印,则可以将它们组合在一起并取反。

我有一种解决方案,但它适用于 (本文结尾处的解决方案)。 也许可以用。

使用散列是在恒定时间内进行搜索的好主意。

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

我做了一些测量。 我使用以下脚本生成了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之前添加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 -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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM