bash - sed:如果与模式匹配,则打印分隔的行 block

标签 bash awk sed

我想使用 sed 来匹配由 pattern1/pattern2 分隔的行 block ,然后仅对包含 pattern3 的 block 执行操作(例如打印 block ) >.

在下面的示例中,我在所有由匹配 {} 的行分隔的 block 中寻找“如果你能 catch 我” em>(然后我想完整地打印匹配 block )。

我尝试过的:

sed -n -e '/{/,/}/{1h;1!{$!{H;d};H;x;/catch me if you can/p}}'

(思路是匹配由{}分隔的 block ,然后将每个 block 累积到保持空间;在每个 block 的末尾,交换保持空间并为“如果可以的话 catch 我”)执行匹配。 这是行不通的,因为所有匹配的 block 一起被 sed 视为一个 block ,而不是单独处理每个 block 。

输入数据:

"block1": {
    "foo": "abcd",
    "bar": "catch me if you can",
    "aaa": "12345"
},
"block2": {
    "bbb": "24680",
    "bar": "blah",
    "foo": "argh",
    "ccc": "135"
},
"block3": {
    "ddd": "zzz"
},
"block4": {
    "foo": "xyz",
    "bar": "catch me if you can",
}

期望的输出:

"block1": {
    "foo": "abcd",
    "bar": "catch me if you can",
    "aaa": "12345"
},
"block4": {
    "foo": "xyz",
    "bar": "catch me if you can"
},

注意 1:每个 block 内字段的顺序是随机的。字段的数量和值的长度在 block 之间不是恒定的。我正在寻找的字段可能在某些 block 中丢失(而不是仅仅具有不同的值)。

注 2:出于教育目的,我更喜欢使用 sed 的解决方案,但如果那不可能,awk 或 bash 也可以。请不要使用 perl 或其他工具。

引用资料:

  1. Sed command summary
  2. Sed one liners

最佳答案

我就是这样做的。这里有两个版本,一个是BSD(Mac OS X)sed(也适用于其他不运行GNU sed的系统),一个是GNU sed.

BSD sed

$ cat script.bsd-sed
/{/,/}/{
    /{/{ h; b next
    }
    /}/{ H; x; /catch me if you can/p; b next
    }
    H
    :next
}
$ sed -n -f script.bsd-sed data
"block1": {
    "foo": "abcd",
    "bar": "catch me if you can",
    "aaa": "12345"
},
"block4": {
    "foo": "xyz",
    "bar": "catch me if you can",
}
$

逻辑是:

  • 不要打印任何东西,除非被告知要这样做 (-n)。
  • 在包含 {} 的行之间>
  • 如果该行匹配 {,则将模式复制到保留空间并跳转到标签 next
  • 如果该行匹配,将其添加到保留空间;切换模式并保持空间;如果模式空间(以前的保持空间)与您的其他模式匹配('如果可以的话 catch 我'),打印它;跳转到标签 next
  • 将行添加到保留空间。

BSD(经典)sedb next 之后的行中不需要任何内容​​,因此操作的 } 在下一行。

GNU sed

$ cat script.gnu-sed 
/{/,/}/{
    /{/{ h; b next }
    /}/{ H; x; /catch me if you can/p; b next }
    H
    :next
}
$ /opt/gnu/bin/sed -n -f script.gnu-sed data
"block1": {
    "foo": "abcd",
    "bar": "catch me if you can",
    "aaa": "12345"
},
"block4": {
    "foo": "xyz",
    "bar": "catch me if you can",
}
$

GNU sed 将标签后的分号或右大括号识别为终止命令,因此它允许更紧凑的符号。你甚至可以把它全部压平成一行——你必须添加几个分号:

$ /opt/gnu/bin/sed -n -e '/{/,/}/{ /{/{ h; b next }; /}/{ H; x; /catch me if you can/p; b next }; H; :next }' data
"block1": {
    "foo": "abcd",
    "bar": "catch me if you can",
    "aaa": "12345"
},
"block4": {
    "foo": "xyz",
    "bar": "catch me if you can",
}
$

您也可以删除不在模式匹配中的空格:

$ /opt/gnu/bin/sed -n -e '/{/,/}/{/{/{ h;b next};/}/{H;x;/catch me if you can/p;b next};H;:next}' data
"block1": {
    "foo": "abcd",
    "bar": "catch me if you can",
    "aaa": "12345"
},
"block4": {
    "foo": "xyz",
    "bar": "catch me if you can",
}
$

扩展数据文件data

"block1": {
    "foo": "abcd",
    "bar": "catch me if you can",
    "aaa": "12345"
},
"block2": {
    "bbb": "24680",
    "bar": "blah",
    "foo": "argh",
    "ccc": "135"
},
"block3": {
    "ddd": "zzz"
},
"block4": {
    "foo": "xyz",
    "bar": "catch me if you can",
}
"block5": [
    "oops": "catch me if you can"
],
"block6": {
    "rhubarb": "dandelion"
}

关于bash - sed:如果与模式匹配,则打印分隔的行 block ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37357323/

相关文章:

linux - 从文件中过滤具有给定前缀的特定行

python - 用于检查哪个串行端口已连接的 Bash 脚本

linux - 获取 "-("和 ")-"之间的字符串,linux 目录

regex - sed 在一个系统上工作但不在其他系统上工作

bash - 如何从基因列表中挑选多个fasta序列

Linux `mpstat` 无法按 crontab 脚本的预期工作

bash - 根据 shell 脚本中的列打印行

linux - 使用 shell 脚本在文件中获取具有相同字段的 1 行

bash - Unix 代码想要复制模板文件并在复制的文件中替换模板文件中的字符串

bash - 使用sed在指定位置替换大于指定数的数