如何使用grep在多行中找到模式?

时间:2022-02-26 22:58:12

I want to find files that have "abc" AND "efg" in that order, and those two strings are on different lines in that file. Eg: a file with content:

我想要找到在那个顺序中有"abc"和"efg"的文件,这两个字符串在那个文件中的不同行。有内容的文件:

blah blah..
blah blah..
blah abc blah
blah blah..
blah blah..
blah blah..
blah efg blah blah
blah blah..
blah blah..

Should be matched.

应该匹配。

21 个解决方案

#1


163  

Grep is not sufficient for this operation.

Grep不适合这个操作。

pcregrep which is found in most of the modern Linux systems can be used as

在大多数现代Linux系统中都可以使用pcregrep。

pcregrep -M  'abc.*(\n|.)*efg' test.txt

There is a newer pcre2grep also. Both are provided by the PCRE project.

还有一个新的pcre2grep。两者都由PCRE项目提供。

pcre2grep is available for Mac OS X via Mac Ports as part of port pcre2:

pcre2grep可通过Mac端口作为pcre2端口的一部分使用Mac OS X:

% sudo port install pcre2 

and via Homebrew as:

通过自制程序:

% brew install pcre

#2


102  

I'm not sure if it is possible with grep, but sed makes it very easy:

我不确定grep是否可行,但sed很容易:

sed -e '/abc/,/efg/!d' [file-with-content]

#3


47  

Here is a solution inspired by this answer:

以下是这个答案的启发:

  • if 'abc' and 'efg' can be on the same line:

    如果“abc”和“efg”可以在同一条线上:

    grep -zl 'abc.*efg' <your list of files>
    
  • if 'abc' and 'efg' must be on different lines:

    如果“abc”和“efg”必须在不同的线上:

    grep -Pzl '(?s)abc.*\n.*efg' <your list of files>
    

Params:

参数:

  • -z Treat the input as a set of lines, each terminated by a zero byte instead of a newline. i.e. grep threats the input as a one big line.

    -z将输入视为一组行,每一行都以0字节而不是换行符结束。即grep威胁输入为一条大线。

  • -l print name of each input file from which output would normally have been printed.

    -l打印每个输入文件的名称,输出通常会打印出来。

  • (?s) activate PCRE_DOTALL, which means that '.' finds any character or newline.

    激活PCRE_DOTALL,这意味着。发现任何字符或换行符。

#4


27  

sed should suffice as poster LJ stated above,

sed应该就像上面所说的海报LJ,

instead of !d you can simply use p to print:

你可以简单地用p来打印:

sed -n '/abc/,/efg/p' file

#5


10  

I relied heavily on pcregrep, but with newer grep you do not need to install pcregrep for many of its features. Just use grep -P.

我非常依赖pcregrep,但是使用新的grep,您不需要为它的许多特性安装pcregrep。只使用grep - p。

In the example of the OP's question, I think the following options work nicely, with the second best matching how I understand the question:

在OP的问题示例中,我认为以下选项很好地工作,第二个最佳匹配是我如何理解这个问题:

grep -Pzo "abc(.|\n)*efg" /tmp/tes*
grep -Pzl "abc(.|\n)*efg" /tmp/tes*

I copied the text as /tmp/test1 and deleted the 'g' and saved as /tmp/test2. Here is the output showing that the first shows the matched string and the second shows only the filename (typical -o is to show match and typical -l is to show only filename). Note that the 'z' is necessary for multiline and the '(.|\n)' means to match either 'anything other than newline' or 'newline' - i.e. anything:

我将文本复制为/tmp/test1,并删除了“g”并保存为/tmp/test2。这里显示的输出显示,第一个显示匹配的字符串,第二个显示的是文件名(典型的-o是显示匹配,典型的-l是显示文件名)。请注意,“z”是多行和“(.|\n)”的意思,意思是“除了换行”或“换行”以外的任何东西,即任何东西:

user@host:~$ grep -Pzo "abc(.|\n)*efg" /tmp/tes*
/tmp/test1:abc blah
blah blah..
blah blah..
blah blah..
blah efg
user@host:~$ grep -Pzl "abc(.|\n)*efg" /tmp/tes*
/tmp/test1

