【问题标题】:bash, regex, post process row and column databash、正则表达式、后处理行和列数据
【发布时间】:2016-08-25 07:03:02
【问题描述】:

我的项目是从日志文件中提取特定数据。

数据有列和行;有些是文本字符串,有些是数字。

为了只提取我感兴趣的信息行,我尝试了一个 awk 命令,该命令曾经可以工作(很久以前),看起来像这样:

awk '{if($1 == text1Exp || textExp2 || textExp3) {print $1, $2}}' file.log

嗯,效果不太好。搜索给了我以下运行良好的语法:

awk '/Counter/ || /IfInErrors/ || /IfOutErrors/ {print $0}'

我制作了一个运行良好的简短脚本:

for i in `ls`; do awk '/Counter/ || /IfInErrors/ || /IfOutErrors/ {print $0}' $i > $i.csv; done

我只用我想要的数据处理了 200 多个文件。现在我需要将数据作为 csv 文件导入,其中制表符和空格都设置为一个长度。我使用以下方法迭代地完成了这项工作:

sed -i 's/  */ /g' *.csv   # all tab white space and white space set

接着是:

sed -i "s/ /,/g" *.csv     # substitute spaces with a comma.

还有一些其他替换可以清理数据的格式。现在我的数据看起来很干净:

,Counter Name,port 1/1/x33,port 1/1/x34,port 1/1/x35,port 1/1/x36
,IfInErrors:,0,0,0,0
,IfOutErrors:,0,0,0,0
,Counter Name,port 1/1/x37,port 1/1/x38,port 1/1/x39,port 1/1/x40
,IfInErrors:,**95**,0,0,0
,IfOutErrors:,0,0,0,0

(我知道前导逗号但很懒惰;我将在 0.2a 版中修复它)。

我要完成以下工作:

  1. 计算 IfInErrors && IfOutErrors 的零值的数量
  2. 计算 IfInErrors && IfOutErrors 的非零值的数量
  3. 忽略带有 Counter ... 的行
  4. 报告 IfInErrors && IfOutErrors 的非零值的总和并在文件中捕获
  5. 报告零值的总和并将总和捕获到在需求 4 中创建的同一文件中。

不幸的是蚱蜢,这超出了你的剧本,大师说。 :(

我尝试在 Exell 中处理数据,但是,你知道...

我尝试了一些在线课程,但尽管它们有所帮助,但我发现通过实践、努力和与导师合作,我学得最好。我试图找到帮助大卫卡拉丹的盲人肖林僧侣,但他退休了,用棍子打我......

有什么建议吗?

【问题讨论】:

  • 问题显然是你拼错了 Shaolin ;-)。编辑您的问题以包含一些简洁、可测试的示例输入和预期输出,您可能会得到一些帮助。

标签: regex bash logging awk sed


【解决方案1】:

所以,第一次和第二次尝试确实需要一些工作 - 因为 if/then 不能按照您的想法工作......此外,使用 awk可以很容易地处理分隔符替换> 使用 OFS 内置变量。

awk 'BEGIN { OFS="," } $1 ~ /^Counter/ || $1 ~ /^IfInErrors/ || $1 ~ /^IfOutErrors/ { $1=$1; print > FILENAME ".csv" }' file.log

此代码使用限制性更强的正则表达式检查第一个字段,其中包含“开头为”或“^”的概念。请注意,在每种情况下,我们必须每次都重复“$1 ~”……如果我们要将其放入原始的“if 语句”中,这也是需要的。但是...我们还可以构造一个更简洁的正则表达式,它可以一次处理所有情况,而无需逻辑或...

awk 'BEGIN { OFS="," } $1 ~ /^Counter|^If(In|Out)Errors/ { $1=$1; print > FILENAME ".csv" }' file.log

OFS="," 语句将输出分隔符设置为逗号。 $1=$1 实际上将分隔符的更改应用于输入行。不合格的打印语句打印当前行缓冲区。 > FILENAME ".csv" 输出到你想要的文件。

这使我们得到以下结果:

Counter,Name,port,1/1/x33,port,1/1/x34,port,1/1/x35,port,1/1/x36
IfInErrors:,0,0,0,0
IfOutErrors:,0,0,0,0
Counter,Name,port,1/1/x37,port,1/1/x38,port,1/1/x39,port,1/1/x40
IfInErrors:,**95**,0,0,0
IfOutErrors:,0,0,0,0

这与您的输出不同,但这是我对您的脚本的期望——因为这里的所有空格都转换为单个逗号。我认为你实际上有一个制表符分隔的文件开始;所以,如果你发现你真的不想要上面的所有逗号,我们可以在 BEGIN 部​​分指定输入分隔符:

awk 'BEGIN { FS="\t"; OFS="," } $1 ~ /^Counter|^If(In|Out)Errors/ { $1=$1; print > FILENAME ".csv" }' file.log

这会产生更像您在示例输出中的内容:

Counter Name,port 1/1/x33,port 1/1/x34,port 1/1/x35,port 1/1/x36
IfInErrors:,0,0,0,0
IfOutErrors:,0,0,0,0
Counter Name,port 1/1/x37,port 1/1/x38,port 1/1/x39,port 1/1/x40
IfInErrors:,**95**,0,0,0
IfOutErrors:,0,0,0,0

如果您没有简单的制表符分隔并且想要更多地去除分隔符,可以为FS 使用更复杂的正则表达式。

对于您需要的其余部分,通过...向我们展示一个输出示例将有助于澄清,特别是因为您的问题当前要求 0 的总和的值...这是.. . 嗯... 0. 一个有用的提示是使用带有几个计数器的 for 循环....类似于以下子句:

$1 ~ /^If(In|Out)Errors/ {
        for (i=2; i<=NF; i++) {
                gsub(/\*/, "", $i)
                if ($i == 0) {
                        z++
                } else {
                        nz++
                        s += $i
                }
        }
}

并在 END { } 子句中执行一些操作并更改 FILENAME,例如:

END || filename != FILENAME {
        if (z || nz || s) {
                print filename ": " z, nz, s
                z=nz=s=0
        }
        filename=FILENAME
}

【讨论】:

  • 哇,比我的干净多了!谢谢迈克尔。
猜你喜欢
  • 1970-01-01
  • 2022-07-15
  • 2015-05-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-12-12
  • 2017-03-11
相关资源
最近更新 更多