如何在第一次出现“开始模式”-“停止模式”对后打印文本?

如何在第一次出现“开始模式”-“停止模式”对后打印文本?

我有一个包含一堆证书的文件:

-----BEGIN CERTIFICATE-----
AAAAAAA
AAAAAAA
AAAAAAA
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
BBBBBBB
BBBBBBB
BBBBBBB
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
CCCCCCC
CCCCCCC
CCCCCCC
-----END CERTIFICATE-----

A我想把里面写的那张砍掉,只得到B C……n证书。

它非常类似于这个问题我希望有一种便携的方式来做到这一点。sed如果可能的话最好使用 with ,但awk如果不可能使用 ,也可以使用sed

有没有办法让 sed 打印直到除了第一次出现之外的特定值?

答案1

如果您的输入是格式良好的证书文件(如示例所示),那么我想到的最简单的方法是使用以下命令awk

awk '$0=="-----BEGIN CERTIFICATE-----" {n++} n>1' test.cert 

n每当当前行 ( $0) 与“起始模式”完全匹配时,这都会增加一个计数器变量。如果“看似杂散”的布尔表达式n>1为真,即从第二次出现的起始模式开始,它将打印当前行。awk将未初始化的变量视为零(或空字符串,具体取决于使用上下文),因此无需在节中显式n初始化。0BEGIN

如果您的输入文档可能被损坏,即包含与结束模式不正确匹配的开始模式,则事情会变得更加复杂,反之亦然。

答案2

@AdminBee 答案是正确的方法,但是如果您确定要省略的部分是第一的sed你也可以这样做:

sed -n '/^-*END CERTIFICATE-*$/!d;:a n;p;ba' file

或者以多线便携式方式:

sed -n '
    /^-*END CERTIFICATE-*$/!d;:a
    n;p;ba
' file

sed将删除所有行,直到找到第一行-----END CERTIFICATE-----,然后创建一个循环,该循环将吃掉并打印所有其他行。

答案3

您应该将正则表达式 /BEGIN/ 和 /END/ 更改为必需的。

awk '
/BEGIN/,/END/{
  if ( /END/ && !f++ ) next
}f
' file

perl处于吸食模式 ( -0777)

perl -0777 -pe '
  my($b,$e) = map { quotemeta s/$/ CERTIFICATE/r } qw(BEGIN END);
  my($B,$E) = map { qr{^-+ $_ -+\n}mx } ($b,$e);
  my $re = qr{$B (?s:.*?) $E}mx;
  substr($_,0,$+[0],"") if m{$re}m;
' file

GNU sed 编辑器

sed -ne 's/\n//;t2
  /BEGIN/!{$!N;D;}
  :1;n;/END/!b1
  n
  :2;$!{N;P;G;D;}
  p
' file

csplit此处也可以使用linux 实用程序。我们首先在 END 行周围对输入文件进行分块。然后删除第一个文件或前两个文件,以防 END 出现在 BEGiN 之前。

csplit -sz file '/END/+1' '{*}'
for f in xx*;do
  sed -n '/BEGIN/,/END/!d;$Q1' $f
  status=$?; rm -- "$f"
  [ " $status" = ' 1' ] && break
done
printf '%s\n' xx* | xargs -r cat

相关内容