在负回顾后面使用grep反斜杠

29

我想在我的 LaTex 文档中找到不以反斜杠符号 \ 开头的 XXX 出现次数。因此,我正在寻找没有被反斜杠所前置的出现次数。

我尝试了以下方法:

grep -c -e '(?<!\)XXX' test.tex    #result: grep: Unmatched ) or \)
grep -c -e '(?<!\\)XXX' test.tex   #result: 0
grep -c -e "(?<!\\)XXX" test.tex   #result: -bash: !\\: event not found

它们都不能按预期工作。事实上,我根本不理解最后一个错误消息。

我的 test.tex 文件仅包含以下几行内容

%test.tex

XXX

\XXX

所以期望的结果是1

有什么想法吗?

附注:我正在使用bash工作。


1
请注意,在您的最后一个示例中,双引号中的感叹号被解释为历史扩展符,但是“!'\'”不是有效的历史扩展表达式。 - chepner
4个回答

58

标准和扩展正则表达式都不支持向后查找。请使用Perl兼容的正则表达式:

grep -P '(?<!\\)xxx' test.tex

这似乎有效,但我进行了交叉检查,搜索了\\XXX(?<!\\)XXXXXX。第一个结果为39,第二个为10,最后一个为34。这些数字不符合预期,所以某些地方可能存在问题。但这可能超出了我的初始问题。 - Simon
@Simon:也许同一行上有几次出现? - choroba

1

尝试使用

grep -P '(?<!\\)\bXXX\b' test.tex

这很有帮助,但根据我的grep手册,-P是“高度实验性的”,这仍然正确吗?是否有一种非Perl方式可以使用grep进行前后查找? - santiago arizti
4
@grep -P@已经存在很长时间了,我从未听说过它应该被视为“实验性”。由于我喜欢Perl正则表达式功能,所以我总是使用这个开关并使用Perl格式。 - Ωmega

0

我的MacOS Catalina版的grep甚至没有支持Perl风格正则表达式的-P标志。


    $ grep --version
    grep (BSD grep) 2.5.1-FreeBSD

所以我刚刚自己编写了一个grep -l命令的版本,我需要使用负向前瞻正则表达式获取匹配文件列表,以下是源代码,请随意根据您自己的需求进行调整。

    #!/usr/bin/perl
      
    use strict;
    use warnings;
    
    # Tries to mimic at least partially `grep -l` command, and provides support for look-arounds using Perl regex'
    # Usage: ls <some folder> | grepList.pl <reg-ex>
    # Algorithm:
    # Open each file in the list supplied
    #   Apply regex to each line, as soon as it matches output file name to STDOUT and continue to next file
    #   If EOF file reached, means file did not match, do not print file name, and move on to next file
    # Runtime complexity: O(m * n), where m is number of files and n is the maximum number of lines a file can have
    # Space complexity:   O(1), no intermediary memory storage needed
    
    my $reg_ex = qr/$ARGV[0]/;
    
    while(<STDIN>) {
        chop($_);
        my $file = $_;
        open(IN, $file) || die "Unable to open $file: $!";
        while(<IN>) {
            my $line = $_;
            if ($line =~ /$reg_ex/) {
                print "$file\n";
                last;
            }
        }
    }


0
如果您使用GNU grep,则应支持具有--perl-regexp或-P命令行选项的Perl兼容正则表达式。经典的Perl正则表达式仅支持否定字符类,例如,[^a]表示除“a”之外的任何字符。
您提供的示例看起来像是Perl兼容的正则表达式,而不是经典的正则表达式,您必须使用带有--perl-regexp或-P命令行选项的GNU grep,或者您可以安装启用了PCRE的grep,例如“pcregrep”-它不需要任何PCRE的命令行选项,因此更方便。
此外,您的模式似乎不像是负断言。它应该是
(?!pattern)

不是

(?<!pattern)

在这里找到更多:https://perldoc.perl.org/perlre.html

如果你喜欢兼容Perl的正则表达式并且有Perl但没有pcregrep或者你的grep不支持--perl-regexp,你可以使用一行Perl脚本来实现相同的功能,就像grep一样。Perl接受标准输入和grep一样,例如:

ipset list | perl -e "while (<>) {if (/packets(?! 0 )/){print;};}"

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接