【问题标题】:find and copy to new file using sed使用 sed 查找并复制到新文件
【发布时间】:2013-06-05 07:52:26
【问题描述】:

我在一个文件中有多行。每行都有一个共同的开始标签和结束标签。我想要 获取标签之间的内容并将其放入以 /r 分隔的新文件中。

1) 我尝试了以下 .. 但它复制了整行并放入新文件中

#!/bin/sh

startline="<Mytag>"
endline="<Nexttag>"

echo $startline
echo $endline

sed "/$startline/,/$endline/!d" input.txtt > test.txt

2) 理想情况下,结束标记应该是 &lt;/Mytag&gt;,但 sed 并没有很好地使用 '/'。如何克服这一点?我应该使用'//'吗?

谢谢


更新


input.txt 有以下几行

<?xml version="1.0" encoding="UTF-8" ?><InputRecord xmlns:xsi= "http://www.w3.org/2001/XMLSchema-instance" <tag1>blah</tag1><mytag>myinfo</mytag><tag2>blah</tag2></InputRecord>

<?xml version="1.0" encoding="UTF-8" ?><InputRecord xmlns:xsi= "http://www.w3.org/2001/XMLSchema-instance" <tag1>blah1</tag1><mytag>myinfo1</mytag><tag2>blah2</tag2></InputRecord>

预期输出

myinfo
myinfo1

【问题讨论】:

  • 请将您的示例粘贴到/输出中
  • 您更新的问题与您所说的问题 100% 不同。看来您正在寻找每行输入中给定开始标记和结束标记之间的文本,而不是包含开始标记的行和包含结束标记的另一行的全部内容。

标签: bash sed


【解决方案1】:

修改问题的答案

给定输入:

<?xml version="1.0" encoding="UTF-8" ?><InputRecord xmlns:xsi= "http://www.w3.org/2001/XMLSchema-instance" <tag1>blah</tag1><mytag>myinfo</mytag><tag2>blah</tag2></InputRecord>
<?xml version="1.0" encoding="UTF-8" ?><InputRecord xmlns:xsi= "http://www.w3.org/2001/XMLSchema-instance" <tag1>blah1</tag1><mytag>myinfo1</mytag><tag2>blah2</tag2></InputRecord>

输出应该是:

myinfo
myinfo1

暂时忽略用正则表达式解析XML通常是不明智的,这可以被视为在单行上查找开始标记和结束标记之间的文本的请求。这转化为:

starttag="<mytag>"
endtag="</mytag>"
sed -n "\%.*$starttag\(.*\)$endtag.*% s//\1/p"

POSIX sed 需要\% 表示法,以允许使用斜杠以外的其他内容作为正则表达式的分隔符。 POSIX sed 说:

... 上下文地址(由 BRE 组成,如 sed 中的正则表达式中所述,前后是分隔符,通常是 &lt;slash&gt;

和:

在上下文地址中,构造"\cBREc",其中c&lt;backslash&gt;&lt;newline&gt; 以外的任何字符,应与"/BRE/" 相同。如果c 指定的字符出现在&lt;backslash&gt; 之后,那么它应该被认为是那个字面字符,它不会终止BRE。比如上下文地址"\xabc\xdefx",第二个x代表自己,所以BRE就是"abcxdef"

原始版本问题的答案

如果您的 $endline 值正确,您的脚本应该可以正常工作。但是,IMNSHO,对打印范围持肯定态度会更简单:

sed -n "/$startline/,/$endline/p" input.txtt > test.txt

-n 的意思是“除非我告诉你,否则不要打印”,脚本方式“在匹配起始行的行和匹配结束行的行之间打印。

对于带有斜线的结束标签,你需要用反斜线转义斜线:

endline="<\/Nexttag>"

或者您可以使用. 代替斜线,理论上它可以匹配&lt;XNexttag&gt; 的开头,但可能不会。没有反斜杠可以解释为什么你得到了从开始行到文件结尾的所有内容。


论积极性的好处

考虑数据文件:

line1
line2 start1
line3
line4 end1
line5
line6 start2
line7
line8 end2
line9

并考虑 shell 和 sed 命令:

echo Positive Single
sed -n -e '/start1/,/end1/p'  data
echo Negative Single
sed    -e '/start1/,/end1/!d' data

echo Positive Double
sed -n -e '/start1/,/end1/p'  -e '/start2/,/end2/p'  data
echo Negative Double
sed    -e '/start1/,/end1/!d' -e '/start2/,/end2/!d' data

运行该脚本的输出是:

$ sh sed.scripts
Positive Single
line2 start1
line3
line4 end1
Negative Single
line2 start1
line3
line4 end1
Positive Double
line2 start1
line3
line4 end1
line6 start2
line7
line8 end2
Negative Double
$

对于要匹配单个模式范围的情况,!d 公式与 -n 加上 p 公式没有问题。

但是,“正双”模式工作正常,产生了我期望的答案,用于“打印 start1end1 之间的线以及之间的线start2end2',而“负双精度”模式不再正常工作。我宁愿使用可扩展的版本,而不是需求变化时必须重写的版本。

【讨论】:

  • 你的-n /../p和OP的/../!d有什么区别?
  • 数量不多,但我喜欢正面的而不是负面的。我认为“打印开始和结束标记之间的行”比“不打印开始和结束标记之间的行”更容易。我可能应该回过头来并指出,如果结束标记正确,原始脚本不会有太大问题,但示例中没有。
  • 嗨,这不适用于我的输入。它仍然打印输出中的每一行。我已经用示例输入和预期输出更新了我的问题。你能检查一下吗?谢谢!
【解决方案2】:

要转义斜杠,请在斜杠前面加上反斜杠,如下所示:

<\/Nexttag>

但您只需要这样做,因为您已选择 使用斜线作为分隔符。您可以使用您想要的 any 字符(通常选择斜杠,因为许多其他语言使用它来分隔正则表达式)。所以选择了一个不会出现在标签中的字符,比如哈希#:

sed "#$startline#,#$endline#!d" input.txtt > test.txt

【讨论】:

  • 从技术上讲,您只能选择除\ 之外的任何可打印作为分隔符
【解决方案3】:

这可能不是最佳解决方案,但它会为您的样本输入产生预期的输出:

#!/bin/sh

startline="<mytag>"
endline="<\/mytag>"

awk '{ gsub(">", "&\n"); gsub("<", "\n&"); print; }' | sed -e "/$startline/,/$endline/!d" -e "/$startline/d" -e "/$endline/d"

将您的示例输入重定向到此脚本,例如:

sh script.sh < sample.txt

中间的awk 只是在&gt; 之后和&lt; 之前放置一个换行符,因为sed 脚本只有在开始和结束标记单独位于它们自己的行时才有效。 (说实话,这真的不是一个好剧本。)

【讨论】:

  • 我已经用示例输入和输出更新了我的问题。您提供的样本不会产生任何输出
  • "..." 解释(相当有限的一组)\ 转义,您需要转义它或使用'。 (@user1164061 - 试试"&lt;\\/.."'&lt;\/...'
  • @user1164061 我更新了我的答案,使其适用于您在帖子中添加的示例输入
  • 这行得通。万分感谢!你能解释一下你给出的awk脚本吗?谢谢!
  • @user1164061 我附上了解释。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-02-20
  • 2014-02-10
  • 2021-12-24
  • 2020-05-01
  • 1970-01-01
  • 2013-07-24
  • 2018-07-28
相关资源
最近更新 更多