如何在匹配grep表达式的行之后获取文件的一部分?(第一场比赛)

Yug*_*dle 163 bash shell scripting grep

我有一个大约1000行的文件.我希望我的文件的一部分在与我的grep语句匹配的行之后.

$ cat file | grep 'TERMINATE'     # It is found on line 534
Run Code Online (Sandbox Code Playgroud)

所以,我希望该行文件__CODE__可以进一步处理.

我该怎么做 ?

jfg*_*956 296

以下将打印匹配TERMINATE到文件末尾的行:

sed -n -e '/TERMINATE/,$p'
Run Code Online (Sandbox Code Playgroud)

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

这将从行匹配后的行打印TERMINATE到文件末尾:(
从匹配行到EOF之后,不包括匹配行)

sed -e '1,/TERMINATE/d'
Run Code Online (Sandbox Code Playgroud)

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

编辑:

如果你想要之前的行TERMINATE:

sed -e '/TERMINATE/,$d'
Run Code Online (Sandbox Code Playgroud)

如果你想TERMINATE在一次传递中在2个不同的文件中前后两行:

sed -e '1,/TERMINATE/w before
/TERMINATE/,$w after' file
Run Code Online (Sandbox Code Playgroud)

before和after文件将包含具有terminate的行,因此要处理每个需要使用的行:

head -n -1 before
tail -n +2 after
Run Code Online (Sandbox Code Playgroud)

EDIT2:

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

before=before.txt
after=after.txt
sed -e "1,/TERMINATE/w $before
/TERMINATE/,\$w $after" file
Run Code Online (Sandbox Code Playgroud)

但是你必须逃避$最后一行的含义,所以shell不会尝试扩展$w变量(注意我们现在使用脚本周围的双引号而不是单引号).

我忘了告诉新行在脚本中的文件名之后很重要,以便sed知道文件名结束.


编辑: 2016-0530

SébastienClément问道:"你怎么TERMINATE用变量取代硬编码?"

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

matchtext=TERMINATE
before=before.txt
after=after.txt
sed -e "1,/$matchtext/w $before
/$matchtext/,\$w $after" file
Run Code Online (Sandbox Code Playgroud)

使用变量作为匹配文本与前面的示例:

## 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"
Run Code Online (Sandbox Code Playgroud)
## 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"
Run Code Online (Sandbox Code Playgroud)
## 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"
Run Code Online (Sandbox Code Playgroud)

在这些情况下,用变量替换文本的重点是:

  1. $variablename包含在single quotes[ ']中的变量()不会"展开",而是double quotes[ "]中的变量.因此,如果它们包含要用变量替换的文本single quotes,double quotes则必须更改所有to .
  2. sed范围也包含$并紧跟像字母:$p,$d,$w.他们也将像变量加以扩展,所以你要逃避这些$字符用反斜杠[ \],如:\$p,\$d,\$w.

  • 这里缺少的一个用例是如何在最后一个标记之后打印行(如果文件中可以有多个行,请考虑日志文件等)。 (2认同)

aio*_*obe 61

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

grep -A100000 TERMINATE file
Run Code Online (Sandbox Code Playgroud)

它会在该TERMINATE行之后输出并输出多达100000行.

从手册页

-A NUM, --after-context=NUM

匹配行后打印NUM行尾随上下文. 在连续的匹配组之间放置一个包含组分隔符( - )的行.使用-o或--only-matching选项,这不起作用,并给出警告.

  • 好主意!如果你不确定上下文的大小,你可以计算`file`的行:`grep -A $(cat file | wc -l)TERMINATE file` (3认同)
  • 我认为这是一个实用的解决方案! (2认同)
  • 类似-B NUM, - before-context = NUM​​在匹配行之前打印NUM行前导上下文.在连续的匹配组之间放置一个包含组分隔符( - )的行.使用-o或--only-matching选项,这不起作用,并给出警告. (2认同)

小智 26

这里使用的工具是awk:

cat file | awk 'BEGIN{ found=0} /TERMINATE/{found=1}  {if (found) print }'
Run Code Online (Sandbox Code Playgroud)

这是如何运作的:

  1. 我们将变量'found'设置为零,评估为false
  2. 如果在正则表达式中找到'TERMINATE'的匹配项,我们将其设置为1.
  3. 如果我们的'found'变量的计算结果为True,则打印:)

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

  • 这里不使用的工具是cat。“ awk”完全有能力将一个或多个文件名作为参数。另请参阅/sf/ask/819738671/ (2认同)

Ulf*_*lfR 8

如果我正确地理解了你的问题,你确实想要后面 的行TERMINATE,不包括TERMINATE-line.awk可以这么简单地做到这一点:

awk '{if(found) print} /TERMINATE/{found=1}' your_file
Run Code Online (Sandbox Code Playgroud)

说明:

  1. 虽然不是最佳实践,但您可以依赖于所有变量默认为0或空字符串(如果未定义)的事实.所以第一个表达式(if(found) print)不会打印任何东西.
  2. 打印完成后,我们检查这是否是起动线(不应包括在内).

这将打印-line 之后的所有行TERMINATE.


概括:

  • 你有一个文件的开始 -和结束 -lines,你想那些线之间的线不包括开始 -和结束 -lines.
  • start - 和end -lines可以由匹配该行的正则表达式定义.

例:

$ 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
$
Run Code Online (Sandbox Code Playgroud)

说明:

  1. 如果发现结束线,则不应进行打印.请注意,此检查在实际打印之前完成,以从结果中排除结束行.
  2. 如果found已设置,则打印当前行.
  3. 如果找到起始found=1线,则设置为打印以下行.请注意,此检查在实际打印完成,以从结果中排除起始线.

笔记:

  • 代码依赖于以下事实:如果未定义,则所有awk-vars默认为0或空字符串.这是有效的,但可能不是最佳实践,因此您可以BEGIN{found=0}在awk-expression的开头添加一个.
  • 如果找到多个起始端块,则全部打印.


Mu *_*iao 7

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

content=$(cat file)
echo "${content#*TERMINATE}"
Run Code Online (Sandbox Code Playgroud)

  • 如果文件是100GB大小会发生什么? (6认同)
  • 否决:这是可怕的(将文件读入变量)并且是错误的(使用变量而不引用它;并且您应该正确使用“printf”或确保您确切地知道要传递给“echo”的内容。)。 (2认同)

小智 6

grep -A 10000000 '终止' 文件

  • 比 sed 快得多,尤其是在处理非常大的文件时。它最多可以运行 1000 万行(或您输入的任何内容),因此将其设置得足够大以处理您遇到的任何问题都没有坏处。


fed*_*qui 5

有很多方法可以使用sedor来做到这一点awk

sed -n '/TERMINATE/,$p' file
Run Code Online (Sandbox Code Playgroud)

这会在您的文件中查找TERMINATE并从该行打印到文件末尾。

awk '/TERMINATE/,0' file
Run Code Online (Sandbox Code Playgroud)

这与以下行为完全相同sed

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

awk 'NR>=535' file
Run Code Online (Sandbox Code Playgroud)

例子

$ 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
Run Code Online (Sandbox Code Playgroud)