我需要:
打印“hi”和“hello”模式之间的文本。
模式可能会重复,并且可能会多次出现hi
......hello
源文件:
hi aa bb cc
dd ee ff
hello xxxxxx
aaaa dddd cccc hi aaa bbb
ccc hello cccc fff
预期输出。
hi aa bb cc
dd ee ff
hello
hi aaa bbb
ccc hello
我尝试使用awk
and命令,但它显示第一次出现和最后一次出现sed -n
之间的所有行。hi
hello
答案1
来自第一的你好第一的你好,接下来。
grep
使用 (GNU) grep 和 tr:
$ <infile grep -oPz "(?s)hi.*?hello" | tr '\0' '\n'
hi aa bb cc
dd ee ff
hello
hi aaa bbb
ccc hello
说明:
<infile
源文件。grep -oPz
拨电grep
至:- (
-P
) 匹配一个磷商业地产(Perl 兼容正则表达式) - (
-o
)哦只打印匹配的部分。 - (
-z
) 用一个zero 字节(又名 NUL 和 aka\0
)作为行分隔符。
- (
"(?s)
使 PCRE 点 (.
) 也匹配换行符。hi
从字符串开始hi
。.*?
匹配后面的所有字符(非贪婪,因为?
)。hello"
直到字符串hello
匹配为止。| tr '\0' '\n'
将 NUL (\0
) 字节(从grep -z
)转换为换行符。
sed
GNU sed:
<infile sed 's/hi/\n&/;s/[^\n]*\n//;s/\(hello\).*/\1/;/hi/,/hello/!d'
或者,对于 BSD sed,它不允许\n
在 的右侧s///
,您需要定义一个newline
变量nl
:
$ eval "$(printf "nl='\n'")"
进而:
<infile sed 's/hi/\'"$nl"'&/;s/[^\n]*\n//;s/\(hello\).*/\1/;/hi/,/hello/!d'
或者;如果你可以写一个明确的换行符:
<infile sed 's/hi/\
&/;s/[^\n]*\n//;s/\(hello\).*/\1/;/hi/,/hello/!d'
答案2
grep -oPz "(?s)hi.*?hello" fileName
解释如下。
猫tmp
输出->
hi aa bb cc
dd ee ff
hello xxxxxx
aaaa dddd cccc hi aaa bbb
ccc hello cccc fff
00000000000
hi ff
djd h
sdkf hello
dfj
解决方案:
grep -oPz "(?s)hi.*?hello" tmp
输出->
hi aa bb cc
dd ee ff
hello
hi aaa bbb
ccc hello
hi ff
djd h
sdkf hello
参数:
-z
该选项告诉 grep 将换行符视为普通文本字符,并查找空字节来分隔记录。在没有空字节的文本文件中,grep -z
会将整个文件视为一行。(?s)
激活 PCRE_DOTALL,这意味着 '.'查找任何字符或换行符。o
只打印匹配的部分-P
将该模式解释为与 Perl 兼容的正则表达式 (PCRE)。这是高度实验性的,特别是与 -z (--null-data) 选项结合使用时,并且“grep -P”可能会警告未实现的功能。.*?
对于非贪婪匹配。hi
当您找到第一个实例时,开始搜索下一个匹配项hello
答案3
awk 咯咯笑
#!/usr/bin/awk -f
{
n=split($0, col, FS)
for(i=1;i<=n;i++){
(i==n)? sp="" : sp=FS
if (col[i] ~ /hi/) p=1
if (p == 1 && col[i] ~ /hello/) h=1
if (p == 1) printf("%s%s",col[i],sp)
if (h==1) p=0;h=0
}
printf "\n"
}
kapu@jake:$ cat SourceFile
hi aa bb cc
dd ee ff
hello xxxxxx
aaaa dddd cccc hi aaa bbb
ccc hello cccc fff
kapu@jake:$ script.awk SourceFile
hi aa bb cc
dd ee ff
hello
hi aaa bbb
ccc hello