【问题标题】:Compare values within line of file, display line if condition met比较文件行内的值,如果满足条件则显示行
【发布时间】:2019-12-13 23:05:03
【问题描述】:

试图在这里解决一个问题。我有一个包含许多行的文件,但我想过滤特定行,然后比较该行中的两个值,如果满足条件,我想显示该行。这将通过整个文件完成。我正在为此使用 BASH 脚本。

行格式:

<timestamp>  <date> : <ServerName> <Device> <In>/<Out> <Value1>/<Value2>

行示例:

15:13:33   12/13/19 : Host1  Device1  In/Out 33/34

使用这个,我想做以下事情:

  • grep 用于“输入/输出”

  • 在变量中将 33 和 34 分开

  • 比较 33 和 34

  • 如果 33 和 34 之间的 delta 大于“X”,则将整行显示为 标准输出。

到目前为止,我有:

#!/bin/bash

input="logfile.log"

while IFS= read -r line
do
     echo $line
done < "$input"

我知道echo $line 在哪里,我可以执行命令。但我不确定如何“回显”它并在 while 循环中处理它。

我可以这样做:

line=`grep "In/Out" logfile.log`
var1=`grep "In/Out" logfile.log | awk -F" " '{ print $7 }' | awk -F/ '{ print $1 }'`
var2=`grep "In/Out" logfile.log | awk -F" " '{ print $7 }' | awk -F/ '{ print $2 }'`

And then compare, the difference, and if met, echo the $line value, but that feels very inefficient.

Any thoughts/input would be greatly appreciated.


【问题讨论】:

  • 哎哟!每个变量分配三个子外壳和两个管道?还要避免将`...` 用于命令替换,而应使用$(...)
  • 感谢您提醒使用 $(...) - 已经习惯了使用其他的习惯。需要打破它。顺便说一句,是的,完全同意三个子壳很糟糕。不幸的是,它确实“工作”了,真的很草率!

标签: bash awk sed


【解决方案1】:

对文件的此类(某种)复杂操作通常在单个 awk 脚本中完成。

awk -v X=10000 '
function abs(v) {return v < 0 ? -v : v}
{ 
   if ($6 == "In/Out") {
      split($7, a, "/");
      if (abs(a[1] - a[2]) > X) {
           print
      }
    }
}' 

脚本几乎是人类可读的。首先检查第 6 个字段是否为In/Out。如果是,则拆分/ 上的第 7 个字段,计算数字的绝对值并将它们与 delta 进行比较。如果他们比较“超过 X”,则打印整行。

repl 上测试。我认为您必须根据需要调整脚本。

【讨论】:

  • 这是一个好方法。我会取消该功能,只是将检查和split 合并为规则的一部分,但该功能的可重用性没有任何问题。
  • 一个函数会很棒,但老实说..我还在学习脚本。虽然我在概念上得到了某些东西,但我还是个新手。不太清楚如何合并它来调用我的文件。
  • 只是awk ' the script the script' logfile.log。您可以替换所有空格的换行符并制作一个可爱的 oneliner。
  • 谢谢,这也很有帮助!
【解决方案2】:

你会尝试以下方法:

X=3                              # Or assign to whatever
pat="In/Out +([0-9]+)/([0-9]+)"  # Regex pattern to extract the times
while IFS= read -r line; do
    [[ $line =~ $pat ]] && (( ${BASH_REMATCH[2]} - ${BASH_REMATCH[1]} > X )) && echo "$line"
done < "logfile.log"

logfile.log的测试样本:

15:13:33   12/13/19 : Host1  Device1  In/Out 33/34
15:13:33   12/13/19 : Host1  Device1  In/Out 33/36
15:13:33   12/13/19 : Host1  Device1  In/Out 33/38
15:13:33   12/13/19 : Host1  Device1  In/Out 33/40

输出:

15:13:33   12/13/19 : Host1  Device1  In/Out 33/38
15:13:33   12/13/19 : Host1  Device1  In/Out 33/40

[编辑]
根据 OP 的信息,正则表达式模式已更新:

X=3
pat="In/Out +\(([0-9]+)/([0-9]+)"
while IFS= read -r line; do
    [[ $line =~ $pat ]] && (( ${BASH_REMATCH[2]} - ${BASH_REMATCH[1]} > X )) && echo "$line"
done < "logfile.log"

示例输入:

15:13:33   12/13/19 : Host1  Device1  In/Out (33/34).
15:13:33   12/13/19 : Host1  Device1  In/Out (33/36).
15:13:33   12/13/19 : Host1  Device1  In/Out (33/38).
15:13:33   12/13/19 : Host1  Device1  In/Out (33/40).

输出:

15:13:33   12/13/19 : Host1  Device1  In/Out (33/38).
15:13:33   12/13/19 : Host1  Device1  In/Out (33/40).