To determine if your version is new enough, run man grep and see if something similar to this appears near the top:

要确定你的版本是否足够新,运行man grep,看看是否有类似的东西出现在顶部附近:

   -P, --perl-regexp
          Interpret  PATTERN  as a Perl regular expression (PCRE, see
          below).  This is highly experimental and grep -P may warn of
          unimplemented features.

That is from GNU grep 2.10.

这是来自GNU grep2.10。

#6


9  

This can be done easily by first using tr to replace the newlines with some other character:

这可以很容易地完成,首先使用tr来替换换行和其他字符:

tr '\n' '\a' | grep 'abc.*def' | tr '\a' '\n'

Here, I am using the alarm character, \a (ASCII 7) in place of a newline. This is almost never found in your text, and grep can match it with a ., or match it specifically with \a.

在这里,我使用了警报字符\a (ASCII 7)代替了换行符。这在您的文本中几乎找不到,grep可以与a匹配,或者与\a特别匹配。

#7


6  

You can do that very easily if you can use Perl.

如果可以使用Perl,您可以很容易地做到这一点。

perl -ne 'if (/abc/) { $abc = 1; next }; print "Found in $ARGV\n" if ($abc && /efg/); }' yourfilename.txt

You can do that with a single regular expression too, but that involves taking the entire contents of the file into a single string, which might end up taking up too much memory with large files. For completeness, here is that method:

您也可以使用单个正则表达式来实现这一点,但这需要将文件的全部内容放入单个字符串中,这可能会占用大量的内存,而这些内存可能会占用大量的文件。为了完整性,这里有一个方法:

perl -e '@lines = <>; $content = join("", @lines); print "Found in $ARGV\n" if ($content =~ /abc.*efg/s);' yourfilename.txt

#8


5  

I don't know how I would do that with grep, but I would do something like this with awk:

我不知道怎么用grep来做这个,但我会用awk做这样的事情:

awk '/abc/{ln1=NR} /efg/{ln2=NR} END{if(ln1 && ln2 && ln1 < ln2){print "found"}else{print "not found"}}' foo

You need to be careful how you do this, though. Do you want the regex to match the substring or the entire word? add \w tags as appropriate. Also, while this strictly conforms to how you stated the example, it doesn't quite work when abc appears a second time after efg. If you want to handle that, add an if as appropriate in the /abc/ case etc.

不过,你要小心你是如何做到这一点的。您想要正则表达式匹配子字符串还是整个单词?在适当的时候添加\w标签。而且,虽然这严格遵循了您的示例,但是当abc在efg之后第二次出现时,它并不是很有效。如果你想处理这个问题,请在/abc/ case等中添加一个If。

#9


4  

awk one-liner:

awk一行程序:

awk '/abc/,/efg/' [file-with-content]

#10


3  

I released a grep alternative a few days ago that does support this directly, either via multiline matching or using conditions - hopefully it is useful for some people searching here. This is what the commands for the example would look like:

几天前,我发布了一个grep选项,它可以直接支持这一功能,可以通过多行匹配或使用条件——希望它对在这里搜索的人有用。这是示例的命令如下所示:

Multiline: sift -lm 'abc.*efg' testfile
Conditions: sift -l 'abc' testfile --followed-by 'efg'

多行:筛选- lm的abc。*efg' testfile Conditions: sift -l 'abc' testfile——以下为efg

You could also specify that 'efg' has to follow 'abc' within a certain number of lines:
sift -l 'abc' testfile --followed-within 5:'efg'

您还可以指定“efg”必须在一定数量的行中遵循“abc”:sift -l 'abc'的testfile——后面是5:“efg”

You can find more information on sift-tool.org.

你可以在sift-tool.org上找到更多的信息。

#11


2  

Sadly, you can't. From the grep docs:

可悲的是,你不能。grep的文档:

grep searches the named input FILEs (or standard input if no files are named, or if a single hyphen-minus (-) is given as file name) for lines containing a match to the given PATTERN.

grep搜索指定的输入文件(如果没有命名文件,或者如果一个连字符- - -(-)作为文件名),用于包含与给定模式匹配的行。

#12


2  

