如何获取文件中匹配正则表达式的第一行之后的部分

我有一个大约1000行的文件。我想要我的文件中与我的grep语句匹配的行之后的部分。

那就是:

cat file | grep 'TERMINATE'     # It is found on line 534

因此,我需要从第535行到第1000行的文件进行进一步处理。

我该怎么做?

223766 次浏览

使用如下所示的bash参数扩展:

content=$(cat file)
echo "${content#*TERMINATE}"

作为一个简单的近似值,您可以使用

grep -A100000 TERMINATE file

其对TERMINATE进行greps,并在该行之后输出多达100,000行。

手册页

-A NUM, --after-context=NUM

在匹配行之后打印尾部上下文的行数。 将包含组分隔符(--)的行放置在 连续的匹配组。与-o或--only-匹配 选项,则不起作用,并给出警告。

_,ABC_0是一个更好的工作工具:

sed -n '/re/,$p' file

其中re是正则表达式。

另一个选项是格雷普--after-context标志。您需要传入一个结束于的数字,使用文件上的厕所应该给出正确的停止值。将其与-n和匹配表达式结合使用。

以下命令将打印与TERMINATE匹配的行,直到文件结束:

sed -n -e '/TERMINATE/,$p'

解释:-n禁用sed的默认行为,即在其上执行脚本后打印每一行,-esed指示脚本,/TERMINATE/,$是地址(行)范围选择,表示与TERMINATE正则表达式(如grep)匹配的第一行到文件末尾($),p是打印当前行的打印命令。

这将从与TERMINATE匹配的行的下一行开始打印,直到文件结束: (从匹配行之后到EOF,不包括匹配行)

sed -e '1,/TERMINATE/d'

解释:1,/TERMINATE/是地址(行)范围选择,意味着第一行用于输入到与TERMINATE正则表达式匹配的第一行,并且d是删除命令,其删除当前行并跳到下一行。由于sed的默认行为是打印行,因此它将在TERMINATE到输入结束后打印行。

如果要_ABC之前的行,请_0:

sed -e '/TERMINATE/,$d'

如果您希望在一次传递中在两个不同的文件中将_ABC之前和之后的两行_0:

sed -e '1,/TERMINATE/w before
/TERMINATE/,$w after' file

BEFORE和AFTER文件将包含带有TERMINATE的行,因此要处理每个行,您需要使用:

head -n -1 before
tail -n +2 after

如果您不想在sed脚本中对文件名进行硬编码,则可以:

before=before.txt
after=after.txt
sed -e "1,/TERMINATE/w $before
/TERMINATE/,\$w $after" file

但是,您必须对$(表示最后一行)进行转义,这样shell就不会尝试展开$w变量(请注意,我们现在使用双引号将脚本括起来,而不是使用单引号)。

我忘了告诉您,在脚本中文件名之后的新行很重要,这样sed就知道文件名结束了。

如何将硬编码的TERMINATE替换为变量?

您可以为匹配的文本创建一个变量,然后按照与上一个示例相同的方式执行:

matchtext=TERMINATE
before=before.txt
after=after.txt
sed -e "1,/$matchtext/w $before
/$matchtext/,\$w $after" file

要将变量用于前面示例中的匹配文本,请执行以下操作:

## Print the line containing the matching text, till the end of the file:
## (from the matching line to EOF, including the matching line)
matchtext=TERMINATE
sed -n -e "/$matchtext/,\$p"
## Print from the line that follows the line containing the
## matching text, till the end of the file:
## (from AFTER the matching line to EOF, NOT including the matching line)
matchtext=TERMINATE
sed -e "1,/$matchtext/d"
## Print all the lines before the line containing the matching text:
## (from line-1 to BEFORE the matching line, NOT including the matching line)
matchtext=TERMINATE
sed -e "/$matchtext/,\$d"

