如何使用 awk 将 mixed/partly 缺失的记录提取到定义的顺序
How to extract mixed/partly absent records to a defined order with awk
我有以下数据(它还包含其他行,这里是一个有意义的摘录):
group
bb 1
cc 1
dd 1
end
group
dd 2
bb 2
end
group
aa 3
end
我不知道值(如“1”、“2”等),必须通过名称(通用“group”、“aa”等)进行匹配
我想按以下顺序过滤和排序数据(当字符串不存在时使用空选项卡):
group bb 1 cc 1 dd 1
group bb 2 dd 2
group aa 3
我运行:
awk 'BEGIN {ORS = "\t"}\
/^group/ {print "\n" [=13=]}; \
/^aa/ {AA = [=13=]}; \
/^bb/ {BB = [=13=]}; \
/^cc/ {CC = [=13=]}; \
/^dd/ {DD = [=13=]}; \
/^end/ {print AA; print BB; print CC; print DD}' test.txt
并得到
group bb 1 cc 1 dd 1
group bb 2 **cc 1** dd 2
group aa 3 **bb 2** **cc 1** **dd 2**
顺序正确,但数据错误(标有星号)。执行此过滤的正确方法是什么?
谢谢!
假设:
- 输入行不以任何白色开头space
- 每个
^group
都有匹配的 ^end
- 文件的第一行是
^group
- 文件的最后一行是
^end
^end
和下一个 ^group
之间没有行(忽略)
主要问题是每次看到 group
时,我们都需要 clear/reset 其他变量,否则我们会沿用之前 group
.
的值
其他(次要)问题:
ORS
对比 OFS
- 多个
print
命令与单个print
命令
- 不需要续行符(
\
)
更新 awk
脚本的一个想法:
awk '
BEGIN { OFS="\t" }
/^group/ { AA=BB=CC=DD="" ; next }
/^aa/ { AA=[=10=] ; next }
/^bb/ { BB=[=10=] ; next }
/^cc/ { CC=[=10=] ; next }
/^dd/ { DD=[=10=] ; next }
/^end/ { print "group",AA,BB,CC,DD }
' test.txt
注意: ; next
子句是可选的,作为视觉提醒我们不需要担心脚本的其余部分(对于当前行)
这会生成:
group bb 1 cc 1 dd 1
group bb 2 dd 2
group aa 3
这里有一个更简单的 awk
解决方案来做同样的事情:
awk '/^group$/{delete m; next} {m[]=[=10=]} /^end$/{
printf "group\t%s\t%s\t%s\t%s\n", m["aa"], m["bb"], m["cc"], m["dd"]
}' file
group bb 1 cc 1 dd 1
group bb 2 dd 2
group aa 3
使用 GNU awk
尝试以下代码。仅使用显示的示例编写和测试。简单的解释是,将 RS 设置为 end\n(可选),然后简单地用空格替换新行并打印该行。
awk -v RS='end\n?' 'RT{gsub(/\n/,OFS);print}' Input_file
OR 如果您想将输出作为制表符分隔,请尝试以下操作:
awk -v RS='end\n?' -v OFS="\t" 'RT{gsub(/\n/,OFS);print}' Input_file
我有以下数据(它还包含其他行,这里是一个有意义的摘录):
group
bb 1
cc 1
dd 1
end
group
dd 2
bb 2
end
group
aa 3
end
我不知道值(如“1”、“2”等),必须通过名称(通用“group”、“aa”等)进行匹配 我想按以下顺序过滤和排序数据(当字符串不存在时使用空选项卡):
group bb 1 cc 1 dd 1
group bb 2 dd 2
group aa 3
我运行:
awk 'BEGIN {ORS = "\t"}\
/^group/ {print "\n" [=13=]}; \
/^aa/ {AA = [=13=]}; \
/^bb/ {BB = [=13=]}; \
/^cc/ {CC = [=13=]}; \
/^dd/ {DD = [=13=]}; \
/^end/ {print AA; print BB; print CC; print DD}' test.txt
并得到
group bb 1 cc 1 dd 1
group bb 2 **cc 1** dd 2
group aa 3 **bb 2** **cc 1** **dd 2**
顺序正确,但数据错误(标有星号)。执行此过滤的正确方法是什么? 谢谢!
假设:
- 输入行不以任何白色开头space
- 每个
^group
都有匹配的^end
- 文件的第一行是
^group
- 文件的最后一行是
^end
^end
和下一个^group
之间没有行(忽略)
主要问题是每次看到 group
时,我们都需要 clear/reset 其他变量,否则我们会沿用之前 group
.
其他(次要)问题:
ORS
对比OFS
- 多个
print
命令与单个print
命令 - 不需要续行符(
\
)
更新 awk
脚本的一个想法:
awk '
BEGIN { OFS="\t" }
/^group/ { AA=BB=CC=DD="" ; next }
/^aa/ { AA=[=10=] ; next }
/^bb/ { BB=[=10=] ; next }
/^cc/ { CC=[=10=] ; next }
/^dd/ { DD=[=10=] ; next }
/^end/ { print "group",AA,BB,CC,DD }
' test.txt
注意: ; next
子句是可选的,作为视觉提醒我们不需要担心脚本的其余部分(对于当前行)
这会生成:
group bb 1 cc 1 dd 1
group bb 2 dd 2
group aa 3
这里有一个更简单的 awk
解决方案来做同样的事情:
awk '/^group$/{delete m; next} {m[]=[=10=]} /^end$/{
printf "group\t%s\t%s\t%s\t%s\n", m["aa"], m["bb"], m["cc"], m["dd"]
}' file
group bb 1 cc 1 dd 1
group bb 2 dd 2
group aa 3
使用 GNU awk
尝试以下代码。仅使用显示的示例编写和测试。简单的解释是,将 RS 设置为 end\n(可选),然后简单地用空格替换新行并打印该行。
awk -v RS='end\n?' 'RT{gsub(/\n/,OFS);print}' Input_file
OR 如果您想将输出作为制表符分隔,请尝试以下操作:
awk -v RS='end\n?' -v OFS="\t" 'RT{gsub(/\n/,OFS);print}' Input_file