While the sed option is the simplest and easiest, LJ's one-liner is sadly not the most portable. Those stuck with a version of the C Shell will need to escape their bangs:

虽然sed选项是最简单、最简单的,但遗憾的是LJ的一行程序并不是最便携的。那些卡在C外壳上的人需要逃离他们的刘海:

sed -e '/abc/,/efg/\!d' [file]

This unfortunately does not work in bash et al.

不幸的是,这在bash等中不起作用。

#13


2  

If you are willing to use contexts, this could be achieved by typing

如果您愿意使用上下文,可以通过输入来实现。

grep -A 500 abc test.txt | grep -B 500 efg

This will display everything between "abc" and "efg", as long as they are within 500 lines of each other.

这将显示“abc”和“efg”之间的所有内容,只要它们相距500行以内。

#14


2  

If you need both words are close each other, for example no more than 3 lines, you can do this:

如果你需要两个单词都是相近的,例如不超过3行,你可以这样做:

find . -exec grep -Hn -C 3 "abc" {} \; | grep -C 3 "efg"

Same example but filtering only *.txt files:

同样的例子,但只过滤*。txt文件:

find . -name *.txt -exec grep -Hn -C 3 "abc" {} \; | grep -C 3 "efg"

And also you can replace grep command with egrep command if you want also find with regular expressions.

而且,如果您想要找到正则表达式,也可以用白鹭命令替换grep命令。

#15


1  

#!/bin/bash
shopt -s nullglob
for file in *
do
 r=$(awk '/abc/{f=1}/efg/{g=1;exit}END{print g&&f ?1:0}' file)
 if [ "$r" -eq 1 ];then
   echo "Found pattern in $file"
 else
   echo "not found"
 fi
done

#16


1  

you can use grep incase you are not keen in the sequence of the pattern.

您可以使用grep incase您不喜欢模式的序列。

grep -l "pattern1" filepattern*.* | xargs grep "pattern2"

example

例子

grep -l "vector" *.cpp | xargs grep "map"

grep -l will find all the files which matches the first pattern, and xargs will grep for the second pattern. Hope this helps.

grep -l将找到与第一个模式匹配的所有文件,xargs将为第二个模式寻找grep。希望这个有帮助。

#17


1  

With silver searcher:

用银搜索器:

ag 'abc.*(\n|.)*efg'

similar to ring bearer's answer, but with ag instead. Speed advantages of silver searcher could possibly shine here.

类似于戒指持有者的答案,但与ag相反。银色搜索器的速度优势可能会在这里发光。

#18


0  

As an alternative to Balu Mohan's answer, it is possible to enforce the order of the patterns using only grep, head and tail:

作为对巴鲁·莫汉的回答的一种替代,我们可以只使用grep、head和tail来执行模式的顺序:

for f in FILEGLOB; do tail $f -n +$(grep -n "pattern1" $f | head -n1 | cut -d : -f 1) 2>/dev/null | grep "pattern2" &>/dev/null && echo $f; done

This one isn't very pretty, though. Formatted more readably:

不过这个不太漂亮。格式化更可读:

for f in FILEGLOB; do
    tail $f -n +$(grep -n "pattern1" $f | head -n1 | cut -d : -f 1) 2>/dev/null \
    | grep -q "pattern2" \
    && echo $f
done

This will print the names of all files where "pattern2" appears after "pattern1", or where both appear on the same line:

这将打印在“pattern1”之后出现“pattern2”的所有文件的名称,或者它们都出现在同一条线上:

$ echo "abc
def" > a.txt
$ echo "def
abc" > b.txt
$ echo "abcdef" > c.txt; echo "defabc" > d.txt
$ for f in *.txt; do tail $f -n +$(grep -n "abc" $f | head -n1 | cut -d : -f 1) 2>/dev/null | grep -q "def" && echo $f; done
a.txt
c.txt
d.txt

