【问题标题】:bash script to grab screen output and parse to csv用于抓取屏幕输出并解析为 csv 的 bash 脚本
【发布时间】:2016-05-19 08:28:25
【问题描述】:

当我运行一个名为 chk 的自定义脚本并将其输出到 csv 时,我正在编写一个 bash 脚本来获取屏幕输出。

chk 命令的屏幕输出示例

type:   ISDSL ACCESS ADSL
circt:  219317638
speed:  4096
rroutr: Billion 7404 (IS)
intr:   196.214.12.124/30
vrf:     PCTPT

我的命令 $line 可以是任何 linetag

chk $line | egrep 'type|circt|speed|rroutr|intr|vrf' | awk'{if(NR==1){print "  Circuit,Speed,CE,,WAN IP,VRF";}
else{print $2 $3} ORS=","}'

这个的输出是

Circuit,Speed,CE,,WAN IP,VRF
219317638,4096,Billion,196.214.12.124/30,PCTPT

在此之后,我想向脚本输入一个项目列表($line),我想在其上运行自定义脚本并将每个屏幕输出解析到 csv 文件中。

# This script takes in a file of many $line's and runs the chk command on each line usage  ./parsechk2csv <filename>
#!/bin/bash
cat $1|while read line; do
echo "$line\n";
chk $line | egrep 'type|circt|speed|rroutr|intr|vrf' | awk'{if(NR==1) {print "";}else{print $2 $3} ORS=","}' >> test.csv
done

它或多或少地工作,但有两件事我遇到了困难。

  1. 如何在我的最终脚本中包含 csv 文件标题,而不会在 shell 脚本中循环一直重写标题(您会注意到我在当前脚本 awk 命令中留下了标题)。使用 NR==1 还会覆盖我需要的屏幕输出中的第一行

  2. 如何使用命令 args 来指定输出 csv 文件的名称。我试图重定向到 >>$3.csv 但这不起作用。

【问题讨论】:

  • 数据行中的条目小于标题行...这似乎是错误的。即使缺少其中一列,您也必须插入一个占位符列。类似于 219317638,4096,,pep-ctn3-dsl/e0,Billion,196.214.12.124/30, 的通知 2 ,4096 之后。
  • 感谢您的反馈。我忘了提到我想将 $line 变量添加为 csv 文件中第一个字段的数据
  • 不管您的评论如何,您的标题行 LineTag,Circuit,Type,PE,Speed,WAN IP,VRF 中仍然有 7 个字段,而 awk 输出中只有 5 个字段:@ 987654329@。 (或者当我们将最后一个逗号作为第 6 个(空)字段的字段分隔符时为 6),并且您的输入中只有 6 行。问题是不一致的。例如speed 位于标题的第 5 列和数据行的第 2 列 (4096)。所以,编辑问题,更正它,并添加示例输出你真正想要什么。

标签: linux bash shell awk


【解决方案1】:

不清楚输入文件的哪些行映射到输出中的哪些字段,因为您显示的值似乎对标题行中的名称没有意义,但这是您想要做的事情:

$ cat file
first linetag
type:   ISDSL ACCESS ADSL
circt:  219317638
speed:  4096
routr:  ctn3-dsl/e0
rroutr: Billion 7404 (IS)
intr:   196.214.12.124/30
vrf:    first idk

second linetag
type:   Next fake ACCESS
circt:  123456
speed:  2020
routr:  foo-hspd/e1
rroutr: dozens 6564 (IS)
intr:   100.200.30.304/27
vrf:    second idk

.

$ cat tst.sh
#infile="$1"
#outfile="${2:-test.csv}"
#<"$infile" xargs -d'\n' -n1 -Iline sh -c 'echo "line"; chk "line"' |
cat file |
awk -v RS= -F'\n' -v OFS="," '
BEGIN {
    split("LineTag,Router,Type,Circuit,Speed,PE,WANIP,VRF",names,/,/)
    split("linetag,routr,type,circt,speed,rroutr,intr,vrf",abbrs,/,/)
}
NR==1 {
    for (i=1; i in names; i++) {
        printf "%s%s", (i>1?OFS:""), names[i]
    }
    print ""
}
{
    delete abbr2value
    abbr2value[abbrs[1]] = $1
    for (i=2; i<=NF; i++) {
        abbr = value = $i
        sub(/:.*/,"",abbr)
        sub(/[^:]+:[[:space:]]*/,"",value)
        abbr2value[abbr] = value
    }
    for (i=1; i in abbrs; i++) {
        printf "%s%s", (i>1?OFS:""), abbr2value[abbrs[i]]
    }
    print ""
}'
#}' >> "$outfile"

.

$ ./tst.sh
LineTag,Router,Type,Circuit,Speed,PE,WANIP,VRF
first linetag,ctn3-dsl/e0,ISDSL ACCESS ADSL,219317638,4096,Billion 7404 (IS),196.214.12.124/30,first idk
second linetag,foo-hspd/e1,Next fake ACCESS,123456,2020,dozens 6564 (IS),100.200.30.304/27,second idk

只需将cat file(只是用于模拟两次运行chk 的输出以演示awk 脚本的工作)替换为脚本开头当前注释掉的行,然后替换最后的 }' 与当前注释掉的 }' &gt;&gt; "$outfile" 并根据您认为合适的方式更改 split() 命令中的字段顺序。

请注意,以上与您今天的显着差异是:

  1. 您不再有显式的 shell 循环,而是使用 xargs 来遍历输入文件内容(请参阅 why-is-using-a-shell-loop-to-process-text-considered-bad-practice 了解为什么这很重要),并且
  2. 您现在对所有 chk 调用的输出运行一次 awk,而不是为每个单独的 chk 调用运行一次 awk。

【讨论】:

  • 感谢埃德。非常感激。为令人困惑的标题道歉。如果我想将 $line 添加为 csv 中的第一项,然后添加屏幕输出中的其他字段。基本上 $line 将是 linetag 的值,我将添加另一个标题字段以匹配我从屏幕输出中获取的内容
  • 好的,我更新了我的答案,但如果您只是编辑您的问题以显示简洁、可测试的示例输入以及在给定该输入的情况下您想要的输出,这确实会有所帮助。
  • 谢谢埃德。我肯定会改进我今后提出的意见。我已经尝试过新脚本,但我的输出 csv 文件中似乎只有一行。在前面的脚本中,它为输入文件中的所有行生成了 csv。我会进一步调查,看看是什么原因造成的
  • awk 脚本将起作用,因此您只需调查xargs...chk。试试&lt;"$infile" xargs -d'\n' -n1 -Iline sh -c 'echo "line"; chk "line"' | awk '{print NR, NF, "&lt;" $0 "&gt;"}' 看看xargs...chk 是否产生了你认为应该的输出。
猜你喜欢
  • 1970-01-01
  • 2019-03-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-04-28
  • 1970-01-01
  • 2014-05-22
相关资源
最近更新 更多