regex grep命令,用于匹配具有组中每个字符中的2个字符的行

ilmyapht  于 2023-11-20  发布在  其他
关注(0)|答案(6)|浏览(130)

我需要帮助用grep(我只能用grep)找出一个正则表达式,它将搜索一个文件,并显示从[a-f0-9]中的每个字符中精确包含2的行。
有效匹配示例:

33 e0 f1 76 9c 4f a8 6c 01 5d 45 9e 28 db 7b 2a

字符串
无效匹配示例:

c0 b0 f5 60 02 8b 1c a4 41 7c 53 f2 85 20 a0 d1


我尝试了'(?:[0-9a-f])?^[^\1]*\1[^\1]*\1[^\1]*$',但它显然不起作用,因为你不能否定这样的反向引用,即使我纠正了它,我也不确定这是正确的方法。

krcsximq

krcsximq1#

你可以用一种简单的方式来做这件事,

^                           start of line (check docs)
(?=(\S\S\s){15}\S\S$)       matches 32 symbols with spaces
(?=[^1]*1[^1]*1[^1]*$)      contains exactly two ones
(?=[^2]*2[^2]*2[^2]*$)      contains exactly two ones
...
(?=[^f]*f[^f]*f[^f]*$)      contains exactly two Fs

字符串

sycxhyv7

sycxhyv72#

使用任何POSIX grep

grep -vEf regexlist filetosearch

字符串
其中regexlist包含:

[^0-9a-f ]
^ *([0-9a-f] *){0,31}$
^ *([0-9a-f] *){33}
(0.*){3}
(1.*){3}
(2.*){3}
(3.*){3}
(4.*){3}
(5.*){3}
(6.*){3}
(7.*){3}
(8.*){3}
(9.*){3}
(a.*){3}
(b.*){3}
(c.*){3}
(d.*){3}
(e.*){3}
(f.*){3}

  • 只能包含十六进制和空格
  • 必须包含不少于和不超过32十六进制
  • 不能包含任何十六进制的三个(否则将少于两个其他的)

这是使用De Morgan's law
(A & B & C &.)== NOT(NOT(A)|非(B)|非(C)|......)的情况。
我们提供了一个必须失败的正则表达式的OR列表,然后使用grep的-v进行反转,这将产生一个必须匹配的正则表达式的AND列表。
如果十六进制必须成对出现,则添加:

^[^ ] +
 [^ ] +
 [^ ]$
[^ ]{3}


对于每个数字之间只有一个空格,并且行首或行尾没有空格:

{2}
^ +
 +$


如果您的grep支持-P,则更简单。例如:

grep -P '^(?!^.*?([0-9a-f]).*\1.*\1)(?!^.*?[^0-9a-f ])(?=[^ ]{2}( [^ ]{2}){15}$).*$' filetosearch

  • ^... .*$-匹配整行
  • (?!^.*?([0-9a-f]).*?\1.*?\1)-不能包含三个(或更多)
  • (?!^.*?[^0-9a-f ])-仅十六进制和空格
  • (?=[^ ]{2}( [^ ]{2}){15}$)-正好16对

或者,如果空白的数量是无关紧要的,甚至:

grep -P '^(?!^.*?([0-9a-f]).*?\1.*?\1)( *[0-9a-f]){32} *$' filetosearch


我相信锚定和懒惰:

^(?!^.*?([0-9a-f]).*?\1.*?\1).*$


应该比未锚定和/或贪婪更快地匹配(即失败):

^(?!.*?([0-9a-f]).*?\1.*?\1).*$
^(?!.*([0-9a-f]).*\1.*\1).*$
^(?!^.*([0-9a-f]).*\1.*\1).*$
6xfqseft

6xfqseft3#

我想这是最简单的方法:
第一个月
基本上,它Assert行的开头后面没有出现3个或更多的相同字符,然后匹配空格分隔的字符对。
https://regex101.com/r/zerktu/1

5kgi1eie

5kgi1eie4#

我知道OP出于某种原因需要一个grep解决方案,但其他有类似问题的人在未来阅读这个问题可能不会有同样的限制。
给定这个输入,其中第一行是有效的,但最后3行涵盖了我认为的3种可能的失败情况(**1)input包含[0-9a-f]中的一个字符的少于2个,或者2)input包含不在[0-9a-f]中的一个字符,或者3)**input包含[0-9a-f]中的一个字符的多于2个):

$ cat file
33 e0 f1 76 9c 4f a8 6c 01 5d 45 9e 28 db 7b 2a
   e0 f1 76 9c 4f a8 6c 01 5d 45 9e 28 db 7b 2a
33 e0 f1 76 9c 4f a8 6c 01 5d 45 9e 28 db 7b 2a x
33 e0 f1 76 9c 4f a8 6c 01 5d 45 9e 28 db 7b 2a 3

字符串
记住this saying
有些人在遇到问题时会想:“我知道,我会用正则表达式。”
下面是我真正的方法,在每个Unix机器上的任何shell中使用任何awk:

