使用 awk 比较 BASH 中的两行

使用 awk 比较 BASH 中的两行

我有这样的文本文件。

2015-11-24 12:59:37.112 128.206.6.136 source
2014-11-24 12:59:36.920 8.8.8.8 source
2014-11-24 14:59:38.112 23.234.22.106 destination
2014-11-24 13:59:37.113 23.234.22.106 source
2014-11-24 12:59:29.047 74.125.198.141 source
2014-12-25 12:59:36.920 74.125.198.148 destination

如果一个特定的 Ip 地址被标记为源和目的地,那么我想将该 Ip 标记为两个都。在本例中,IP 23.234.22.106 既是源也是目标。所以,我想将其标记为两个都

我想要的输出应该是这样的

2015-11-24 12:59:37.112 128.206.6.136 source
2014-11-24 12:59:36.920 8.8.8.8 source
2014-11-24 14:59:38.112 23.234.22.106 both
2014-11-24 12:59:29.047 74.125.198.141 source
2014-12-25 12:59:36.920 74.125.198.148 destination

这是我尝试过的。

cat input.txt | awk '{print $3}' | sort | uniq | while read line

do 
grep $line input.txt | sort -r -k1 | head -1
done

但是,我不明白如何将特定 Ip 标记为两个都如果它是源也是目的地。在本例中为 23.234.22.106。

我该如何使用 awk 来做到这一点?任何对此的帮助将不胜感激。谢谢

答案1

尝试用sed

sed '
    N    #add next line
    s/\([0-9.]\+\)\s\S\+\n.*\s\1\s\S\+$/\1 both/
    P    #print first line from two
    D    #remove first line, return to start
    ' input.txt
  • [0-9.]\+一组数字和点
  • \s空格或制表符
  • \S\+一组非空格符号
  • \n新队
  • .*任何符号
  • \1括号内组的反向引用\(...\)
  • $图案结束

(修改:删除t命令(tnx 2杰蒂尔) 并在组前添加 \space 以检查完整地址)

答案2

perl

#! /usr/bin/perl

use strict;

my @lines=();

while(<>) {
  chomp;
  s/#.*//g;        # strip comments
  s/^\s*|\s*$//g;  # strip leading and trailing spaces
  next if (/^$/);  # skip blank lines

  if (! scalar @lines) {
    # store the first line of the file in the array
    # we can't do anything else yet, so skip to the next line.
    push @lines, $_;
    next;
  } else {
    push @lines, $_;

    # split both lines into space-separated fields.
    my @a = split /\s+/, $lines[0];
    my @b = split /\s+/, $lines[1];

    # if 3rd fields are the same, change to "both"
    if ($a[2] eq $b[2]) {
      @lines = map { $_ =~ s/(source|destination)$/both/oi; $_} @lines;
    }
  }
  print $lines[0],"\n";
  shift @lines;
}
print $lines[0],"\n";

这里的想法是使用一个数组(@lines)来保存当前行和上一行。如果两行的第三个字段(perl 数组从零开始)相同,则将字符串“源”或“目标”更改为“两者”。

打印上一行,无论它们是否被更改。然后从数组中删除前一行,以便shift在下一次循环时,当前行将是前一行。

最后,循环完成后,打印最后一个输入行。

输出:

$ ./swatesh.pl <swatesh.txt 
2015-11-24 12:59:37.112 128.206.6.136 source
2014-11-24 12:59:36.920 8.8.8.8 source
2014-11-24 14:59:38.112 23.234.22.106 both
2014-11-24 13:59:37.113 23.234.22.106 both
2014-11-24 12:59:29.047 74.125.198.141 source
2014-12-25 12:59:36.920 74.125.198.148 destination

一些注意事项:

sed脚本运行良好,那么为什么您会选择使用它perl呢?有什么区别?

@Costas 的sed版本更快,如果您有数百万行要处理,这可能会很重要。

perl版本明确检查两行上的字段 3 是否完全相同,而该sed版本仅检查看起来类似于 IP 地址的模式是否稍后在同一连接的双行中重复(这不一定是问题 -对于您的示例输入,该sed版本完美地针对您的示例进行了优化)。

perl版本可能更容易适应不同的输入。

循环开始处的代码是一段有用的代码,我在许多 Perl 脚本中使用它来跳过空白行并支持#文本文件中的注释。我经常在脚本中做同样的事情sed,但是 sed 脚本越长,可读性就越差......而且我喜欢编写在 6 个月的时间内一眼就能理解的代码。

除了这些相对较小的细节之外,两个脚本都使用非常相似的算法。

相关内容