模式In/Out +\(([0-9]+)/([0-9]+)由以下部分组成:

  • In/Out ... 字符串
  • + ... 一个或多个空格(可能难以辨认,但加号前有一个空格。)
  • \( ... 文字左括号
  • ([0-9]+)/([0-9]+) ... 一个或多个数字后跟一个斜线和一个 或更多数字。周围的括号生成capture groups 并且匹配的子字符串(在这种情况下为数字)存储在 bash 变量 ${BASH_REMATCH[1]}${BASH_REMATCH[2]} 按顺序排列。

希望这会有所帮助。

【讨论】:

  • 这也是一个好方法。没有什么比那里的awk 实现慢得多了。测试 [[ .. =~ .. ]] 与 ERE 和 BASH_REMATCH 与类似的 awk 实现之间的性能差异会很有趣......
  • @DavidC.Rankin 感谢您的评论。我使用 KamilCuk 生成 100,000 行输入的答案将执行时间与 awk 解决方案进行了比较。然后 awk 解决方案大约需要。 0.2 秒,而我的 bash 脚本需要 10 秒。我承认 bash 解决方案在处理长行时效率不高。
  • 谢谢你,tshiono。这看起来是一个不错的选择。为了让事情变得更难看,我需要在这里添加更多细节。最后的值用括号括起来,并有一个尾随句点,可能还有一个空格。因此它看起来更像:15:13:33 12/13/19 : Host1 Device1 In/Out (33/40). 仅捕获 33/40 进行比较的最简单方法是什么?谢谢。
  • @DavidC.Rankin - 根据 tshiono 的评论,awk 实现似乎比 BASH_REMATCH 更快?我不太担心速度,但有兴趣了解更多。您还有其他建议方式吗?
  • @pdxwarrior 感谢您的及时反馈。我通过修改将\( 附加到捕获组的模式来更新我的答案。试试看好吗?
【解决方案3】:

假设您接受的答案符合您的要求,您所需要的只是:

awk -F'[[:space:]/]+' '/In\/Out/ && ($NF - $(NF-1)) > 3' file

编辑:给出您评论中的示例输入:

$ cat file
02:22:50 11/11/19 : Host1 Device1 In/Out 208/219

$ awk -F'[[:space:]/]+' '/In\/Out/ && ($NF - $(NF-1)) > 3' file
02:22:50 11/11/19 : Host1 Device1 In/Out 208/219

因此,如果脚本对您不起作用,那么最可能的原因是您使用的是不支持字符类的 pre-POSIX awk(例如 nawk 或非常旧版本的 mawk)。鉴于此,尝试硬编码空白和制表符而不是 [:space:]

$ awk -F'[ \t/]+' '/In\/Out/ && ($NF - $(NF-1)) > 3' file
02:22:50 11/11/19 : Host1 Device1 In/Out 208/219

【讨论】:

  • 感谢您的信息,一直希望了解更多信息。肯定有更有效的方法来编写代码!我尝试了上面的行,无论出于何种原因,它都对我不起作用。假设我的文件名是“myfile” - 这是我使用的命令:awk -F'[[:space:]/]+' '/In\/Out/ &amp;&amp; ($NF - $(NF-1)) &gt; 3' myfile myfile 中的行是:02:22:50 11/11/19 : Host1 Device1 In/Out 208/219 还尝试了:02:22:50 11/11/19 : Host1 Device1 In/Out (208/219)
  • 在您的“也尝试过”案例中将括号括在最后一个字段周围显然不起作用,因为您会破坏预计只有 2 个 / 分隔的数字。请参阅我刚刚添加到答案中的编辑。
  • 我知道括号可能会导致它失败,这就是我尝试两种方式的原因。我没有收到任何错误,只是没有返回输出。即使改用[ \t/]。你是对的,man awk 显示它是 mawkawk -W version 显示 mawk 1.3.3 - 不过不用担心。上面的 bash 方法是有效的,所以不要觉得你必须花太多时间在这上面,但要知道你愿意提供帮助是非常感谢的。
  • bash 版本完全不合适,但不应使用。这根本不是 shell 脚本的工作。你能不能不安装现代版本的 awk,最好是 GNU awk,这样你就不会被困在试图让错误的方法起作用了吗?
  • 我可能不得不这样做。我正在使用 Pi 构建一些用于解析专有日志文件的工具。如果我改变我的环境,它可能会因为我的团队一起旅行的其他 Pi 而破坏它。因此,我可能只需要拿起另一个 Pi,以便我可以更新它,测试一下以确保差异会意外破坏某些东西。之后,我将能够与他们合作更新他们的。
猜你喜欢
  • 2022-10-07
  • 1970-01-01
  • 2018-12-21
  • 2017-01-13
  • 1970-01-01
  • 1970-01-01
  • 2012-01-24
  • 2019-01-15
  • 2015-10-13
相关资源
最近更新 更多