检查文件中的多行内容
Check for multi-line content in a file
我正在尝试使用常用 bash 命令(grep、awk、...)检查文件中是否存在多行字符串。
我想要一个包含几行、普通行而不是模式的文件,它应该存在于另一个文件中,并创建一个命令(序列)来检查它是否存在。如果 grep
可以接受任意多行模式,我会用类似于
的东西来做
grep "`cat contentfile`" targetfile
与 grep 一样,我希望能够从命令中检查退出代码。我对输出并不感兴趣。实际上没有输出是首选,因为从那时起我不必通过管道传输到 /dev/null
.
我已经搜索过提示,但无法提供任何合适的搜索结果。有 How can I search for a multiline pattern in a file?,但那是关于模式匹配的。
我找到了 pcre2grep
,但需要使用 "standard" *nix 工具。
示例:
内容文件:
line 3
line 4
line 5
目标文件:
line 1
line 2
line 3
line 4
line 5
line 6
这应该与 return 0 匹配,因为在目标文件中找到了内容文件中的行序列(以完全相同的顺序)。
编辑:很抱歉不清楚 "pattern" 与 "string" 的比较以及 "output" 与 "exit code" 在这个问题的先前版本中。
编辑: 由于 OP 需要真或假(是或否)形式的命令结果,因此编辑命令现在的方式(在 GNU awk
中创建和测试)。
awk -v message="yes" 'FNR==NR{a[[=10=]];next} ([=10=] in a){if((FNR-1)==prev){b[++k]=[=10=]} else {delete b;k=""}} {prev=FNR}; END{if(length(b)>0){print message}}' patternfile targetfile
能否请您尝试以下,用给定的样本进行测试,如果它们在目标文件中以相同的顺序出现,它应该打印模式文件中的所有连续行(此代码中连续行的计数应至少为 2)。
awk '
FNR==NR{
a[[=11=]]
next
}
([=11=] in a){
if((FNR-1)==prev){
b[++k]=[=11=]
}
else{
delete b
k=""
}
}
{
prev=FNR
}
END{
for(j=1;j<=k;j++){
print b[j]
}
}' patternfile targetfile
说明: 在此处添加对上述代码的说明。
awk ' ##Starting awk program here.
FNR==NR{ ##FNR==NR will be TRUE when first Input_file is being read.
a[[=12=]] ##Creating an array a with index [=12=].
next ##next will skip all further statements from here.
}
([=12=] in a){ ##Statements from here will will be executed when 2nd Input_file is being read, checking if current line is present in array a.
if((FNR-1)==prev){ ##Checking condition if prev variable is equal to FNR-1 value then do following.
b[++k]=[=12=] ##Creating an array named b whose index is variable k whose value is increment by 1 each time it comes here.
}
else{ ##Mentioning else condition here.
delete b ##Deleting array b here.
k="" ##Nullifying k here.
}
}
{
prev=FNR ##Setting prev value as FNR value here.
}
END{ ##Starting END section of this awk program here.
for(j=1;j<=k;j++){ ##Starting a for loop here.
print b[j] ##Printing value of array b whose index is variable j here.
}
}' patternfile targetfile ##mentioning Input_file names here.
一个one-liner:
$ if [ $(diff --left-column -y patternfile targetfile | grep '(' -A1 -B1 | tail -n +2 | head -n -1 | wc -l) == $(cat patternfile | wc -l) ]; then echo "ok"; else echo "error"; fi
解释:
首先是使用diff
比较两个文件:
diff --left-column -y patternfile targetfile
> line 1
> line 2
line 3 (
line 4 (
line 5 (
> line 6
然后过滤以仅显示有趣的行,即“(”行,加上匹配前后的额外 1 行,以检查 patternfile
中的行是否匹配且没有中断。
diff --left-column -y patternfile targetfile | grep '(' -A1 -B1
> line 2
line 3 (
line 4 (
line 5 (
> line 6
然后省略第一行和最后一行:
diff --left-column -y patternfile targetfile | grep '(' -A1 -B1 | tail -n +2 | head -n -1
line 3 (
line 4 (
line 5 (
添加一些代码来检查行数是否与 patternfile
中的行数匹配:
if [ $(diff --left-column -y patternfile targetfile | grep '(' -A1 -B1 | tail -n +2 | head -n -1 | grep '(' | wc -l) == $(cat patternfile | wc -l) ]; then echo "ok"; else echo "error"; fi
ok
要将其与 return-code 一起使用,可以这样创建脚本:
#!/bin/bash
patternfile=
targetfile=
if [ $(diff --left-column -y $patternfile $targetfile | grep '(' -A1 -B1 | tail -n +2 | head -n -1 | grep '(' | wc -l) == $(cat $patternfile | wc -l) ];
then
exit 0;
else
exit 1;
fi
测试(当上面的脚本命名为comparepatterns
):
$ comparepatterns patternfile targgetfile
echo $?
0
awk 中的另一个解决方案:
echo $(awk 'FNR==NR{ a[[=10=]]; next}{ x=([=10=] in a)?x+1:0 }x==length(a){ print "OK" }' patternfile targetfile )
这个returns"OK"如果有匹配。
最简单的方法是使用滑动 window。首先读取模式文件,然后是要搜索的文件。
(FNR==NR) { a[FNR]=[=10=]; n=FNR; next }
{ b[FNR]=[=10=] }
(FNR >= n) { for(i=1; i<=n;++i) if (a[i] != b[FNR-n+i]) { delete b[FNR-n+1]; next}}
{ print "match at", FNR-n+1}
{ r=1}
END{ exit !r}
你称之为
awk -f script.awk patternFile searchFile
你没有说你是想要正则表达式匹配还是字符串匹配,我们无法判断,因为你将搜索文件命名为 "patternfile" 并且 "pattern" 可能意味着任何东西,而且在某一时刻你暗示你想做一个字符串匹配(check if a multi-line _string_ exists
)但是你使用的是 grep 和 pcregpre 没有规定的字符串参数而不是正则表达式匹配。
在任何情况下,这些都可以在任何 shell UNIX 盒子:
对于正则表达式匹配:
$ cat tst.awk
NR==FNR { pat = pat [=10=] ORS; next }
{ tgt = tgt [=10=] ORS }
END {
while ( match(tgt,pat) ) {
printf "%s", substr(tgt,RSTART,RLENGTH)
tgt = substr(tgt,RSTART+RLENGTH)
}
}
$ awk -f tst.awk patternfile targetfile
line 3
line 4
line 5
对于字符串匹配:
$ cat tst.awk
NR==FNR { pat = pat [=11=] ORS; next }
{ tgt = tgt [=11=] ORS }
END {
lgth = length(pat)
while ( beg = index(tgt,pat) ) {
printf "%s", substr(tgt,beg,lgth)
tgt = substr(tgt,beg+lgth)
}
}
$ awk -f tst.awk patternfile targetfile
line 3
line 4
line 5
话虽如此,如果您对模式文件内容的正则表达式匹配和反斜杠解释没有问题,则可以使用 GNU awk 执行以下操作(因此 \t
被视为文字制表符):
$ awk -v RS="$(cat patternfile)" 'RT!=""{print RT}' targetfile
line 3
line 4
line 5
或使用 GNU grep:
$ grep -zo "$(cat patternfile)" targetfile | tr '[=13=]' '\n'
line 3
line 4
line 5
还有许多其他选项,具体取决于您真正尝试进行的匹配类型以及您可用的工具版本。
根据 Cyrus, who pointed to How to know if a text file is a subset of another 的评论,以下 Python one-liner 可以解决问题
python -c "content=open('content').read(); target=open('target').read(); exit(0 if content in target else 1);"
除非您谈论的是 10 GB+
,否则这是一个基于 awk
的解决方案,既快速又干净:
mawk '{ exit NF==NR }' RS='^$' FS="${multiline_pattern}"
- 该模式仅存在于文件中
"${m2p}"
- 嵌入在 1st 测试的 multi-file 管道中,
- 但是没有 第二个一个
目前,此解决方案不会自动处理需要正则表达式 meta-character 转义的实例。按您认为合适的方式进行更改。
除非该模式出现得太频繁,否则一次完成所有操作甚至可以节省时间,而不必检查 line-by-line,包括在某些临时模式中沿途保存行 space .
NR
总是 1
因为 RS
被强制到输入的尾端。 NF
仅在找到模式时才大于 1。通过评估 exit NF == NR
,它反转匹配,从而匹配 posix
退出代码的结构。
% echo; ( time ( \
\
echo "\n\n multi-line-pattern :: \n\n " \
"-------------\n${multiline_pattern}\n" \
" -----------\n\n " \
"$( nice gcat "${m2m}" "${m3m}" "${m3l}" "${m2p}" \
"${m3r}" "${m3supp}" "${m3t}" | pvE0 \
\
| mawk2 '{ exit NF == NR
}' RS = '^$' \
FS = "${multiline_pattern}" \
\
) exit code : ${?} " ) ) | ecp
in0: 3.10GiB 0:00:01 [2.89GiB/s] [2.89GiB/s] [ <=> ]
( echo ; ) 0.77s user 1.74s system 110% cpu 2.281 total
multi-line-pattern ::
-------------
77138=1159=M
77138=1196=M
77138=1251=M
77138=1252=M
77138=4951=M
77138=16740=M
77138=71501=M
-----------
exit code : 0
% echo; ( time ( \
\
echo "\n\n multi-line-pattern :: \n\n " \
"-------------\n${multiline_pattern}\n" \
" -----------\n\n " \
"$( nice gcat "${m2m}" "${m3m}" "${m3l}" \
"${m3r}" "${m3supp}" "${m3t}" | pvE0 \
\
| mawk2 '{ exit NF == NR
}' RS = '^$' \
FS = "${multiline_pattern}" \
\
) exit code : ${?} " ) ) | ecp
in0: 2.95GiB 0:00:01 [2.92GiB/s] [2.92GiB/s] [ <=> ]
( echo ; ) 0.64s user 1.65s system 110% cpu 2.074 total
multi-line-pattern ::
-------------
77138=1159=M
77138=1196=M
77138=1251=M
77138=1252=M
77138=4951=M
77138=16740=M
77138=71501=M
-----------
exit code : 1
如果您的模式是完整文件,那么类似这样的东西 - 即使将完整文件用作单个巨大的 153 MB
模式,与 [=24= 相比,它的完成时间不到 2.4 secs
] 输入。
echo
( time ( nice gcat "${m2m}" "${m3m}" "${m3l}" "${m3r}" "${m3supp}" "${m3t}" | pvE0 \
\
| mawk2 -v pattern_file="${m2p}" '
BEGIN {
RS = "^$"
getline FS < pattern_file
close(pattern_file)
} END {
exit NF == NR }' ; echo "\n\n exit code :: $?\n\n" ))|ecp;
du -csh "${m2p}" ;
( time ( nice gcat "${m2m}" "${m3m}" "${m3l}" \
"${m2p}" "${m3r}" "${m3supp}" "${m3t}" | pvE0 \
\
| mawk2 -v pattern_file="${m2p}" '
BEGIN {
RS = "^$"
getline FS < pattern_file
close(pattern_file)
} END {
exit NF == NR }' ; echo "\n\n exit code :: $?\n\n" ))|ecp;
in0: 2.95GiB 0:00:01 [2.58GiB/s] [2.58GiB/s] [ <=> ]
( nice gcat "${m2m}" "${m3m}" "${m3l}" "${m3r}" "${m3supp}" "${m3t}" | pvE 0.)
0.82s user 1.71s system 111% cpu 2.260 total
exit code :: 1
153M /Users/************/m2map_main.txt
153M total
in0: 3.10GiB 0:00:01 [2.56GiB/s] [2.56GiB/s] [ <=> ]
( nice gcat "${m2m}" "${m3m}" "${m3l}" "${m2p}" "${m3r}" "${m3supp}" "${m3t}")
0.83s user 1.79s system 112% cpu 2.339 total
exit code :: 0
我正在尝试使用常用 bash 命令(grep、awk、...)检查文件中是否存在多行字符串。
我想要一个包含几行、普通行而不是模式的文件,它应该存在于另一个文件中,并创建一个命令(序列)来检查它是否存在。如果 grep
可以接受任意多行模式,我会用类似于
grep "`cat contentfile`" targetfile
与 grep 一样,我希望能够从命令中检查退出代码。我对输出并不感兴趣。实际上没有输出是首选,因为从那时起我不必通过管道传输到 /dev/null
.
我已经搜索过提示,但无法提供任何合适的搜索结果。有 How can I search for a multiline pattern in a file?,但那是关于模式匹配的。
我找到了 pcre2grep
,但需要使用 "standard" *nix 工具。
示例:
内容文件:
line 3
line 4
line 5
目标文件:
line 1
line 2
line 3
line 4
line 5
line 6
这应该与 return 0 匹配,因为在目标文件中找到了内容文件中的行序列(以完全相同的顺序)。
编辑:很抱歉不清楚 "pattern" 与 "string" 的比较以及 "output" 与 "exit code" 在这个问题的先前版本中。
编辑: 由于 OP 需要真或假(是或否)形式的命令结果,因此编辑命令现在的方式(在 GNU awk
中创建和测试)。
awk -v message="yes" 'FNR==NR{a[[=10=]];next} ([=10=] in a){if((FNR-1)==prev){b[++k]=[=10=]} else {delete b;k=""}} {prev=FNR}; END{if(length(b)>0){print message}}' patternfile targetfile
能否请您尝试以下,用给定的样本进行测试,如果它们在目标文件中以相同的顺序出现,它应该打印模式文件中的所有连续行(此代码中连续行的计数应至少为 2)。
awk '
FNR==NR{
a[[=11=]]
next
}
([=11=] in a){
if((FNR-1)==prev){
b[++k]=[=11=]
}
else{
delete b
k=""
}
}
{
prev=FNR
}
END{
for(j=1;j<=k;j++){
print b[j]
}
}' patternfile targetfile
说明: 在此处添加对上述代码的说明。
awk ' ##Starting awk program here.
FNR==NR{ ##FNR==NR will be TRUE when first Input_file is being read.
a[[=12=]] ##Creating an array a with index [=12=].
next ##next will skip all further statements from here.
}
([=12=] in a){ ##Statements from here will will be executed when 2nd Input_file is being read, checking if current line is present in array a.
if((FNR-1)==prev){ ##Checking condition if prev variable is equal to FNR-1 value then do following.
b[++k]=[=12=] ##Creating an array named b whose index is variable k whose value is increment by 1 each time it comes here.
}
else{ ##Mentioning else condition here.
delete b ##Deleting array b here.
k="" ##Nullifying k here.
}
}
{
prev=FNR ##Setting prev value as FNR value here.
}
END{ ##Starting END section of this awk program here.
for(j=1;j<=k;j++){ ##Starting a for loop here.
print b[j] ##Printing value of array b whose index is variable j here.
}
}' patternfile targetfile ##mentioning Input_file names here.
一个one-liner:
$ if [ $(diff --left-column -y patternfile targetfile | grep '(' -A1 -B1 | tail -n +2 | head -n -1 | wc -l) == $(cat patternfile | wc -l) ]; then echo "ok"; else echo "error"; fi
解释:
首先是使用diff
比较两个文件:
diff --left-column -y patternfile targetfile
> line 1
> line 2
line 3 (
line 4 (
line 5 (
> line 6
然后过滤以仅显示有趣的行,即“(”行,加上匹配前后的额外 1 行,以检查 patternfile
中的行是否匹配且没有中断。
diff --left-column -y patternfile targetfile | grep '(' -A1 -B1
> line 2
line 3 (
line 4 (
line 5 (
> line 6
然后省略第一行和最后一行:
diff --left-column -y patternfile targetfile | grep '(' -A1 -B1 | tail -n +2 | head -n -1
line 3 (
line 4 (
line 5 (
添加一些代码来检查行数是否与 patternfile
中的行数匹配:
if [ $(diff --left-column -y patternfile targetfile | grep '(' -A1 -B1 | tail -n +2 | head -n -1 | grep '(' | wc -l) == $(cat patternfile | wc -l) ]; then echo "ok"; else echo "error"; fi
ok
要将其与 return-code 一起使用,可以这样创建脚本:
#!/bin/bash
patternfile=
targetfile=
if [ $(diff --left-column -y $patternfile $targetfile | grep '(' -A1 -B1 | tail -n +2 | head -n -1 | grep '(' | wc -l) == $(cat $patternfile | wc -l) ];
then
exit 0;
else
exit 1;
fi
测试(当上面的脚本命名为comparepatterns
):
$ comparepatterns patternfile targgetfile
echo $?
0
awk 中的另一个解决方案:
echo $(awk 'FNR==NR{ a[[=10=]]; next}{ x=([=10=] in a)?x+1:0 }x==length(a){ print "OK" }' patternfile targetfile )
这个returns"OK"如果有匹配。
最简单的方法是使用滑动 window。首先读取模式文件,然后是要搜索的文件。
(FNR==NR) { a[FNR]=[=10=]; n=FNR; next }
{ b[FNR]=[=10=] }
(FNR >= n) { for(i=1; i<=n;++i) if (a[i] != b[FNR-n+i]) { delete b[FNR-n+1]; next}}
{ print "match at", FNR-n+1}
{ r=1}
END{ exit !r}
你称之为
awk -f script.awk patternFile searchFile
你没有说你是想要正则表达式匹配还是字符串匹配,我们无法判断,因为你将搜索文件命名为 "patternfile" 并且 "pattern" 可能意味着任何东西,而且在某一时刻你暗示你想做一个字符串匹配(check if a multi-line _string_ exists
)但是你使用的是 grep 和 pcregpre 没有规定的字符串参数而不是正则表达式匹配。
在任何情况下,这些都可以在任何 shell UNIX 盒子:
对于正则表达式匹配:
$ cat tst.awk
NR==FNR { pat = pat [=10=] ORS; next }
{ tgt = tgt [=10=] ORS }
END {
while ( match(tgt,pat) ) {
printf "%s", substr(tgt,RSTART,RLENGTH)
tgt = substr(tgt,RSTART+RLENGTH)
}
}
$ awk -f tst.awk patternfile targetfile
line 3
line 4
line 5
对于字符串匹配:
$ cat tst.awk
NR==FNR { pat = pat [=11=] ORS; next }
{ tgt = tgt [=11=] ORS }
END {
lgth = length(pat)
while ( beg = index(tgt,pat) ) {
printf "%s", substr(tgt,beg,lgth)
tgt = substr(tgt,beg+lgth)
}
}
$ awk -f tst.awk patternfile targetfile
line 3
line 4
line 5
话虽如此,如果您对模式文件内容的正则表达式匹配和反斜杠解释没有问题,则可以使用 GNU awk 执行以下操作(因此 \t
被视为文字制表符):
$ awk -v RS="$(cat patternfile)" 'RT!=""{print RT}' targetfile
line 3
line 4
line 5
或使用 GNU grep:
$ grep -zo "$(cat patternfile)" targetfile | tr '[=13=]' '\n'
line 3
line 4
line 5
还有许多其他选项,具体取决于您真正尝试进行的匹配类型以及您可用的工具版本。
根据 Cyrus, who pointed to How to know if a text file is a subset of another 的评论,以下 Python one-liner 可以解决问题
python -c "content=open('content').read(); target=open('target').read(); exit(0 if content in target else 1);"
除非您谈论的是 10 GB+
,否则这是一个基于 awk
的解决方案,既快速又干净:
mawk '{ exit NF==NR }' RS='^$' FS="${multiline_pattern}"
- 该模式仅存在于文件中
"${m2p}"
- 嵌入在 1st 测试的 multi-file 管道中,
- 但是没有 第二个一个
目前,此解决方案不会自动处理需要正则表达式 meta-character 转义的实例。按您认为合适的方式进行更改。
除非该模式出现得太频繁,否则一次完成所有操作甚至可以节省时间,而不必检查 line-by-line,包括在某些临时模式中沿途保存行 space .
NR
总是 1
因为 RS
被强制到输入的尾端。 NF
仅在找到模式时才大于 1。通过评估 exit NF == NR
,它反转匹配,从而匹配 posix
退出代码的结构。
% echo; ( time ( \
\
echo "\n\n multi-line-pattern :: \n\n " \
"-------------\n${multiline_pattern}\n" \
" -----------\n\n " \
"$( nice gcat "${m2m}" "${m3m}" "${m3l}" "${m2p}" \
"${m3r}" "${m3supp}" "${m3t}" | pvE0 \
\
| mawk2 '{ exit NF == NR
}' RS = '^$' \
FS = "${multiline_pattern}" \
\
) exit code : ${?} " ) ) | ecp
in0: 3.10GiB 0:00:01 [2.89GiB/s] [2.89GiB/s] [ <=> ]
( echo ; ) 0.77s user 1.74s system 110% cpu 2.281 total
multi-line-pattern ::
-------------
77138=1159=M
77138=1196=M
77138=1251=M
77138=1252=M
77138=4951=M
77138=16740=M
77138=71501=M
-----------
exit code : 0
% echo; ( time ( \
\
echo "\n\n multi-line-pattern :: \n\n " \
"-------------\n${multiline_pattern}\n" \
" -----------\n\n " \
"$( nice gcat "${m2m}" "${m3m}" "${m3l}" \
"${m3r}" "${m3supp}" "${m3t}" | pvE0 \
\
| mawk2 '{ exit NF == NR
}' RS = '^$' \
FS = "${multiline_pattern}" \
\
) exit code : ${?} " ) ) | ecp
in0: 2.95GiB 0:00:01 [2.92GiB/s] [2.92GiB/s] [ <=> ]
( echo ; ) 0.64s user 1.65s system 110% cpu 2.074 total
multi-line-pattern ::
-------------
77138=1159=M
77138=1196=M
77138=1251=M
77138=1252=M
77138=4951=M
77138=16740=M
77138=71501=M
-----------
exit code : 1
如果您的模式是完整文件,那么类似这样的东西 - 即使将完整文件用作单个巨大的 153 MB
模式,与 [=24= 相比,它的完成时间不到 2.4 secs
] 输入。
echo
( time ( nice gcat "${m2m}" "${m3m}" "${m3l}" "${m3r}" "${m3supp}" "${m3t}" | pvE0 \
\
| mawk2 -v pattern_file="${m2p}" '
BEGIN {
RS = "^$"
getline FS < pattern_file
close(pattern_file)
} END {
exit NF == NR }' ; echo "\n\n exit code :: $?\n\n" ))|ecp;
du -csh "${m2p}" ;
( time ( nice gcat "${m2m}" "${m3m}" "${m3l}" \
"${m2p}" "${m3r}" "${m3supp}" "${m3t}" | pvE0 \
\
| mawk2 -v pattern_file="${m2p}" '
BEGIN {
RS = "^$"
getline FS < pattern_file
close(pattern_file)
} END {
exit NF == NR }' ; echo "\n\n exit code :: $?\n\n" ))|ecp;
in0: 2.95GiB 0:00:01 [2.58GiB/s] [2.58GiB/s] [ <=> ]
( nice gcat "${m2m}" "${m3m}" "${m3l}" "${m3r}" "${m3supp}" "${m3t}" | pvE 0.)
0.82s user 1.71s system 111% cpu 2.260 total
exit code :: 1
153M /Users/************/m2map_main.txt
153M total
in0: 3.10GiB 0:00:01 [2.56GiB/s] [2.56GiB/s] [ <=> ]
( nice gcat "${m2m}" "${m3m}" "${m3l}" "${m2p}" "${m3r}" "${m3supp}" "${m3t}")
0.83s user 1.79s system 112% cpu 2.339 total
exit code :: 0