非常好,感谢你的进一步澄清!
你要的效果是:提取 setIntlkAlarm、createActionAlarm、acceptUnknown 节点的内容,并获取距离它最近的上一级父节点(即它被包裹的直接父节点),而不是随意更上层的祖先节点。
⸻
✅ 正确脚本(仅 Bash + awk + sed,不依赖第三方工具)
这个版本专门修复了“上一级父节点不准确”的问题,用一个栈来追踪 XML 层级结构,始终记录当前的“直接父标签”。
#!/bin/bash
目标标签名
TAGS=("setIntlkAlarm" "createActionAlarm" "acceptUnknown")
for xmlfile in *.xml; do
[[ ! -f "$xmlfile" ]] && continue
base="${xmlfile%.xml}"
outfile="${base}.rc"
> "$outfile"
id=1
sed -e 's/></>\n</g' "$xmlfile" | awk -v out="$outfile" -v tags="${TAGS[*]}" -v id_start=1 '
BEGIN {
split(tags, taglist, " ")
for (i in taglist) tagmap[taglist[i]] = 1
id = id_start
depth = 0
}
function trim(str) {
gsub(/^[ \t\r\n]+|[ \t\r\n]+$/, "", str)
return str
}
{
line = $0
gsub(/^[ \t\r\n]+/, "", line)
gsub(/[ \t\r\n]+$/, "", line)
# 匹配开标签且非自闭合
if (match(line, /^<([a-zA-Z0-9_]+)[^>]*>$/, m) && line !~ /\/>/ && line !~ /^<\/.*/) {
stack[++depth] = m[1]
}
# 匹配目标标签行并提取内容
for (tag in tagmap) {
pattern = "<" tag "[^>]*>([^<]*)</" tag ">"
if (match(line, pattern, m)) {
content = m[1]
n = split(content, arr, ",")
str1 = trim(arr[1])
str2 = trim(arr[2])
str3 = ""
for (i = 3; i <= n; i++) {
if (i > 3) str3 = str3 ","
str3 = str3 trim(arr[i])
}
# 获取直接父节点
parent = (depth > 0) ? stack[depth] : ""
# 输出格式
printf("KEY:%s%s\n", parent, str1) >> out
printf("MSG:%s\n", str3) >> out
printf("DES:%s\n", str3) >> out
printf("ALARMID:%d\n", id) >> out
printf("TYPE:%s\n\n", str2) >> out
id++
}
}
# 匹配闭标签
if (match(line, /^<\/([a-zA-Z0-9_]+)>$/, m)) {
if (depth > 0 && stack[depth] == m[1]) {
delete stack[depth]
depth--
}
}
}'
echo "✅ 已生成: $outfile"
done
⸻
✅ 示例输出格式(每一条报警):
KEY:NodeNameABC123
MSG:Some meaningful message
DES:Some meaningful message
ALARMID:1
TYPE:2
⸻
✅ 使用方法:
1. 保存为 extract_alarm.sh
2. 运行:
chmod +x extract_alarm.sh
./extract_alarm.sh
⸻
🔧 说明:
• 直接父节点是用 XML 栈维护的:每次进入一个标签就 push,遇到 就 pop,所以能准确取最近上一级。
• 不依赖 xmlstarlet、xmllint 等工具,适合你要求的纯 Bash + awk + sed 环境。
• 自动处理所有当前目录下的 .xml 文件,生成同名 .rc 文件。
⸻
如果你愿意,我也可以直接用你上传的 Interlock_LB.xml 文件跑这个脚本并给你生成 .rc 文件作为验证演示,是否需要我来跑一遍并提供下载?