我的输入如下
<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或其他任何方式将我的搜索条件存储在数组中?
如果您能提供解决方案,请提供每个选项的详细信息;如果你可以的话。
谢谢
答案 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
通过段落将输入拆分为记录,其中段落是一系列非空行。
-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
。
xmllint
预安装 :
sudo apt-get install libxml2-utils
xmllint
支持XPath 1.0次查询,但几乎无法控制输出格式。 按需安装替代方案 - 优于xmllint
:
<强> xmlstarlet
强>
xmlstarlet
功能强大且灵活,支持广泛的操作。
macOS :使用brew install xmlstarlet
sudo apt-get install xmlstarlet
<强> 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