SED 将特定部分复制到不同的文件

SED 将特定部分复制到不同的文件

我尝试创建一个可以节省时间的脚本。我想做的是:我有一个包含多个部分的大型 .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部分输出为按数字递增的文件名context1context2等等。

为了更容易排序,您可能需要通过构造固定宽度的数字前缀来稍微改进它,例如

$ 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 个部分,则将其保存为、、...;以此类推。20102001002

简短说明

  • <"$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、bbeingHEADcbeingTAIL
  • $0~b{d++;e=1}b– 如果当前行包含(= HEAD)的内容,则增加d一并设置e=1
  • d==a&&e==1– 如果d等于a(= 当前计数)并且e等于1,则打印当前行(print是隐含的操作;本质上是:如果它在a第 th 次出现之后HEAD,并且我们位于 和 之间HEADTAIL则打印)
  • $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

相关内容