【问题标题】:Removing new line between xml tags in unix shell script在 unix shell 脚本中删除 xml 标记之间的新行
【发布时间】:2019-06-11 07:39:37
【问题描述】:
<mstr>
  <srt>Payment towards File# 1234</srt>
  <msg>info for treat sxc
  Pay to shankar  A/C#999999
  bank of ooty</msg>
  <ins>info for party BB
   Pay to kumar A/C#999999
   state bank</ins>
</mstr>

我想像下面这样转换上面的 XML,这意味着数据包装在开始和结束标记之间的下一行。应该在一行中,如下所示。

 <mstr>
  <srt>Payment towards File# 1234</srt>
  <msg>info for treat sxc Pay to shankar  A/C#999999 bank of chen</msg>
  <ins>info for party BB Pay to kumar A/C#999999 state bank</ins>
</mstr>

我通过引用网络答案尝试了以下方式。想了解下面的 awk 命令是否还在寻找任何其他替代方法来解决我的问题?

 awk '{printf /^</&&!/^<\//?RS $0:$0}'

【问题讨论】:

  • 您找到的答案对您有用吗?如果没有,你能显示它给出的输出吗?
  • 不要使用面向行的工具来解析 XML。
  • 是的,它起作用了,我想了解 awk 是如何应用在这里的
  • 有人称它为summoning the daemon,其他人称它为the Call for Cthulhu,很少有人称它为turned mad and met the Pony。简而言之,永远不要使用正则表达式解析 XML 或 HTML!您是否尝试过诸如 xmlstarletxmllintxsltproc 之类的 XML 解析器?
  • xmllint 我用过这个,但没有帮助

标签: xml bash shell unix formatting


【解决方案1】:

我的建议是,尽管肯定有更好的选择,但在 PYX format 文件上使用 awk。 PYX 格式是从 SGML ESIS 格式派生的 XML 文档的面向行的表示。 (请参阅 ESIS - ISO 8879 元素结构信息集规范、ISO/IEC JTC1/SC18/WG8 N931 (ESIS))。

PYX 格式非常易于描述和理解。每行的第一个字符标识该行的内容类型。内容不直接跨越行,尽管连续的行可能包含相同的内容类型。在标签属性的情况下,属性名称和值只是用空格分隔,没有使用额外的引号。前缀字符是:

( start-tag
) end-tag
A attribute
- character data (content)
? processing instruction

所以我们可以要求,xmlstarlet 将 XML 转换为 PYX,使用 awk 删除行,并将其转换回 XML 文件:

$ xmlstarlet pyx file.xml | sed -E '/^-\\n/b;/^-/s/\\n +/ /g' | xmlstarlet p2x -

【讨论】:

  • 喜欢这个。我可能会建议在中间使用sed -E 's/\\n[[:space:]]+([^[:space:]])/ \1/g',但这并不是很大的改进。
  • +1 将 ESIS 带入讨论;我不敢,因为它太老了(Perl 时代),尽管它是将 SGML/XMLish 输入带入面向行的 Unix 工具的最通用技术。顺便说一句,您还可以使用 OpenSP SGML 处理包中的 onsgmls/nsgmls 程序从任何 SGML 或 XML 生成 ESIS
【解决方案2】:

这是对awk 脚本的解释。

awk '{printf /^</&&!/^<\//?RS $0:$0}'

希望屏幕图像清晰。

【讨论】:

  • 这不是代码插入,这是代码文档。
  • 请将代码和数据添加为文本 (using code formatting),而不是图像。图片:A)不允许我们复制粘贴代码/错误/数据进行测试; B) 不允许根据代码/错误/数据内容进行搜索;和many more reasons。一般来说,文本格式的代码/错误/数据>>>>作为图像的代码/错误/数据>>没有。除了代码格式的文本之外,只有在图像添加了一些重要的东西,而不仅仅是文本代码/错误/数据传达的内容时,才应该使用图像。上图可以替换。
【解决方案3】:

使用 XML 感知工具来处理 XML,使用 sedawk 可能很容易破坏数据。

例如,在xsh,我碰巧维护的一个工具,它实际上是XML::LibXML的一个包装器,你可以这样做:

open file.xml ;
for /mstr/* set . normalize-space(.) ;
save :b ;

输出几乎是预期的 - 它只是保留“ooty”而不是“chen”,但这可以通过添加轻松修复

set /mstr/msg xsh:subst(/mstr/msg, 'ooty', 'chen') ;

【讨论】:

  • 看起来很有趣。我注意到“XSH2 手册页”链接已损坏。
  • @glennjackman:谢谢,我会解决的。 Ticket
【解决方案4】:

您想要做的 - 删除文本开头和结尾的空格字符,并将任何空格和换行符序列折叠成文本中的单个空格字符 - 称为 空白规范化在 XML 中,并且可以通过许多开箱即用的 XML 处理工具来完成,而无需专门的 shell 脚本。例如,您可以使用tidy(可在http://tidy.sourceforge.net/ 获得,并且可能已经安装在您的机器上或通过sudo apt-get install tidy 在Debian/Ubuntu 上安装)如下对您的输入进行空白规范化(假设存储在@987654324 @):

tidy -xml -w 80 test.xml

您的 awk 单行打印任何输入行 ($0),省略换行符(使用 printf() 而不是 print()),并在前面添加 RS(记录分隔符,默认为制表符)如果该行以开始元素标记 (/&lt;/) 开头,但看起来不像结束元素标记 (!/^&lt;\//),则输出行。这两个正则表达式测试与&amp;&amp; 逻辑和运算符组合在一起,并且是条件表达式的一部分,? 字符之前的所有内容都是测试条件,? 之后的部分代表“if”和“else”分支,分别根据条件取取,用:隔开。对于您的输入,awk 程序将在一行上输出所有内容(不终止换行符),并在每个开始元素标记之前添加一个制表符。它将无法通过空格字符分隔连续的文本行,并且错误地将整个输入行作为第一个参数传递给printf(),这样输入文本中的百分比字符被解释为printf 格式字符串中的占位符,会打破你的输出。

帮自己一个忙,使用真正的 XML 或 SGML 工具来处理标记。

【讨论】:

    【解决方案5】:

    我会使用带有 XML 解析模块的脚本语言。例如,使用 ruby​​:

    ruby -r'rexml/document' -e '
        file = ARGV.shift
        doc = REXML::Document.new(File.new(file))
        doc.elements.each("/mstr/*") {|child| child.text = child.text.gsub(/\n\s*/, " ")}
        File.open(file, "w") {|f| f.puts(doc.to_s)}
    ' file.xml
    

    文件中的结果

    <mstr>
      <srt>Payment towards File# 1234</srt>
      <msg>info for treat sxc Pay to shankar  A/C#999999 bank of ooty</msg>
      <ins>info for party BB Pay to kumar A/C#999999 state bank</ins>
    </mstr>
    

    【讨论】:

      猜你喜欢
      • 2012-04-01
      • 2017-06-09
      • 2017-01-25
      • 1970-01-01
      • 2016-10-08
      • 1970-01-01
      • 2018-06-23
      • 1970-01-01
      • 2014-07-10
      相关资源
      最近更新 更多