【问题标题】:Parse input into blocks enclosed in curly braces with Sed使用 Sed 将输入解析为用花括号括起来的块
【发布时间】:2015-10-16 02:06:15
【问题描述】:

两个文件:

文件 1

{
foo
}
{
bar
}

文件 2

foo
}
{
bar
}

(唯一的区别是文件 1 以大括号开头)

以下表达式适用于文件 2,但不适用于文件 1,为什么? (以及如何解决?):

sed '1!N; s/}\n{/},\n{/' < file1 or file2

我不知道为什么在匹配前 2 行的大括号会导致这种行为。我想知道 sed 被绊倒的原因以及如何解决它。但除了 sed 之外,我还可以通过其他方式在 }\n{ 之间插入 ,

【问题讨论】:

  • 您还没有告诉我们脚本“工作”意味着什么。请编辑您的问题以显示两个文件的预期输出。

标签: regex awk sed


【解决方案1】:

这是你想要的吗(使用 GNU awk 进行多字符 RS):

$ awk -v RS='}\n{' '{ORS=(RT?"},\n{":"")} 1' file1
{
foo
},
{
bar
}

$ awk -v RS='}\n{' '{ORS=(RT?"},\n{":"")} 1' file2
foo
},
{
bar
}

【讨论】:

  • 干得好;或许可以帮助其他人理解:GNU Awk 的(非标准)RT 变量反映了作为手头记录的终止符(分隔符)遇到的RS 的实际匹配。使用 OP 的输入,RT 对于 last 记录将是 empty,这允许将 ORS 动态设置为最后一条记录的空字符串,以便 @ 987654326@ 序列实际上只插入 个记录之间。
【解决方案2】:

这不是左大括号,而是额外的行。将 anything 放在该行(甚至什么都没有),您的脚本就会停止工作。

您的脚本严格是成对的。仅当结束 } 位于 even 行时才有效。

您的 Sed 脚本显示 1!N - 对于不是第一行的每一行,将 next 行读入模式空间,然后尝试 s/// 转换。

然后脚本结束,Sed 打印模式空间,丢弃它,然后读取下一行。您只能同时配对两条线路,从第二条线路开始。

一个文件

}
{

也不会被正确修改,但是

f
}
{

会的。

这个 Sed 命令

sed '/}$/{N;s/}\n{/},\n{/}'

适用于不包含 }\n}\n{ 的文件(它会错过这些)。

对于更强大的东西,你需要像 awk 这样的东西,或者比我更擅长 sed 的人来想出一个更智能的脚本。

【讨论】:

    【解决方案3】:

    这可能对你有用(GNU sed):

     sed '1b;$!N; s/}\n{/},\n{/;P;D' file
    

    目前,您正在处理第一行之后的行对,您想要的是使用两行窗口遍历文件。 这会将两行窗口一次再增加一行到文件末尾,即它删除第一行但保留第二行,然后将下一行附加到该行。要查看它是如何工作的(并且通常对所有 sed 脚本有用),请使用 l 命令:

    sed '1b;$!N;l;s/}\n{/},\n{/;P;D' file
    

    【讨论】:

    • 滑动窗口技术很棒。这是一个使用 l 的更简单的 GNU Sed 命令,它帮助我理解了它:sed '$!N; l; D' &lt;&lt;&lt;$'line 1\nline 2\nline 3\nline 4\nline 5'(遗憾的是,只有 GNU Sed 将模式空间内部换行符可视化为 \n - 它对 BSD Sed 的帮助较小)。我建议一般使用$!N,以确保最后一行也被完全处理(GNU Sed,当N在最后一行被调​​用时,仍然会打印它,但会跳过脚本中剩余的命令;BSD Sed,根据 POSIX,甚至不会打印它)。
    • 这是一个(调整过的)命令版本,它也适用于 BSD/OSX Sed(假设是 Bash、Ksh 或 Zsh):sed $'$!N; s/}\\n{/},\\\n{/; P; D' file
    • @mklement0 已修改以适应最后一行处理
    【解决方案4】:

    Etan Reisner's answer 很好地解释了您的sed 命令的问题。

    通常,awk 可以更轻松地处理跨行的解析。

    假设 GNU awkmawk,请尝试以下操作:

    awk -v RS='\n}\n{' '/\}\n$/ { printf "%s", $0; next; } { printf "%s\n},\n{", $0 }' file
    

    在顶级序列中嵌套{...} 序列是可以的,只要它们的结束}缩进

    • RS='\n}\n{' 表示输入被\n}\n{ 序列分成记录 - 跨行。
      • 注意:多字符RS 值是POSIX standard扩展,GNU Awk 和 Mawk 恰好实现了它; BSD Awk,也用于 OS X,
    • /\}\n$/ 只匹配 last 记录,因为它后面没有另一个 {
      • printf "%s", $0 只是按原样打印;它的开头{(如果有)打印在先前记录的上下文中(如果有)。
    • 否则 - 第一条或任何内部记录 - 打印时在其结束 } 和下一条记录的开始 { 之间插入 ,{ printf "%s\n},\n{", $0 }

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2010-12-13
      • 1970-01-01
      • 2016-11-25
      • 1970-01-01
      • 2015-01-14
      • 1970-01-01
      • 2012-01-18
      相关资源
      最近更新 更多