结合awk命令输出的倍数搜索显示在一行

时间:2017-05-27 06:08:56

标签: arrays linux bash awk sed

我的输入如下

<connection name="test1" transport="tcp">
<LPort>host1:11111</hostPort>
<hostPort>host1:11111</hostPort>
<abcd> 1234

<connection name="test2" transport="tcp">
<hostPort>host2:22222</hostPort>
<GPort>host1:12111</hostPort>

<connection name="xyz1" transport="tcp">
<hostPort>host3:33333</hostPort>
<FPort>host1:12113</hostPort>
<efgi> 5678

<connection name="xyz2" transport="tcp">
<LPort>host1:12234</hostPort>
<hostPort>host4:4444</hostPort>

我希望我的出局如下:

test1  host1 1111
test2  host2 2222
xyz1   host3 3333
xyz2   host4 4444

为了解决这个问题,这就是我的工作。但在我看来,必须有一种更好更简单的方法,我没有包含整个逻辑(数组);但是当我有多个时,我一直在使用这种方法 在文件中搜索,它的工作原理。我尝试使用&amp;&amp;组合awk命令命令,它失败了。

以下是我的代码的一部分&amp;逻辑 1)我抓住了文件 2)摆脱额外的角色并用空格替换使用sed 3)我获取我想要的值并使用awk分配给数组值      请注意我没有包含其余的逻辑(但它有效)      简而言之,我做一个while循环然后将值分配给2或3个数组并在同一行上打印它们以获得所需的输出

cat file  | grep -A5 connection  | sed s'/[:="><]/ /g' | awk '/name/ {print $3}'
cat file | grep -A5 connection  | sed s'/[:="><]/ /g' | awk '/hostPort/ {print $2 " " $3}'

如果可能,请提供替代解决方案,不涉及使用sed / awk或其他任何方式将我的搜索条件存储在数组中?

如果您能提供解决方案,请提供每个选项的详细信息;如果你可以的话。

谢谢

3 个答案:

答案 0 :(得分:0)

使用单个 sed 方法:

sed -n '/<connection/{N;N; s/<connection name="\([^"]*\)".*<hostPort>\([^:]*\):\([^<]*\).*/\1 \2 \3/p}' file

输出:

test1 host1 11111
test2 host2 22222
xyz1 host3 33333
xyz2 host4 4444
  • N;N; - 将下两行添加到模式空间(包括换行符)

  • connection name="\([^"]*\) - 捕获连接名称

  • <hostPort>\([^:]*\):\([^<]*\) - 捕获主机名端口号

答案 1 :(得分:0)

强制性提醒:假设您的输入是格式良好的XML,使用XML解析器将提供更强大的解决方案(见底部)。

这是一个单一实用程序awk解决方案:

awk -v RS= -F '<connection name="|<hostPort>' '
  {
    sub(/".*/, "", $2)
    split($3, tokens, /[:<]/)
    printf "%-6s %s %s\n", $2, tokens[1], tokens[2]
  }
' file
  • -v RS=告诉awk通过段落将输入拆分为记录,其中段落是一系列非空行。

    < / LI>
  • -F '<connection name="|<hostPort>'按照<connection name="或(|<hostPort>的出现将每个段落拆分为字段,以便感兴趣的数据位于开始第二和第三个字段($2$3)。

  • sub(/".*/, "", $2)会从第2个字段中删除第一个"后面的所有内容,实际上只留下连接名称。

  • split($3, tokens, /[:<]/)通过出现:<将第3个字段拆分为令牌数组,从而在第1个数组元素中产生主机名,在第2个数组元素中产生端口

  • printf "%-6s %s %s\n", $2, tokens[1], tokens[2]打印输出行,将连接名称右侧填充至少6个带空格的字符,如示例输出中所示;如果您只想要一个空格来分隔输出字段,只需省略-6

可选读物:可在shell脚本中使用的XML解析实用程序(CLI)

    某些平台上
  • xmllint 预安装

    • macOS / FreeBSD / PC-BSD (可能还有其他BSD变体)
    • 部分 Linux 发行版: Fedora CentOS
    • 在其他人身上,可能有包裹;例如,在Ubuntu上:
      sudo apt-get install libxml2-utils
    • 警告:虽然xmllint支持XPath 1.0次查询,但几乎无法控制输出格式
  • 按需安装替代方案 - 优于xmllint

    • <强> xmlstarlet

      • xmlstarlet功能强大且灵活,支持广泛的操作。

      • macOS :使用brew install xmlstarlet

      • 通过Homebrew安装
      • Linux :很可能它可以与您平台的软件包管理器一起安装;例如,在基于Debian的发行版上,例如 Ubuntu
        sudo apt-get install xmlstarlet
      • Windows :从sourceforge手动下载并安装。
    • <强> xidel

      • xidel需要手动download and installation,但其功能和灵活性弥补了这一不便。

      • 支持 Linux macOS Windows

以下解决方案与上面列出的3个实用程序形成鲜明对比

假设以下格式良好的XML文档包含在file中 - 请注意<connection>元素现在如何包含在单个顶级<doc>元素中:

<doc>
  <connection name="test1" transport="tcp">
    <LPort>host1:11111</LPort>
    <hostPort>host1:11111</hostPort>
    <abcd>1234</abcd>
  </connection>

  <connection name="test2" transport="tcp">
    <hostPort>host2:22222</hostPort>
    <GPort>host1:12111</GPort>
  </connection>

  <connection name="xyz1" transport="tcp">
    <hostPort>host3:33333</hostPort>
    <FPort>host1:12113</FPort>
    <efgi>5678</efgi>
  </connection>

  <connection name="xyz2" transport="tcp">
    <LPort>host1:12234</LPort>
    <hostPort>host4:4444</hostPort>
  </connection>
</doc>

xmllint解决方案:

xmllint对查询结果的格式化缺乏控制需要一个非常重要的awk帮助程序命令:

echo 'cat //connection/@name | //hostPort/text()' | xmllint --shell file | awk -F\" '
  NR % 2 { next }                  # skip separator lines
  NR % 4 == 2 { conn = $2; next }  # save connnection name
  { 
    split($0, tokens, ":")
    printf "%-6s %s %s\n", conn, tokens[1], tokens[2] 
  }
'

xmlstarlet解决方案:

xmlstarlet的{​​{1}}子命令通过在后台将选项转换为XLST模板来支持非常灵活的提取:

sel

xmlstarlet sel -t -m '//connection' -v 'str:align(@name, " ")' \ -o ' ' \ -c 'str:replace(hostPort, ":", " ")' -n file 解决方案:

xidel非常灵活,不仅支持XML,还支持HTML和JSON。

虽然它不支持XLST,但它支持XQuery,XPath的超集,具有类似XSLT的功能,可实现强大的转换。提示Reino
据我所知,没有填充功能,但是,使用了一个简单的 - 辅助xidel命令:

awk

也就是说,XQuery甚至支持用户定义的函数,因此您可以编写自己的填充函数:

xidel file -q --xquery \
  'for $c in //connection return concat($c/@name, " ", replace($c/hostPort, ":", " "))' |
    awk '{ printf "%-6s %s %s\n", $1, $2, $3 }'

答案 2 :(得分:0)

合并空白行分隔的块,并使用反向引用从每个块中提取所需的值:

sed '${/^$/!{H;s/.*//;};};/^$/!{H;d;};/^$/{x;s/^\n<connection name="\([^"]*\)".*<hostPort>\([^:]*\):\([^<]*\).*/\1 \2 \3/;};' file