我尝试创建一个可以节省时间的脚本。我想做的是:我有一个包含多个部分的大型 .xml 文件,假设它看起来像这样:
some text
.....
HEAD
context A
TAIL
some text
.....
HEAD
context B
TAIL
....
some text
....
HEAD
context C
TAIL
....
some text
我需要剪切部分并context A
移至文件 contextA,然后context B
移至文件 contextB,依此类推。(所有文件都应包含语法HEAD context x TAIL
)问题是所有上下文都以相同的正则表达式(HEAD 和 TAIL)开始和结束。我可以将所有部分剪切到一个文件中,但这还不够。
你能帮助我吗?
这是一个小的更新,因为也许我没有足够清楚地说明:假设我的文件如下所示:
some text 1
<config>
1
2
3
</config>
some text 2
<config>
4
5
6
</config>
some text 3
<config>
7
8
9
</config>
some text 4
我想要文件名为:
“first” 包含:
<config>
1
2
3
</config>
“second” 包含:
<config>
4
5
6
</config>
等等。
答案1
使用 awk 来做这件事更容易:
awk -v RS="HEAD\n" -v FS="\n" 'NR>1{print "HEAD\n" $0 > $1".txt"}' ex
笔记:
RS="HEAD\n"
寄存器以“HEAD\n”分隔FS="\n"
每个字段为一行NR>1{print "HEAD\n" $0 > $1}
对于除第一个之外的所有寄存器,将其写入名为字段 1——“$1”的文件中
更新:对于新问题:
awk -v RS="<config>\n" -F"</config>" 'NR>1{print RS $1 FS > "conf-"NR-1}' ex
配置输出存储在名为“conf-1”的文件中
答案2
如果你真的不能使用合适的 XML 解析器那么我awk
建议
awk '/^HEAD/ {p=1; ++n} p {print > "context"n} /^TAIL/ {p=0}' file.xml
将把HEAD
...TAIL
部分输出为按数字递增的文件名context1
,context2
等等。
为了更容易排序,您可能需要通过构造固定宽度的数字前缀来稍微改进它,例如
$ awk '/^HEAD/ {p=1; outfile = sprintf("context%03d", ++n)} p {print > outfile} /^TAIL/ {p=0}' file.xml
$ head context*
==> context001 <==
HEAD
context A
TAIL
==> context002 <==
HEAD
context B
TAIL
==> context003 <==
HEAD
context C
TAIL
答案3
我的脚本使用awk
:
#!/bin/bash
for i in $(seq -w $(<"$1" grep -cx "$2")); do
<"$1" >$i awk -va=$i -vb="$2" -vc="$3" '$0~b{d++;e=1}d==a&&e==1;$0~c{e=0}'
done
将其另存为例如myscript.sh
,使其可执行,导航到您的onebig.xml
并像这样调用它:
/path/to/myscript.sh onebig.xml HEAD TAIL
它会剪切出从onebig.xml
开始HEAD
到结束的每个部分,如果有少于 10 个部分,TAIL
则将其保存为1
、、...;如果有 10 到 99 个部分,则将其保存为、 、...;如果有 100 到 999 个部分,则将其保存为、、...;以此类推。2
01
02
001
002
简短说明
<"$1" grep -cx "$2"
HEAD
– 计算中的出现次数onebig.xml
,假设3
for i in $(seq -w 3); do …; done
– 循环遍历从 1 到 的每一个出现次数3
,如果需要,选项会添加尾随seq
零-w
<"$1" >$i
– 读取onebig.xml
和写入与当前计数同名的文件awk -va=$i -vb="$2" -vc="$3"
– 启动awk
并分配三个变量,a
分别是 count、b
beingHEAD
和c
beingTAIL
$0~b{d++;e=1}
b
– 如果当前行包含(=HEAD
)的内容,则增加d
一并设置e=1
d==a&&e==1
– 如果d
等于a
(= 当前计数)并且e
等于1
,则打印当前行(print
是隐含的操作;本质上是:如果它在a
第 th 次出现之后HEAD
,并且我们位于 和 之间HEAD
,TAIL
则打印)$0~c{e=0}
c
– 如果当前行包含(= )的内容则TAIL
设置e=0
答案4
请检查以下脚本是否对您有帮助:
#!/bin/bash
for x in {A..Z}; do
# check if the pattern exists in the file
if grep -qF "context $x" file.txt; then
# Store the lines between the 2 patterns including the matching lines in a text file
awk '/context '$x'/,/TAIL/' file.txt > context$x.txt
else
echo "Sorry this pattern does not exists in file"
fi
done