Explanation

  • tail -n +i - print all lines after the ith, inclusive
  • 尾-n +i -打印出第ith后的所有行,包括。
  • grep -n - prepend matching lines with their line numbers
  • 与他们的行号匹配的grep -n - prepend匹配行。
  • head -n1 - print only the first row
  • 头-n1 -打印只有第一行。
  • cut -d : -f 1 - print the first cut column using : as the delimiter
  • cut -d: - f1 -打印第一个剪切列:作为分隔符。
  • 2>/dev/null - silence tail error output that occurs if the $() expression returns empty
  • 如果$()表达式返回空,则会出现>/dev/null -静默尾部错误输出。
  • grep -q - silence grep and return immediately if a match is found, since we are only interested in the exit code
  • grep -q -沉默grep,如果找到匹配,立即返回,因为我们只对退出代码感兴趣。

#19


0  

This should work too?!

这应该工作吗? !

perl -lpne 'print $ARGV if /abc.*?efg/s' file_list

$ARGV contains the name of the current file when reading from file_list /s modifier searches across newline.

$ARGV包含当前文件的名称,从file_list /s修饰符搜索到换行。

#20


0  

The filepattern *.sh is important to prevent directories to be inspected. Of course some test could prevent that too.

filepattern *。sh对于防止目录被检查很重要。当然,一些测试也可以防止这种情况发生。

for f in *.sh
do
  a=$( grep -n -m1 abc $f )
  test -n "${a}" && z=$( grep -n efg $f | tail -n 1) || continue 
  (( ((${z/:*/}-${a/:*/})) > 0 )) && echo $f
done

The

grep -n -m1 abc $f 

searches maximum 1 matching and returns (-n) the linenumber. If a match was found (test -n ...) find the last match of efg (find all and take the last with tail -n 1).

搜索最大匹配和返回(-n)的linenumber。如果找到匹配(test -n…),找到efg的最后一个匹配(找到所有的,最后用tail -n 1)。

z=$( grep -n efg $f | tail -n 1)

else continue.

其他的继续。

Since the result is something like 18:foofile.sh String alf="abc"; we need to cut away from ":" till end of line.

因为结果大约是18:foofile。sh字符串阿尔夫=“abc”;我们需要从“:”中删去。

((${z/:*/}-${a/:*/}))

Should return a positive result if the last match of the 2nd expression is past the first match of the first.

如果第2个表达式的最后一个匹配通过第一个匹配,则返回一个正结果。

Then we report the filename echo $f.

然后我们报告文件名echo $f。

#21


-2  

This should work:

这应该工作:

cat FILE | egrep 'abc|efg'

If there is more than one match you can filter out using grep -v

如果有多个匹配,您可以使用grep -v过滤掉。

#1


163  

Grep is not sufficient for this operation.

Grep不适合这个操作。

pcregrep which is found in most of the modern Linux systems can be used as

在大多数现代Linux系统中都可以使用pcregrep。

pcregrep -M  'abc.*(\n|.)*efg' test.txt

There is a newer pcre2grep also. Both are provided by the PCRE project.

还有一个新的pcre2grep。两者都由PCRE项目提供。

pcre2grep is available for Mac OS X via Mac Ports as part of port pcre2:

pcre2grep可通过Mac端口作为pcre2端口的一部分使用Mac OS X:

% sudo port install pcre2 

and via Homebrew as:

通过自制程序:

% brew install pcre

#2


102  

I'm not sure if it is possible with grep, but sed makes it very easy:

我不确定grep是否可行,但sed很容易:

sed -e '/abc/,/efg/!d' [file-with-content]

#3


47  

Here is a solution inspired by this answer:

以下是这个答案的启发:

  • if 'abc' and 'efg' can be on the same line:

    如果“abc”和“efg”可以在同一条线上:

    grep -zl 'abc.*efg' <your list of files>
    
  • if 'abc' and 'efg' must be on different lines:

    如果“abc”和“efg”必须在不同的线上:

    grep -Pzl '(?s)abc.*\n.*efg' <your list of files>
    

Params:

参数:

  • -z Treat the input as a set of lines, each terminated by a zero byte instead of a newline. i.e. grep threats the input as a one big line.

    -z将输入视为一组行,每一行都以0字节而不是换行符结束。即grep威胁输入为一条大线。

  • -l print name of each input file from which output would normally have been printed.

    -l打印每个输入文件的名称,输出通常会打印出来。

  • (?s) activate PCRE_DOTALL, which means that '.' finds any character or newline.

    激活PCRE_DOTALL,这意味着。发现任何字符或换行符。

