检查文件中的多行内容

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