【问题标题】:AWK issue : counting "non-matches"AWK 问题:计算“不匹配”
【发布时间】:2020-09-23 04:52:04
【问题描述】:

我想计算文件中某些单词的出现次数。然后我修改我的代码以额外计算有多少行与任何单词不匹配。

例如这里是我的输入文件(test.txt):

fred
fred
fred
bob
bob
john
BILL
BILL

这是我的代码:

awk '
    /fred/ { count["fred"]++ }
    /bob/ { count["bob"]++ }
    /john/ { count["john"]++ }
   END      { for (name in count) print name, "was found on", count[name], "lines." }
   ' test.txt

这很好,并给了我这个输出:

john was found on 1 lines.
bob was found on 2 lines.
fred was found on 3 lines.

现在我想计算不匹配的行数,所以我执行了以下代码:

awk '
    found=0
    /fred/ { count["fred"]++; found=1 }
    /bob/ { count["bob"]++; found=1 }
    /john/ { count["john"]++; found=1 }
    if (found==0) { count["none"]++ }
   END      { for (name in count) print name, "was found on", count[name], "lines." }
   ' test.txt

我在 if 语句中遇到如下错误:

awk: syntax error at source line 6
 context is
        >>>  if <<<  (found==0) { count["none"]++; }
awk: bailing out at source line 8

任何想法为什么这不起作用?

【问题讨论】:

  • 您能否详细解释一下这里的匹配行是什么意思?您的意思是要打印计数为 1 的行吗?请在同一时间确认一次。
  • @Brajesh 你有一个简单的语法错误。语句“if found==0”不能在 awk 中单独作为条件开始。这是一个应该嵌套在 {} 中的操作,如下所示:{ if (found==0) do_something }。或者你可以在{} 之前有这样的条件:found==0{ do_something}

标签: if-statement awk syntax


【解决方案1】:

您在使用条件时遇到简单的语法错误。该声明无效:

awk 'if (found==0) { count["none"]++ }'  # syntax error

因为if () 它不构成{} 之外可能存在的条件。你应该使用:

awk '{ if (found==0) count["none"]++ }'

awk 'found==0{ count["none"]++ }'

脚本开头的found = 0 也应该在{} 内,因为它也是一个语句。以下是一些有用的链接:{} 的外部和前面可以是这些patterns{} 内部是actions


只有必要修改的脚本可能是:

BEGIN { count["fred"]; count["bob"]; count["john"]; count["none"] }
{ found = 0 }
/fred/ { count["fred"]++; found=1 }
/bob/ { count["bob"]++; found=1 }
/john/ { count["john"]++; found=1 }
found==0{ count["none"]++ }
END { for (name in count) print name, "was found on", count[name]+0, "lines." }
  • 纠正了两个语法错误。
  • 添加了项目初始化,因为没有它,如果根本没有“fred”,则不会为“fred”打印任何行。
  • 添加了count[name]+0,所以如果项目是空字符串,将打印零。

【讨论】:

    【解决方案2】:

    考虑到您要打印仅出现 1 次的行,您能否尝试以下操作。您不需要为每个数组值定义相同的变量,因为它可能会产生误报结果。所以最好从条件数组的值中检查计数值。

    awk '
    /fred/{ count["fred"]++ }
    /bob/{ count["bob"]++}
    /john/{ count["john"]++}
    END{
      for(name in count){
         if(count[name]==1){
           print name, "was found only 1 time ", name
         }
      }
    }
    '  Input_file
    

    注意:同样在您的语法错误中,awk 适用于 condition 然后 action 的方法,因此当条件为真或假时,提到的操作将按照例如-> /test/{print "something...."} 执行。在您的情况下,您直接提到了将值分配给变量的操作,如果您使用 {found=1} 这只是为了回答您的语法错误部分。

    【讨论】:

      【解决方案3】:

      有几种方法可以实现您想要的。虽然 OP 提供的方法有效,但它并不是很灵活。我们假设您有一个字符串str,其中包含您感兴趣的单词:

      awk -v str="fred bob john"                 \
          'BEGIN{split(str,b);for(i in b) a[b[i]]; delete b }
           ($0 in a) {a[$0]++; c++}
           END {for(i in a) print i,"was found",a[i]+0", times
                print NR-c, "lines did not match" }' file1 file2 file3
      

      【讨论】:

      • 这也解决了 OP 问题,即 jimbob 将被视为匹配 bobjohnfred 将被计算两次,一次用于 john,另一次用于 fred .
      猜你喜欢
      • 1970-01-01
      • 2015-04-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-08-10
      • 2022-01-18
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多