#4


27  

sed should suffice as poster LJ stated above,

sed应该就像上面所说的海报LJ,

instead of !d you can simply use p to print:

你可以简单地用p来打印:

sed -n '/abc/,/efg/p' file

#5


10  

I relied heavily on pcregrep, but with newer grep you do not need to install pcregrep for many of its features. Just use grep -P.

我非常依赖pcregrep,但是使用新的grep,您不需要为它的许多特性安装pcregrep。只使用grep - p。

In the example of the OP's question, I think the following options work nicely, with the second best matching how I understand the question:

在OP的问题示例中,我认为以下选项很好地工作,第二个最佳匹配是我如何理解这个问题:

grep -Pzo "abc(.|\n)*efg" /tmp/tes*
grep -Pzl "abc(.|\n)*efg" /tmp/tes*

I copied the text as /tmp/test1 and deleted the 'g' and saved as /tmp/test2. Here is the output showing that the first shows the matched string and the second shows only the filename (typical -o is to show match and typical -l is to show only filename). Note that the 'z' is necessary for multiline and the '(.|\n)' means to match either 'anything other than newline' or 'newline' - i.e. anything:

我将文本复制为/tmp/test1,并删除了“g”并保存为/tmp/test2。这里显示的输出显示,第一个显示匹配的字符串,第二个显示的是文件名(典型的-o是显示匹配,典型的-l是显示文件名)。请注意,“z”是多行和“(.|\n)”的意思,意思是“除了换行”或“换行”以外的任何东西,即任何东西:

user@host:~$ grep -Pzo "abc(.|\n)*efg" /tmp/tes*
/tmp/test1:abc blah
blah blah..
blah blah..
blah blah..
blah efg
user@host:~$ grep -Pzl "abc(.|\n)*efg" /tmp/tes*
/tmp/test1

To determine if your version is new enough, run man grep and see if something similar to this appears near the top:

要确定你的版本是否足够新,运行man grep,看看是否有类似的东西出现在顶部附近:

   -P, --perl-regexp
          Interpret  PATTERN  as a Perl regular expression (PCRE, see
          below).  This is highly experimental and grep -P may warn of
          unimplemented features.

That is from GNU grep 2.10.

这是来自GNU grep2.10。

#6


9  

This can be done easily by first using tr to replace the newlines with some other character:

这可以很容易地完成,首先使用tr来替换换行和其他字符:

tr '\n' '\a' | grep 'abc.*def' | tr '\a' '\n'

Here, I am using the alarm character, \a (ASCII 7) in place of a newline. This is almost never found in your text, and grep can match it with a ., or match it specifically with \a.

在这里,我使用了警报字符\a (ASCII 7)代替了换行符。这在您的文本中几乎找不到,grep可以与a匹配,或者与\a特别匹配。

#7


6  

You can do that very easily if you can use Perl.

如果可以使用Perl,您可以很容易地做到这一点。

perl -ne 'if (/abc/) { $abc = 1; next }; print "Found in $ARGV\n" if ($abc && /efg/); }' yourfilename.txt

You can do that with a single regular expression too, but that involves taking the entire contents of the file into a single string, which might end up taking up too much memory with large files. For completeness, here is that method:

您也可以使用单个正则表达式来实现这一点,但这需要将文件的全部内容放入单个字符串中,这可能会占用大量的内存,而这些内存可能会占用大量的文件。为了完整性,这里有一个方法:

perl -e '@lines = <>; $content = join("", @lines); print "Found in $ARGV\n" if ($content =~ /abc.*efg/s);' yourfilename.txt

#8


5  

I don't know how I would do that with grep, but I would do something like this with awk:

我不知道怎么用grep来做这个,但我会用awk做这样的事情:

awk '/abc/{ln1=NR} /efg/{ln2=NR} END{if(ln1 && ln2 && ln1 < ln2){print "found"}else{print "not found"}}' foo

You need to be careful how you do this, though. Do you want the regex to match the substring or the entire word? add \w tags as appropriate. Also, while this strictly conforms to how you stated the example, it doesn't quite work when abc appears a second time after efg. If you want to handle that, add an if as appropriate in the /abc/ case etc.

