提问人:mister entername 提问时间:10/22/2023 最后编辑:mister entername 更新时间:10/28/2023 访问量:220
grep 命令,该命令将行与组中每个字符的 2 个字符进行匹配
grep command that matches lines with exactly 2 of every character from a group
问:
我需要帮助找出一个带有 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]*$'
答:
下面是一个 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"
:对于十六进制范围内的每个字符,计算行 () 中的出现次数。如果计数不正好是 2,则字符通过 grep 过滤器。运算符反转 grep 结果,因此仅当所有字符的计数正好为 2 时,grep 表达式才为 true。[a-f0-9]
$_ =~ tr/$_//
!
print if
:如果 grep 表达式为 true,则打印该行。
此命令打印仅由十六进制字符组成且每个十六进制字符正好出现两次的所有行。file.txt
评论
$_
不插值 。 说:“从来没有任何变量插值,所以”$“和”@“总是被视为文字”tr/$_//
perldoc -f y
你可以用一种直接的方式做到这一点
^ 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
使用任何 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 个十六进制
- 不能包含任何十六进制的三个(否则其他十六进制将少于两个)
这是利用德摩根定律:
( A & B & C & ...)== NOT( NOT(A) |非(B) |NOT(C) |...)
我们提供了一个必须失败的正则表达式的 OR 列表,然后使用 grep 进行反转,从而产生一个必须匹配的正则表达式的 AND 列表。-v
如果十六进制必须成对出现,请添加:
^[^ ] +
[^ ] +
[^ ]$
[^ ]{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).*$
我认为这是最简单的方法:
^(?!.*([a-f0-9])(?:.*\1){2,})(?:[a-f0-9]{2}(?: |$)){16}$
基本上,它断言行的开头后面没有出现 3 个或更多个相同的字符,然后匹配以空格分隔的字符对。
https://regex101.com/r/zerktu/1
评论
echo 00 11 22 | grep -P '^(?!.*([a-f0-9])(?:.*\1){2,})(?:[a-f0-9]{2}(?: |$))+$'
00 11 22
01 23 45
{16}
+
我知道 OP 出于某种原因需要 grep 解决方案,但其他将来阅读此问题时遇到类似问题的人可能没有同样的限制。
给定此输入,其中第一行有效,但最后 3 行涵盖了我认为的 3 种可能的故障情况:(1) 输入包含的 [0-9a-f 中的一个字符少于 2 个],或 2) 输入包含不在 [0-9a-f] 中的字符,或 3) 输入包含 [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
并牢记这句话:
有些人在遇到问题时会想“我知道,我会用 正则表达式。现在他们有两个问题。
这是我真正处理这个问题的方式,在每个 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
如果您只能使用...
那么我认为管道会让它更容易阅读和维护,尽管我不喜欢这个设计运行 18 次的事实......grep
grep
$: 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|
你可能必须把这些命令中的每一个都放在自己的一行上,但这仍然更好,而且可能更容易让下一个人把他的脑袋绕过来。
评论
^(?!.*?(\S).*\1.*\1)(?:\b ?[a-f\d]{2}){16}$
它与 @jhnc 的解决方案之一非常相似,所以我不会发布另一个答案。grep -P
\S
[a-f\d]
((?!^.*?([a-f\d]).*?\1.*?\1)
LANG=C
\d