我使用 grep 查找一个字符串和该字符串上下的不确定行数,直到匹配双断线。例如,在我有这样内容的文档中:
Name: Alice
ID: 6969
Interests: foo,bar
Name: Bob
ID: 5555
Interests: foo,bar
Experience
Name: Carl
ID: 3236
Interests: foo,bar
我想通过 ID 查找有关一个人的所有信息,所以如果我查找 5555,我想要的输出是:
Name: Bob
ID: 5555
Interests: foo,bar
Experience
我尝试使用grep -C n ID_string
(其中 n 是上下行数以匹配给定的字符串),但输出是固定数量的行"n",我想要直到双换行符。知道吗?谢谢。
这可能对你有用(GNU sed):
sed -n '/S/{h;:a;n;//{H;$!ba};g;/5555/p}' file
关闭隐式打印-n
。
当当前行包含非空格字符时,开始保留空间中的行集合。
在空白行或文件末尾结束集合。
测试集合中所需字符串,如果匹配,则打印整个集合。重复。
要向结果添加换行符,请使用:
sed -n '/S/{h;:a;n;//{H;$!ba};z;H;g;/5555/p}' file
Sed 是一个流编辑器。它是用来编辑文本文件,通常一次处理一行文本。它有两个缓冲区用于完成此任务。模式空间 (PS) 和称为保持空间 (HS) 的备用缓冲区。正常的事件流是 sed 将一行文本读入 PS 并剥离其换行符。Sed 指令(命令)作用于 PS,剥离的换行符被重新附加,结果被传递到标准输出,即打印。
-n
选项关闭了将 PS 传送到 stdout 的隐式性质,即如果要打印某些内容,则必须发出命令才能执行此操作,例如打印 PS 或打印 PS 第一行的p
或P
。
Sed 使用正则表达式来决定是否将命令应用于 PS。/S/
是一个正则表达式,用于测试 PS 中是否有任何非空格字符。Sed 使用括号对命令进行分组,命令用分号分隔。
h
命令将保留空间 (HS) 中的任何内容替换为 PS 的内容。
Sed 可以执行循环。它通过定义要循环到的占位符和要中断到循环占位符的命令来实现此目的。:a
定义一个名为a
的循环占位符,b
是命令的中断。
n
命令将下一行提取到 PS 中。通常,这会导致 PS 的内容在被替换之前被推送到 stdout,但由于-n
选项处于打开状态,因此其内容会被丢弃。
//
是上一个正则表达式的简写,即现在再次测试PS的内容是否有非空格字符,如果是这样,则执行括号中的命令。在这种情况下,H
将 PS 附加到由事先剥离的换行符分隔的 HS。
Sed 知道每行的行号,它还知道文件的最后一行何时出现在 PS 中。$
表示最后一行。!
是 not 命令,并否定以前的地址或正则表达式,例如$!
表示不是文件的最后一行。将它们放在一起$!ba
意味着,如果它不是文件的最后一行,请中断b
到占位符a
。因此,命令流被定向回:a
,sed 从那里恢复处理。
如果//
不匹配,则推断出两种可能性,当前行为空或文件的最后一行。z
将 PS 击倒并清空它。H
将空行追加到由换行符分隔的 HS
。g
用 HS 的内容代替 PS。循环建立的行集合现在位于 PS 中。另一个正则表达式尝试在 PS/5555/
上匹配,如果是这样,则发出打印 PS 的p
命令。
因此,sed 程序在文件中移动,收集 HS 中非空行的集合,并在正则表达式匹配时打印它们。
在每个 UNIX 机器上使用任何 shell 中的任何 awk:
$ awk -v RS= -v ORS='nn' '/(^|n)ID: 5555(n|$)/' file
Name: Bob
ID: 5555
Interests: foo,bar
Experience
使用这种相同方法从字段中查找其他值组合的几个示例:
$ awk -v RS= -v ORS='nn' '/(^|n)Name: [[:alpha:]]*[aA].*(n|$)/' file
Name: Alice
ID: 6969
Interests: foo,bar
Name: Carl
ID: 3236
Interests: foo,bar
$ awk -v RS= -v ORS='nn' '/(^|n)Name: [[:alpha:]]*[aA]/ && /(^|n)ID: 6/' file
Name: Alice
ID: 6969
Interests: foo,bar
$ awk -v RS= -v ORS='nn' '/(^|n)Name: [[:alpha:]]*[aA]/ && /(^|n)ID: [0-9]+6(n|$)/' file
Name: Carl
ID: 3236
Interests: foo,bar
你能试试下面吗?
awk '
/^Name/{
if(found){
print value
}
value=found=""
}
{
value=(value?value ORS:"")$0
}
/ID:/{
if($NF==5555){
found=1
}
}
END{
if(found){
print value
}
}
' Input_file
说明:在此处添加上述代码的详细说明。
awk ' ##Starting awk program from here.
/^Name/{ ##Checking if a line starts with Name then do following.
if(found){ ##Checking if found is SET then do following.
print value ##Printing variable value here.
}
value=found="" ##Nullifying value and found values here.
}
{
value=(value?value ORS:"")$0 ##Creating value here which will have all lines value separated with new line.
}
/ID:/{ ##Checking if a line has ID: then do following.
if($NF==5555){ ##Checking condition if last field is 5555.
found=1 ##Then set found=1 here.
}
}
END{ ##Starting END block of this program here.
if(found){ ##Checking if found is SET then do following.
print value ##Printing variable value here.
}
}
' Input_file ##Mentioning Input_file name here.
使用pcregrep
您可以尝试:
cat data.txt | pcregrep -M '(^.+$n)*ID: 5555n(^.+$n)*'
通过一个小的调整,您还可以用换行符分隔返回/显示的匹配项,以便于阅读:
cat temp.txt | pcregrep -M '(^.+$n)*ID: 5555n(^.+$n)*n?'
您可能需要安装pcregrep
,例如使用:
sudo apt install pcregrep
awk '
!NF{delete buffer;i=0;go_on=0;next}
/ID: 5555/{
for(j=1;j<=i;j++)print buffer[j]
go_on=1
}
go_on
{i++;buffer[i]=$0}
' file
如果您不想对 ID 号或文件名进行硬编码,请保存此内容并使其可执行:
awk -v number=$1 '
!NF{delete buffer;i=0;go_on=0;next}
$0 ~ "ID: " number{
for(j=1;j<=i;j++)print buffer[j]
go_on=1
}
go_on
{i++;buffer[i]=$0}
' "$2"
然后称它为./script 5555 file
.
示例输入文件:
Name: Alice
ID: 6969
Interests: foo,bar
Surname: John
Nickname: Bill
Name: Bob
ID: 5555
Interests: foo,bar
Experience
Name: Carl
ID: 3236
Interests: foo,bar
输出:
Surname: John
Nickname: Bill
Name: Bob
ID: 5555
Interests: foo,bar
Experience
这个Perl单行代码可以:
what=5555 perl -00 -ne '/ID:s+$ENV{what}/m and print' file
-00
开关启用paragraph
模式。在此模式下,每条记录都是由一个或多个空行限定的文本段落(块)。
如果中的一行与带有一个或多个尾随空格的标记ID:
匹配,后跟启动脚本时设置的值what
,则会打印该段落。请注意,what
仅在命令行持续时间内设置。
m
正则表达式修饰符使$
匹配字符串中每行的末尾。这将为what
创建完全匹配。