不过,你要小心你是如何做到这一点的。您想要正则表达式匹配子字符串还是整个单词?在适当的时候添加\w标签。而且,虽然这严格遵循了您的示例,但是当abc在efg之后第二次出现时,它并不是很有效。如果你想处理这个问题,请在/abc/ case等中添加一个If。

#9


4  

awk one-liner:

awk一行程序:

awk '/abc/,/efg/' [file-with-content]

#10


3  

I released a grep alternative a few days ago that does support this directly, either via multiline matching or using conditions - hopefully it is useful for some people searching here. This is what the commands for the example would look like:

几天前,我发布了一个grep选项,它可以直接支持这一功能,可以通过多行匹配或使用条件——希望它对在这里搜索的人有用。这是示例的命令如下所示:

Multiline: sift -lm 'abc.*efg' testfile
Conditions: sift -l 'abc' testfile --followed-by 'efg'

多行:筛选- lm的abc。*efg' testfile Conditions: sift -l 'abc' testfile——以下为efg

You could also specify that 'efg' has to follow 'abc' within a certain number of lines:
sift -l 'abc' testfile --followed-within 5:'efg'

您还可以指定“efg”必须在一定数量的行中遵循“abc”:sift -l 'abc'的testfile——后面是5:“efg”

You can find more information on sift-tool.org.

你可以在sift-tool.org上找到更多的信息。

#11


2  

Sadly, you can't. From the grep docs:

可悲的是,你不能。grep的文档:

grep searches the named input FILEs (or standard input if no files are named, or if a single hyphen-minus (-) is given as file name) for lines containing a match to the given PATTERN.

grep搜索指定的输入文件(如果没有命名文件,或者如果一个连字符- - -(-)作为文件名),用于包含与给定模式匹配的行。

#12


2  

While the sed option is the simplest and easiest, LJ's one-liner is sadly not the most portable. Those stuck with a version of the C Shell will need to escape their bangs:

虽然sed选项是最简单、最简单的,但遗憾的是LJ的一行程序并不是最便携的。那些卡在C外壳上的人需要逃离他们的刘海:

sed -e '/abc/,/efg/\!d' [file]

This unfortunately does not work in bash et al.

不幸的是,这在bash等中不起作用。

#13


2  

If you are willing to use contexts, this could be achieved by typing

如果您愿意使用上下文,可以通过输入来实现。

grep -A 500 abc test.txt | grep -B 500 efg

This will display everything between "abc" and "efg", as long as they are within 500 lines of each other.

这将显示“abc”和“efg”之间的所有内容,只要它们相距500行以内。

#14


2  

If you need both words are close each other, for example no more than 3 lines, you can do this:

如果你需要两个单词都是相近的,例如不超过3行,你可以这样做:

find . -exec grep -Hn -C 3 "abc" {} \; | grep -C 3 "efg"

Same example but filtering only *.txt files:

同样的例子,但只过滤*。txt文件:

find . -name *.txt -exec grep -Hn -C 3 "abc" {} \; | grep -C 3 "efg"

And also you can replace grep command with egrep command if you want also find with regular expressions.

而且,如果您想要找到正则表达式,也可以用白鹭命令替换grep命令。

#15


1  

#!/bin/bash
shopt -s nullglob
for file in *
do
 r=$(awk '/abc/{f=1}/efg/{g=1;exit}END{print g&&f ?1:0}' file)
 if [ "$r" -eq 1 ];then
   echo "Found pattern in $file"
 else
   echo "not found"
 fi
done

#16


1  

you can use grep incase you are not keen in the sequence of the pattern.

您可以使用grep incase您不喜欢模式的序列。

grep -l "pattern1" filepattern*.* | xargs grep "pattern2"

example

例子

grep -l "vector" *.cpp | xargs grep "map"

grep -l will find all the files which matches the first pattern, and xargs will grep for the second pattern. Hope this helps.

grep -l将找到与第一个模式匹配的所有文件,xargs将为第二个模式寻找grep。希望这个有帮助。

#17


1  

With silver searcher:

用银搜索器:

ag 'abc.*(\n|.)*efg'

similar to ring bearer's answer, but with ag instead. Speed advantages of silver searcher could possibly shine here.

