Answers:
Grep不足以执行此操作。
在大多数现代Linux系统中发现的pcregrep可以用作
pcregrep -M 'abc.*(\n|.)*efg' test.txt
其中-M
,--multiline
允许模式匹配多行
pcre2grep可通过Mac Ports作为Mac OS X 在port的一部分使用pcre2
:
% sudo port install pcre2
并通过Homebrew作为:
% brew install pcre
或pcre2
% brew install pcre2
pcre2grep是也可以在Linux(Ubuntu的18.04+)
$ sudo apt install pcre2-utils # PCRE2
$ sudo apt install pcregrep # Older PCRE
-M, --multiline
允许模式匹配多行。
'abc.*(\n|.)*?efg'
.*
-> 'abc(\n|.)*?efg'
来使正则表达式更短(并且要学究)
我不确定grep是否可能,但是sed使其非常容易:
sed -e '/abc/,/efg/!d' [file-with-content]
sed
,但是如果以前从未见过这样的表达。
这是受此答案启发的解决方案:
如果'abc'和'efg'可以在同一行:
grep -zl 'abc.*efg' <your list of files>
如果“ abc”和“ efg”必须位于不同的行:
grep -Pzl '(?s)abc.*\n.*efg' <your list of files>
参数:
-z
将输入视为一组行,每行以零字节而不是换行符结尾。即grep将输入视为一行。
-l
每个输入文件的打印名称,通常可以从中打印输出。
(?s)
激活PCRE_DOTALL,这意味着“。” 查找任何字符或换行符。
l
。AFAIK没有编号-1
选项。
-z
选项指定使用grep将换行符视为新行,zero byte characters
那么为什么我们需要(?s)
在正则表达式中使用?如果已经是非换行符,是否应该不能.
直接将其匹配?
我严重依赖pcregrep,但是使用较新的grep时,无需安装pcregrep的许多功能。只需使用grep -P
。
在OP的问题示例中,我认为以下选项可以很好地发挥作用,第二种最匹配我对问题的理解方式:
grep -Pzo "abc(.|\n)*efg" /tmp/tes*
grep -Pzl "abc(.|\n)*efg" /tmp/tes*
我将文本复制为/ 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
要确定您的版本是否足够新,请运行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.
那是来自GNU grep 2.10。
首先使用tr
换行符替换其他字符即可轻松完成此操作:
tr '\n' '\a' | grep -o 'abc.*def' | tr '\a' '\n'
在这里,我使用警报字符\a
(ASCII 7)代替换行符。几乎不会在您的文本中找到grep
它,可以将其与匹配,也可以将其与.
专门匹配\a
。
\0
,因此需要grep -a
在\x00
... 上进行匹配……您已帮助我简化了工作!echo $log | tr '\n' '\0' | grep -aoE "Error: .*?\x00Installing .*? has failed\!" | tr '\0' '\n'
现在是echo $log | tr '\n' '\a' | grep -oE "Error: .*?\aInstalling .*? has failed\!" | tr '\a' '\n'
grep -o
。
如果可以使用Perl,则可以非常轻松地做到这一点。
perl -ne 'if (/abc/) { $abc = 1; next }; print "Found in $ARGV\n" if ($abc && /efg/); }' yourfilename.txt
您也可以使用单个正则表达式来完成此操作,但这涉及将文件的全部内容都放入单个字符串中,这可能最终会占用大文件太多的内存。为了完整起见,以下是该方法:
perl -e '@lines = <>; $content = join("", @lines); print "Found in $ARGV\n" if ($content =~ /abc.*efg/s);' yourfilename.txt
.*?
)来获得最小匹配。
可悲的是,你不能。从grep
文档:
grep搜索命名的输入文件(如果没有命名文件,或者如果给定单个连字符减号(-)作为文件名,则为标准输入),以查找包含与给定PATTERN匹配的行。
grep -Pz
如果您需要两个单词彼此靠近,例如不超过3行,则可以执行以下操作:
find . -exec grep -Hn -C 3 "abc" {} \; | grep -C 3 "efg"
相同的示例,但仅过滤* .txt文件:
find . -name *.txt -exec grep -Hn -C 3 "abc" {} \; | grep -C 3 "efg"
如果您还想使用正则表达式查找,也可以用grep
command 替换command egrep
。
我几天前发布了一个grep替代方案,它通过多行匹配或使用条件直接支持此功能-希望它对在此处搜索的某些人有用。该示例的命令如下所示:
多行:
sift -lm 'abc.*efg' testfile
条件:
sift -l 'abc' testfile --followed-by 'efg'
您还可以指定“ efg”必须在一定数量的行后跟随“ abc”:
sift -l 'abc' testfile --followed-within 5:'efg'
您可以在sift-tool.org上找到更多信息。
sift -lm 'abc.*efg' testfile
,因为匹配很贪婪,并且吞噬了所有行,直到efg
文件中的最后一行。
如果您不热衷于模式顺序,可以使用grep。
grep -l "pattern1" filepattern*.* | xargs grep "pattern2"
例
grep -l "vector" *.cpp | xargs grep "map"
grep -l
将找到与第一个模式匹配的所有文件,而xargs将为第二个模式进行grep。希望这可以帮助。
(echo abctest; echo efg)|ag 'abc.*(\n|.)*efg'
不匹配
作为替代巴鲁磨憨的答案,有可能只能使用模式的顺序来执行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
不过,这不是很漂亮。格式更可读:
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
这将打印所有文件的名称,这些文件的名称"pattern2"
出现在或之后"pattern1"
,或者都出现在同一行:
$ 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
tail -n +i
-在i
th之后(包括首尾)打印所有行grep -n
-在匹配的行之前加上行号head -n1
-仅打印第一行cut -d : -f 1
-使用:
分隔符打印第一个剪切列2>/dev/null
- tail
如果$()
表达式返回空,则会出现静音错误输出grep -q
-沉默grep
并在找到匹配项后立即返回,因为我们只对退出代码感兴趣&>
?我也在使用它,但是我从未在任何地方看到它的记录。顺便说一句,为什么我们实际上必须以这种方式使grep保持沉默?grep -q
不会做这个把戏吗?
&>
告诉bash重定向标准输出和标准错误,请参见bash手册中的REDIRECTION。您说得很对,因为我们可以做的很好,grep -q ...
而不是grep ... &>/dev/null
抓住好收获!
文件*.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
的
grep -n -m1 abc $f
搜索最多1个匹配项,并返回(-n)行号。如果找到匹配项(测试-n ...),则找到efg的最后一个匹配项(查找全部并使用尾号-n 1获取最后一个匹配项)。
z=$( grep -n efg $f | tail -n 1)
否则继续。
由于结果类似,18:foofile.sh String alf="abc";
我们需要从“:”开始删除直到行尾。
((${z/:*/}-${a/:*/}))
如果第二个表达式的最后一个匹配项超过第一个表达式的第一个匹配项,则应返回正结果。
然后我们报告文件名echo $f
。