在这些情况下,将文本替换为变量的要点是:

  1. single quotes[']中包含的变量($variablename)不会";展开";但是double quotes["]中的变量将。因此,您必须将所有single quotes更改为double quotes(如果它们包含要用变量替换的文本)。
  2. sed范围还包含一个$,后面紧跟一个字母,如:$p$d$w。它们看起来也像是要展开的变量,因此您必须使用反斜杠[\]对这些$字符进行转义,例如:\$p\$d\$w

如果出于任何原因,您希望避免使用塞德,以下命令将打印与TERMINATE匹配的行,直到文件结束:

tail -n "+$(grep -n 'TERMINATE' file | head -n 1 | cut -d ":" -f 1)" file

并且将从以下与TERMINATE匹配的行开始打印以下内容,直到文件结束:

tail -n "+$(($(grep -n 'TERMINATE' file | head -n 1 | cut -d ":" -f 1)+1))" file

sed可以在一个进程中完成的工作需要两个进程来完成,如果文件在执行grep和tail之间发生变化,结果可能不一致,因此我建议使用sed.此外,如果文件不包含TERMINATE,则第一个命令将失败。

这里使用的一个工具是awk

cat file | awk 'BEGIN{ found=0} /TERMINATE/{found=1}  {if (found) print }'

这是如何工作的:

  1. 我们将变量' found '设置为零,求值为false
  2. 如果在正则表达式中找到“ Terminate ”的匹配项,则将其设置为1。
  3. 如果“ found ”变量的计算结果为true,则打印:)

如果您在非常大的文件上使用其他解决方案,它们可能会消耗大量内存。

有许多方法可以使用sedawk来执行此操作:

sed -n '/TERMINATE/,$p' file

这将在文件中查找TERMINATE,并从该行打印到文件末尾。

awk '/TERMINATE/,0' file

这与sed的行为完全相同。

如果您知道要开始打印的行号,则可以将其与NR(记录数,最终表示行号)一起指定:

awk 'NR>=535' file

例子

$ seq 10 > a        #generate a file with one number per line, from 1 to 10
$ sed -n '/7/,$p' a
7
8
9
10
$ awk '/7/,0' a
7
8
9
10
$ awk 'NR>=7' a
7
8
9
10

如果我对您的问题理解正确,您确实需要之后TERMINATE的行,但不包括TERMINATE行。awk可以通过一种简单的方式实现这一点:

awk '{if(found) print} /TERMINATE/{found=1}' your_file

解释:

  1. 尽管不是最佳实践,但您可以依赖这样一个事实,即所有变量都默认为0或空字符串(如果未定义)。因此,第一个表达式(if(found) print)不会打印任何内容。
  2. 打印完成后,我们检查这是否是起始行(不应包括在内)。

这将之后TERMINATE行打印所有行。


一般化:

  • 您有一个包含开始-和结束-行的文件,并且您希望这些行之间的行排除开始-和结束-行。
  • 开始-和结束-行可以由与行匹配的正则表达式定义。

示例:

$ cat ex_file.txt
not this line
second line
START
A good line to include
And this line
Yep
END
Nope more
...
never ever
$ awk '/END/{found=0} {if(found) print} /START/{found=1}' ex_file.txt
A good line to include
And this line
Yep
$

解释:

  1. 如果发现结束行,则不应进行打印。注意,该检查是在ABC_1实际打印_进行的,以从结果中排除结束行。
  2. 如果设置了found,则打印当前行。
  3. 如果找到开始行,则将_ABC_设置为0,以便打印后面的行。注意,该检查是在ABC_2实际打印_进行的,以从结果中排除开始行。

注意事项:

  • 代码依赖于这样一个事实:如果没有定义,所有的awk变量都默认为0或空字符串。这是有效的,但它可能不是最佳实践,因此您可以将BEGIN{found=0}添加到awk表达式的开头。
  • 如果找到多个开始-结束-块,则全部打印。

这将打印最后找到的行中的所有行";TERMINATE";直到文件结束:

LINE_NUMBER=`grep -o -n TERMINATE $OSCAM_LOG | tail -n 1 | sed "s/:/ \\'/g" | awk -F" " '{print $1}'`
tail -n +$LINE_NUMBER $YOUR_FILE_NAME

优秀sed由JFG956应答的替代品,不包括匹配线:

这可能是一种方法。如果您知道文件的哪一行中有您的grep单词,以及您的文件中有多少行:

grep -A466 'TERMINATE' file
grep -A 10000000 'TERMINATE' file

比sed快得多,尤其是在处理非常大的文件时。它可以工作到10米线(或者你放进去的任何东西),所以把它做得足够大来处理你碰到的任何东西都没有任何坏处。