类似于戒指持有者的答案,但与ag相反。银色搜索器的速度优势可能会在这里发光。

#18


0  

As an alternative to Balu Mohan's answer, it is possible to enforce the order of the patterns using only grep, head and tail:

作为对巴鲁·莫汉的回答的一种替代,我们可以只使用grep、head和tail来执行模式的顺序:

for f in FILEGLOB; do tail $f -n +$(grep -n "pattern1" $f | head -n1 | cut -d : -f 1) 2>/dev/null | grep "pattern2" &>/dev/null && echo $f; done

This one isn't very pretty, though. Formatted more readably:

不过这个不太漂亮。格式化更可读:

for f in FILEGLOB; do
    tail $f -n +$(grep -n "pattern1" $f | head -n1 | cut -d : -f 1) 2>/dev/null \
    | grep -q "pattern2" \
    && echo $f
done

This will print the names of all files where "pattern2" appears after "pattern1", or where both appear on the same line:

这将打印在“pattern1”之后出现“pattern2”的所有文件的名称,或者它们都出现在同一条线上:

$ echo "abc
def" > a.txt
$ echo "def
abc" > b.txt
$ echo "abcdef" > c.txt; echo "defabc" > d.txt
$ for f in *.txt; do tail $f -n +$(grep -n "abc" $f | head -n1 | cut -d : -f 1) 2>/dev/null | grep -q "def" && echo $f; done
a.txt
c.txt
d.txt

Explanation

  • tail -n +i - print all lines after the ith, inclusive
  • 尾-n +i -打印出第ith后的所有行,包括。
  • grep -n - prepend matching lines with their line numbers
  • 与他们的行号匹配的grep -n - prepend匹配行。
  • head -n1 - print only the first row
  • 头-n1 -打印只有第一行。
  • cut -d : -f 1 - print the first cut column using : as the delimiter
  • cut -d: - f1 -打印第一个剪切列:作为分隔符。
  • 2>/dev/null - silence tail error output that occurs if the $() expression returns empty
  • 如果$()表达式返回空,则会出现>/dev/null -静默尾部错误输出。
  • grep -q - silence grep and return immediately if a match is found, since we are only interested in the exit code
  • grep -q -沉默grep,如果找到匹配,立即返回,因为我们只对退出代码感兴趣。

#19


0  

This should work too?!

这应该工作吗? !

perl -lpne 'print $ARGV if /abc.*?efg/s' file_list

$ARGV contains the name of the current file when reading from file_list /s modifier searches across newline.

$ARGV包含当前文件的名称,从file_list /s修饰符搜索到换行。

#20


0  

The filepattern *.sh is important to prevent directories to be inspected. Of course some test could prevent that too.

filepattern *。sh对于防止目录被检查很重要。当然,一些测试也可以防止这种情况发生。

for f in *.sh
do
  a=$( grep -n -m1 abc $f )
  test -n "${a}" && z=$( grep -n efg $f | tail -n 1) || continue 
  (( ((${z/:*/}-${a/:*/})) > 0 )) && echo $f
done

The

grep -n -m1 abc $f 

searches maximum 1 matching and returns (-n) the linenumber. If a match was found (test -n ...) find the last match of efg (find all and take the last with tail -n 1).

搜索最大匹配和返回(-n)的linenumber。如果找到匹配(test -n…),找到efg的最后一个匹配(找到所有的,最后用tail -n 1)。

z=$( grep -n efg $f | tail -n 1)

else continue.

其他的继续。

Since the result is something like 18:foofile.sh String alf="abc"; we need to cut away from ":" till end of line.

因为结果大约是18:foofile。sh字符串阿尔夫=“abc”;我们需要从“:”中删去。

((${z/:*/}-${a/:*/}))

Should return a positive result if the last match of the 2nd expression is past the first match of the first.

如果第2个表达式的最后一个匹配通过第一个匹配,则返回一个正结果。

Then we report the filename echo $f.

然后我们报告文件名echo $f。

#21


-2  

This should work:

这应该工作:

cat FILE | egrep 'abc|efg'

If there is more than one match you can filter out using grep -v

如果有多个匹配,您可以使用grep -v过滤掉。