【问题标题】:Comparing three files in Awk比较 awk 中的三个文件
【发布时间】:2013-09-13 22:39:13
【问题描述】:

我有三个文件,我想使用 awk 比较前两个文件,然后用第一个文件中不匹配的名称更新最后一个文件。

文件 1:ignore.txt

bob
diana

文件 2:list.txt

alice
bob
chris
diana
elvis

文件 3:names.txt

alice
chris
elvis

文件 2 每隔一段时间就会添加新名称,因此我必须能够将它与其他两个文件进行比较,并将该列表中的任何新名称添加到 names.txt。

到目前为止,这是我的脚本,比较 List 和 ignore 是有效的,但它没有进行更新,因为我仍然不明白如何正确使用 getline 并将新数组与内存中的文件进行比较。

我正在调用这样的脚本:awk -f compare ignore.txt list.txt,它可以工作。如果我用额外的变量调用它,它不会这样做:awk -f compare -v newnames=1 ignore.txt list.txt

 BEGIN {
  file="list.txt"
  tmpfile="new_list.txt"
  }
# working
FNR == NR { names[$0]++; next }
!names[$0] {
   print > names.txt
}
{ #not working
if (newnames == 1) {
  mvcmd="mv " tmpfile file;
    while ((getline newnames < file) > 0)
      newnames[$0]++; next
     !newnames[$0];
        print > tmpfile 
     system(mvcmd)
     close(dbfile)
  }

我怎样才能让它工作?代码不是很好,因为我觉得它令人困惑。对不起。

【问题讨论】:

  • 如果顺序不重要,您可以完全忽略file3 的内容。
  • 使用 getline 的正当理由很少。如果您正在考虑使用它,请确保您阅读并完全理解 awk.info/?tip/getline 中描述的所有陷阱。

标签: shell awk gawk


【解决方案1】:

鉴于您描述问题的方式,这就是您所需要的:

awk 'FILENAME!=ARGV[3]{seen[$0]++;next} !seen[$0]++' file1 file3 file2

使用输出更新 file3:

awk 'FILENAME!=ARGV[3]{seen[$0]++;next} !seen[$0]++' file1 file3 file2 >> file3

它甚至会从 file2 中删除重复的新名称:

$ cat file1
bob
diana

$ cat file2
alice
bill
bob
chris
ted
diana
elvis
ted

$ cat file3
alice
chris
elvis

$ awk 'FILENAME!=ARGV[3]{seen[$0]++;next} !seen[$0]++' file1 file3 file2
bill
ted

如果 file3 中的所有值也存在于 file2 中,但 file2 中可能存在重复值,那么这就是您所需要的:

awk 'NR==FNR{seen[$0]++;next} !seen[$0]++' file1 file2 > file3

如果 file3 中的所有值也存在于 file2 中,并且 file2 中不可能出现重复,@sudo_O 的解决方案就可以正常工作。

【讨论】:

  • 我想我现在更好地理解了您的解决方案,除了取消设置文件名:您介意解释一下这是如何工作的吗?
  • 我没有取消设置文件名。我对 FILENAME 所做的只是测试它的值,因此当 FILENAME 与脚本的第三个参数(即file2)匹配时,脚本可以做一些不同的事情,而不是前 2 个参数/文件。
  • 哦:这很聪明。谢谢你的解释。
  • 赞成包含FILENAME 变量。我找到的所有其他答案都给出了NR==FNR 技巧,它仅适用于两个文件或某些多个文件设置,但对我不起作用。干杯!
  • 如果你有 GNU awk,你也可以考虑使用 ARGIND==3 而不是 FILENAME==ARGV[3]
【解决方案2】:

这是awk的一个很常见的用法:

$ awk 'FNR==NR{a[$0];next}!($0 in a)' file1 file2
alice
chris
elvis

重写整个file3 然后更新它更容易:

$ awk 'FNR==NR{a[$0];next}!($0 in a)' file1 file2 > file3

说明:

NR 是一个 awk 变量,在读取每条记录后递增,FNR 类似,但每次读取新文件时都会重置为 1。 NR==FNR 因此只能在读取第一个文件时为 True。读取第一个文件时,我们创建一个数组a,其中数组中的键是文件中的行,以及存储 file1 中的所有行,这将删除所有重复项。 next 是一个确保在当前记录上不再执行任何块的命令。一旦读取了file1,我们只需检查是否在数组中找到file2 中的当前行(即在file1 中)。条件!($0 in a) 没有要执行的块,因此默认情况下awk 执行{print $0}

你的脚本有很多错误,如果你想学习awk,最好的办法是阅读Effective Awk Programming

【讨论】:

  • 感谢您提供详细信息。我正在阅读 Awk 的书,但我认为我需要尝试编写一些脚本才能正确学习。
【解决方案3】:

这是一种使用grep 的方法:

grep -v -f names.txt <(grep -v -f ignore.txt list.txt) >>  names.txt

即使 names.txt 一开始不存在,这也可以工作。 (当然,如果对list.txt 进行了新的添加并再次执行命令,它会更新names.txt。)

【讨论】:

  • 谢谢。但我正在努力学习awk。但我投了赞成票。
  • 请注意,如果名称ed 存在于ignore.txt 中,那么上面将忽略tedfred 等,如果它们出现在list.txt 中。
猜你喜欢
  • 1970-01-01
  • 2018-03-04
  • 2019-05-24
  • 2012-09-05
  • 2021-01-27
  • 2014-12-25
  • 2013-10-26
  • 2023-03-27
  • 1970-01-01
相关资源
最近更新 更多