$ awk '
    BEGIN { chars="abcdef0123456789"; lgth=length(chars) }
    {
        input = $0
        for (i=1; i<=lgth; i++) {
            char = substr(chars,i,1)
            if ( gsub(char,"",input) != 2 ) {
                next
            }
        }
    }
    input ~ /^ *$/
' file
33 e0 f1 76 9c 4f a8 6c 01 5d 45 9e 28 db 7b 2a

v1l68za4

v1l68za45#

如果你必须只使用grep ...
那么我认为管道会使它更容易阅读和维护,尽管我不喜欢这个设计运行grep 18次的事实。

$: cat file
c8 f1 7a d9 f2 a7 a0 5a e9 9f c1 4c 2f e3 f5 3b |aanbestedingsdossier|
56 fc 9f 14 da 80 51 3e 74 9a 73 ed 6b c2 80 2b |aanbetaalt|
ce ba ed a0 a5 27 fd 4c 22 a2 1d a1 87 46 91 b3 |aanbranden|
33 e0 f1 76 9c 4f a8 6c 01 5d 45 9e 28 db 7b 2a |good example|
c0 b0 f5 60 02 8b 1c a4 41 7c 53 f2 85 20 a0 d1 |bad example|

$: grep -E '([[:alnum:]]).*\1.* \|' file              | # lines with at least 2 occurances
  grep -vE '([[:alnum:]]).*\1.*\1.* \|'               | # minus lines with 3
  grep a | grep b | grep c | grep d | grep e | grep f | # now just assert each required character
  grep 0 | grep 1 | grep 2 | grep 3 | grep 4 | grep 5 | grep 6 | grep 7 | grep 8 | grep 9
56 fc 9f 14 da 80 51 3e 74 9a 73 ed 6b c2 80 2b |aanbetaalt|
33 e0 f1 76 9c 4f a8 6c 01 5d 45 9e 28 db 7b 2a |good example|

字符串
当然,您至少可以使用sed在一次传递中完成所有操作?

$: sed '
  /\([[:alnum:]]\).*\1.* |/!d;              # drop lines without at least 2 occurances
  /\([[:alnum:]]\).*\1.*\1.* |/d;           # drop lines with 3
  /a/!d; /b/!d; /c/!d; /d/!d; /e/!d; /f/!d; # now drop lines that lack any required character
  /0/!d; /1/!d; /2/!d; /3/!d; /4/!d;
  /5/!d; /6/!d; /7/!d; /8/!d; /9/!d;
' file
56 fc 9f 14 da 80 51 3e 74 9a 73 ed 6b c2 80 2b |aanbetaalt|
33 e0 f1 76 9c 4f a8 6c 01 5d 45 9e 28 db 7b 2a |good example|

$: sed -E '
  /([[:alnum:]]).*\1.* \|/!d;               # drop lines without at least 2 occurances
  /([[:alnum:]]).*\1.*\1.* \|/d;            # drop lines with 3
  /a/!d; /b/!d; /c/!d; /d/!d; /e/!d; /f/!d; # now drop lines that lack any required character
  /0/!d; /1/!d; /2/!d; /3/!d; /4/!d;
  /5/!d; /6/!d; /7/!d; /8/!d; /9/!d;
' file
56 fc 9f 14 da 80 51 3e 74 9a 73 ed 6b c2 80 2b |aanbetaalt|
33 e0 f1 76 9c 4f a8 6c 01 5d 45 9e 28 db 7b 2a |good example|

$: sed '
  /\([[:alnum:]]\).*\1.* |/!d
  /\([[:alnum:]]\).*\1.*\1.* |/d
  /a/!d
  /b/!d
  /c/!d
  /d/!d
  /e/!d
  /f/!d
  /0/!d
  /1/!d
  /2/!d
  /3/!d
  /4/!d
  /5/!d
  /6/!d
  /7/!d
  /8/!d
  /9/!d
' file
56 fc 9f 14 da 80 51 3e 74 9a 73 ed 6b c2 80 2b |aanbetaalt|
33 e0 f1 76 9c 4f a8 6c 01 5d 45 9e 28 db 7b 2a |good example|


您可能必须将这些命令单独放在一行中,但这样更好,而且可能更容易让下一个人理解。

zpf6vheq

zpf6vheq6#

下面是一个Perl一行程序的例子,它应该可以做到这一点:

perl -ne 'print if /^[a-f0-9]*$/ && !grep {($_ =~ tr/$_//) != 2} 0..9,"a".."f"' file.txt

字符串
此命令执行以下操作:

  • perl -ne:为输入中的每一行添加以下Perl代码。
  • /^[a-f0-9]*$/:检查行是否只包含[a-f0-9]范围内的字符。
  • !grep {($_ =~ tr/$_//) != 2} 0..9,"a".."f":对于十六进制范围[a-f0-9]中的每个字符,计算行($_ =~ tr/$_//)中出现的次数。如果计数不正好为2,则字符通过grep过滤器。!运算符将grep结果反转,因此只有当所有字符的计数都正好为2时,grep表达式才为true。
  • print if:如果grep表达式为true,则打印该行。

此命令打印file.txt中仅包含十六进制字符且每个十六进制字符恰好出现两次的所有行。

相关问题