![Grep 在文件中查找文本并打印前面 3 行的文本](https://linux22.com/image/218043/Grep%20%E5%9C%A8%E6%96%87%E4%BB%B6%E4%B8%AD%E6%9F%A5%E6%89%BE%E6%96%87%E6%9C%AC%E5%B9%B6%E6%89%93%E5%8D%B0%E5%89%8D%E9%9D%A2%203%20%E8%A1%8C%E7%9A%84%E6%96%87%E6%9C%AC.png)
我有一个文件,并将在该文件中查找多个文件名。搜索文本示例intl_reg.jcl
。找到它后,我需要获取其上方 3 行的文本。例如,下面是文件中的 4 行。描述“有余额的注册国际学生”之前的文字始终是 CHOICE,后面是描述。
CHOICE List registered international students with balances
SHORTCUT 8
PROCESS CHDIR /d3/locban/arsys
PROCESS CMD ksh /d3/locban/arsys/intl_reg.jcl
我需要 grep 命令来列出它,如下所示:
intl_reg.jcl registered international students with balances
答案1
使用乐(以前称为 Perl_6)
~$ raku -e 'my @a; for lines() {
if @a.elems == 4 { @a.push($_); @a.shift }
elsif @a.elems < 4 { @a.push($_) };
if .match( /intl_reg\.jcl/ ) and @a.elems == 4 {
say $<>.Str, @a[0].split( /<?after List>/ ).[1] };
};' file
简而言之,@a
声明一个数组并lines
从命令行读入。输入被push
添加到@a
数组中,直到elems
等于 4,然后第一个元素被shift
删除以保持最大大小为 4 个元素。
如果match
发现and
数组包含 4 个元素,则$<>
打印匹配变量($<>
与 相同$/
),然后打印 3 行之前记录的行,以适当地split
返回所需的字符串。
输入示例:
A CHOICE List registered international students with balances
A SHORTCUT 8
A PROCESS CHDIR /d3/locban/arsys
A PROCESS CMD ksh /d3/locban/arsys/intl_reg.jcl
B CHOICE List registered international students with balances
B SHORTCUT 8
B PROCESS CHDIR /d3/locban/arsys
B PROCESS CMD ksh /d3/locban/arsys/intl_reg.jcl
示例输出:
intl_reg.jcl registered international students with balances
intl_reg.jcl registered international students with balances
答案2
以下awk
程序可以工作。
选项 1:双通道
awk -v d=3 -v s="intl_reg.jcl" '(NR==FNR) && index($0,s){i=FNR;nextfile}
FNR==(i-d){printf "%s\t%s\n",s,$0; exit}' input.txt input.txt
此处,输入文件被指定两次,以便对其进行两次处理。要搜索的字符串作为awk
变量传递s
,距离作为awk
变量传递d
。
- 在第一遍中,
NR
全局行计数器 等于FNR
每个文件行计数器,每行都会检查字符串是否出现。如果找到,行号将存储在变量中i
,并且执行立即跳到下一个文件(=同一文件的下一次迭代)。 - 在第二遍中,程序检查当前行号是否
d
小于先前识别的模式出现次数(存储在i
.当遇到该行时,它将与搜索字符串一起打印,并且程序终止(以免闲置地循环文件的其余部分。
选项 2:单程
为了加速该过程,并且对于无法重新访问文件的情况(例如,因为它是管道),也可以使用带有缓冲的单通道解决方案。在这种情况下,需要一定长度的 FIFO 缓冲区,这意味着如果太大则d
不切实际(但很少会成为问题):d
awk -v d=3 -v s="intl_reg.jcl" 'FNR>1{for (j=d;j>0;j--) {buf[j]=buf[j-1]}; buf[0]=$0}
FNR>d && match($0,s) {printf "%s\t%s\n",s,buf[d];exit}' input.txt
buf
这将连续用最后几行填充缓冲区d+1
,其中缓冲区内容在每个新行处“向上移动”,并且当前行始终位于buf[0]
.一旦找到搜索字符串,它将打印 的内容buf[d]
,即d
当前行之前的行。同样,由于性能原因,程序会立即终止。
答案3
match=intl_reg.jcl
tac {file} |
sed -n "/$match/ { n; n; n; s/^CHOICE/$match/p }"
确保既不$match
包含/
也不包含正则表达式运算符。 (请注意,这.
实际上是一个通配符,因此请立即打破此规则。这对您来说可能是问题,也可能不是问题。)
答案4
使用任何 awk:
$ awk -v t='intl_reg.jcl' '{a[NR%4]=$0} index($0,t){$0=a[(NR-3)%4]; $1=$2=""; print t $0}' file
intl_reg.jcl registered international students with balances