如何 grep/sed/awk 获取以空格字符开头的一系列输出

Dav*_* T. 3 regex bash awk grep sed

我有一个看起来像这样的文件:

# cat $file
...
ip access-list extended DOG-IN
 permit icmp 10.10.10.1 0.0.0.7 any
 permit tcp 10.11.10.1 0.0.0.7 eq www 443 10.12.10.0 0.0.0.63
 deny   ip any any log
ip access-list extended CAT-IN
 permit icmp 10.13.10.0 0.0.0.255 any
 permit ip 10.14.10.0 0.0.0.255 host 10.15.10.10
 permit tcp 10.16.10.0 0.0.0.255 host 10.17.10.10 eq smtp
...
Run Code Online (Sandbox Code Playgroud)

我希望能够按名称搜索(使用脚本)以获得独立访问列表的“部分”输出。我希望输出看起来像这样:

# grep -i dog $file | sed <options??>

ip access-list extended DOG-IN
 permit icmp 10.10.10.1 0.0.0.7 any
 permit tcp 10.11.10.1 0.0.0.7 eq www 443 10.12.10.0 0.0.0.63
 deny   ip any any log
Run Code Online (Sandbox Code Playgroud)

...不再输出不适用的非缩进行。

我已经尝试过以下方法:

grep -A 10 DOG $file | sed -n '/^[[:space:]]\{1\}/p'
Run Code Online (Sandbox Code Playgroud)

...这只给出了 DOG 之后以单个空格开头的 10 行(包括不适用于搜索的访问列表的行)。

sed -n '/DOG/,/^[[:space:]]\{1\}/p' $file
Run Code Online (Sandbox Code Playgroud)

...这给了我包含 DOG 的行,以及以单个空格开头的下一行。(需要访问列表的所有适用行...)

我想要包含 DOG 的行,以及 DOG 之后以单个空格开头的所有行,直到下一个未缩进的行。内容中有太多变量,无法依赖于除前导空格之外的任何模式(末尾并不总是否认,等等......)。

mkl*_*nt0 5

使用GNU sed (Linux):

name='dog'  # case-INsensitive name of section to extract
sed -n "/$name/I,/^[^[:space:]]/ { /$name/I {p;d}; /^[^[:space:]]/q; p }" file
Run Code Online (Sandbox Code Playgroud)

要使匹配区分大小写,请删除上面I出现的后面的内容/I。

  • -n抑制默认输出,以便必须使用诸如 之类的函数在脚本内显式请求输出p。
  • 请注意在脚本周围使用双引号 ( "...") sed,以便允许引用shell变量$name:双引号确保 shell 变量引用在脚本被传递之前展开sed(sed本身无法访问 shell 变量)。
    • 警告:此技术很棘手,因为 (a) 您必须使用 shell 转义来转义要传递给的shellsed元字符,例如,$并且\$(b) shell 变量值不得包含可能sed破坏sed脚本的元字符;对于在sed脚本中使用的 shell 变量值的通用转义,请参阅我的这个答案,或使用我的awk基于答案。
  • /$name/I,/^[^[:space:]]/使用一个范围来匹配感兴趣的行(/$name/I; 尾随I是 GNUsed的不区分大小写的匹配选项)到下一节的开头( -/^[^[:space:]]/即不以空格开头的下一行);由于sed范围始终包含在内,因此挑战是有选择地删除范围的最后一行(如果它是下一部分的开始)-请注意,如果感兴趣的部分是文件中的最后一个部分,则不会出现这种情况。
    请注意,里面的命令{ ... }仅针对范围内的每一行执行。
  • /$name/I {p;d};无条件打印范围的第一行:d删除该行(已打印)并开始下一个循环(继续到下一个输入行)。
  • /^[^[:space:]]/q匹配范围中的最后一行,如果它是下一部分的第一行,则完全退出处理 ( q),而不打印该行。
  • p然后仅到达部分内部线并打印它们。

笔记:

  • 假设标题行可以通过不以空白字符开头来识别,并且任何其他行都是非标题行 - 如果需要更复杂的匹配,请参阅我的awk基于答案。
  • 此解决方案有一个轻微的缺点,即范围正则表达式必须重复,尽管您可以使用 shell 变量来缓解这一问题。

FreeBSD/macOSsed几乎可以做同样的事情,只是它缺少不区分大小写的选项I。

name='DOG'  # case-SENSITIVE name of section to extract
sed -n -e "/$name/,/^[^[:space:]]/ { /$name/ {p;d;}; /^[^[:space:]]/q; p; }" file
Run Code Online (Sandbox Code Playgroud)

请注意,FreeBSD/OSXsed通常具有更严格的语法要求,例如;命令后即使后跟}.

如果您确实需要不区分大小写,请参阅我的awk答案。