按字母顺序将行与字母匹配的 grep 命令

grep command that matches lines with letters in alphabetical order

提问人:mister entername 提问时间:10/23/2023 最后编辑:John Kugelmanmister entername 更新时间:10/24/2023 访问量:209

问:

我需要帮助找出一个带有 grep 的正则表达式,该表达式将搜索文件并显示将 [a-z] 按字母顺序分别放在每个“集合”中的行(示例会变得很清楚)

有效匹配示例:

a96d7e75-4432-41de-835c-625a636c1914 prefranks
b028224d-314b-4b03-a873-1436838f9233 escape

无效匹配示例:

c69f34e8-905e-4ce8-7893-a3e271d6f48c reconvince
e9db0700-f72b-4bea-ae37-e18ec6ca80d3 lumberjacks

我用了:

egrep '^([^a-f-]*a?[^a-f-]*b?[^a-f-]*c?[^a-f-]*d?[^a-f-]*e?[^a-f-]*f?-){4}[^a-f-]*a?[^a-f-]*b?[^a-f-]*c?[^a-f-]*d?[^a-f-]*e?[^a-f-]*f?[0-9]* ' text.txt

它没有给出任何无效的匹配,但它遗漏了如下的有效示例,我无法弄清楚原因:

6cd11113-bcf3-4f73-85f5-145b49225244 neoconservative
96239f18-5c62-495a-b1f8-50759443b885 fellest
51771125-b4d8-4cf8-a3d9-67117263f708 macular
a266f798-d772-47f0-9bdf-451939c2007e buntings
正则表达式 shell grep

评论

0赞 mister entername 10/23/2023
@CarySwoveland 对于这个冗长的问题,我深表歉意,我以为我的命令只需要进行几次更改,但正则表达式中的仪表不是我所期望的。这是一项小任务,要求答案简短,并使用简单的 grep 命令完成。
0赞 Ed Morton 10/24/2023
egrep已被弃用近 20 年,取而代之的是 .grep -E
0赞 Cary Swoveland 10/24/2023
我欠你一个道歉。我最初说正则表达式必须非常长且高度复杂。事实上,所需的正则表达式是相当可控的。
0赞 Kaz 10/24/2023
@EdMorton 但是,也被弃用了,取而代之的是 ,你知道这是怎么回事。tarpax
0赞 Dominique 10/24/2023
在字符串中,您称之为 valid,在“e”之后有一个“d”,在“e”之后有一个“c”,在“r”之后有一个“e”,在“r”之后有一个“a”,在“n”之后有一个“k”:-)a96d7e75-4432-41de-835c-625a636c1914 prefranks

答:

3赞 dawg 10/23/2023 #1

对 Grep 很强硬......

但鉴于:

$ cat file
a96d7e75-4432-41de-835c-625a636c1914 prefranks
b028224d-314b-4b03-a873-1436838f9233 escape
c69f34e8-905e-4ce8-7893-a3e271d6f48c reconvince
e9db0700-f72b-4bea-ae37-e18ec6ca80d3 lumberjacks

您可以使用 Ruby:

ruby -lane 'puts $_ if $F[0].split(/-/).
                        map{|a| a.scan(/[a-f]/)}.all?{|a| a==a.sort}' file

指纹:

a96d7e75-4432-41de-835c-625a636c1914 prefranks
b028224d-314b-4b03-a873-1436838f9233 escape

或者,任何 awk:

awk '{
    num_fields=split($1,fi,"-")
    for(f=1; f<=num_fields; f++) {
        gsub(/[^a-f]/,"",fi[f])
        if (length(fi[f])>1) 
            for(i=2; i<=length(fi[f]); i++) 
                if (substr(fi[f],i-1,1)>substr(fi[f],i,1)) next
    }
} 1' file
# same output

评论

0赞 Cary Swoveland 10/23/2023
或者,也可以从中去除数字,以产生,然后与 进行比较。由于与具有排序部分的字符串不同,因此此字符串(行)不匹配。我不记得你在之前的评论中提到的问题。我在这里的地方,我无意中在之前的评论中写道。但是,使用可能更好。"aa96d7e75-44d3a2"s = "aade-da"ss.gsub(/[a-z]+/) { |s| s.each_char.sort.join } #=> "aade-ad"sgsubscansplit
5赞 Walter A 10/23/2023 #2

对于后有数字的子字符串,Your 失败。
当您替换为 时,它将起作用,导致
egrepff?*-){4}f?[^a-f]*-){4}

egrep '^([^a-f-]*a?[^a-f-]*b?[^a-f-]*c?[^a-f-]*d?[^a-f-]*e?[^a-f-]*f?[^a-f]*-){4}[^a-f-]*a?[^a-f-]*b?[^a-f-]*c?[^a-f-]*d?[^a-f-]*e?[^a-f-]*f?[0-9]* ' text.txt

当您使用变量时,它同样难以阅读,但更短

x='[^a-f-]'
egrep "^($x*a?$x*b?$x*c?$x*d?$x*e?$x*f?$x*-){4}$x*a?$x*b?$x*c?$x*d?$x*e?$x*f?[0-9]* " text.txt

你可以做一个小循环:

#!/bin/bash
while IFS= read -r line; do
  charline="${line//[0-9]/}"
  if [[ "$charline" =~ ^(a?b?c?d?e?f?-){4}(a?b?c?d?e?f?)\ .* ]]; then
    echo "${line}"
  fi
done < text.txt

最后一个解决方案可以做得更小(并且更难阅读):

#!/bin/bash
while IFS= read -r line; do
  [[ "${line//[0-9]/}" =~ ^(a?b?c?d?e?f?-){4}(a?b?c?d?e?f?)\ .* ]] &&
    echo "${line}"
done < text.txt

编辑:上述解决方案不接受标记中的双字母。
当您想接受这些内容时,请将解决方案更改为

#/bin/bash
while IFS= read -r line; do
  [[ "${line//[0-9]/}" =~ ^(a*b*c*d*e*f*-){4}(a*b*c*d*e*f*)\ .* ]] &&
    echo "${line}"
done < text.txt

编辑 2:
当您想接受双字母,并且知道输入始终与给定的示例(仅标记、标记之间和最后一个标记后的空格)时,您可以使用
[0-9a-f]-

grep -E '^([a0-9]*[b0-9]*[c0-9]*[d0-9]*[e0-9]*[f0-9]*[- ]){5}' text.txt

评论

2赞 dawg 10/23/2023
如果您有多个相同的字母,例如aa96d7e75-4432...
0赞 Walter A 10/23/2023
@dawg 描述要求按字母顺序排列,但 OP 的代码也带有唯一的字母。OP 问为什么他的代码中没有显示某些行,所以我在他的代码中寻找改进。我添加了一个支持重复字母的解决方案。
4赞 The fourth bird 10/23/2023 #3

您似乎希望在第一个空格之前验证数据的第一部分。这些字符串由字符 a-f 0-9 和连字符组成。

如果你可以在 mac 上使用 或例如在 Mac 上用于与 Perl 兼容的正则表达式,则可以使用负前瞻来确保在第一部分中,匹配后 的范围内没有 char,匹配后等的范围内没有 char。grep -Pggrep -P[a-e]f[a-d]e

^(?![a-f\d-]*(?:f\d*[a-e]|e\d*[a-d]|d\d*[abc]|c\d*[ab]|b\d*a))[a-f\d]+(?:-[a-f\d]+){4} 
  • ^字符串的开头
  • (?!消极的展望,断言向右不是
    • [a-f\d-]*匹配可选字符 a-f、数字或-
    • (?:备选方案的非捕获组
      • f\d*[a-e]匹配一个 、 可选数字,然后匹配该范围内的一个字符f[a-e]
      • |
      • e\d*[a-d]匹配一个 、 可选数字,然后匹配e[a-d]
      • |
      • d\d*[abc]d
      • |
      • c\d*[ab]c
      • |
      • b\d*ab
    • )关闭群组
  • )关闭 lookbeahead
  • [a-f\d]+匹配 1+ 个字符 a-f 或数字
  • (?:-[a-f\d]+){4} 重复 4 次匹配和 1+ 字符 a-f 或数字后跟空格-

示例grep -P

grep -P '^(?![a-f\d-]*(?:f\d*[a-e]|e\d*[a-d]|d\d*[abc]|c\d*[ab]|b\d*a))[a-f\d]+(?:-[a-f\d]+){4} ' text.txt

输出

a96d7e75-4432-41de-835c-625a636c1914 prefranks
b028224d-314b-4b03-a873-1436838f9233 escape
6cd11113-bcf3-4f73-85f5-145b49225244 neoconservative
96239f18-5c62-495a-b1f8-50759443b885 fellest
51771125-b4d8-4cf8-a3d9-67117263f708 macular
a266f798-d772-47f0-9bdf-451939c2007e buntings

观看正则表达式演示

评论

2赞 anubhava 10/23/2023
眼睛疲劳正则表达式:-)
1赞 The fourth bird 10/23/2023
@anubhava 这里不可否认:-)幸运的是,OP已经有点习惯了。
0赞 Walter A 10/24/2023
在我的答案的最后一次编辑中,添加了一个简单的 grep,没有(否定)展望。
1赞 The fourth bird 10/24/2023
@WalterA 是的,但这也匹配或 5 个空格-----
1赞 Walter A 10/24/2023
@Thefourthbird 我想为行为良好的输入提供一个简单的解决方案。支持是可能的-----grep -E '^([a0-9]*[b0-9]*[c0-9]*[d0-9]*[e0-9]*[f0-9]*[- ]+){5}' text.txt
0赞 Ed Morton 10/24/2023 #4

我知道 OP 出于某种原因需要 grep 解决方案,但其他将来阅读此问题时遇到类似问题的人可能没有同样的限制。

记住这句话

有些人在遇到问题时会想“我知道,我会用 正则表达式。现在他们有两个问题。

并使用任何 awk:

$ cat tst.awk
{
    str = $1
    gsub(/[0-9]+/,"",str)
    numSegs = split(str,segs,"-")
    for ( segNr=1; segNr<=numSegs; segNr++ ) {
        seg = segs[segNr]
        numChars = length(seg)
        currChar = substr(seg,1,1)
        for ( charNr=2; charNr<=numChars; charNr++ ) {
            prevChar = currChar
            currChar = substr(seg,charNr,1)
            if ( currChar <= prevChar ) {
                next
            }
        }
    }
    print
}

$ awk -f tst.awk file
a96d7e75-4432-41de-835c-625a636c1914 prefranks
b028224d-314b-4b03-a873-1436838f9233 escape
6cd11113-bcf3-4f73-85f5-145b49225244 neoconservative
96239f18-5c62-495a-b1f8-50759443b885 fellest
51771125-b4d8-4cf8-a3d9-67117263f708 macular
a266f798-d772-47f0-9bdf-451939c2007e buntings

评论

0赞 Walter A 10/24/2023
gsub(/[0-9-]+/,"",str)应该是gsub(/[0-9]+/,"",str)
0赞 Ed Morton 10/24/2023
@WalterA对了,现在修复了,谢谢。我搞砸了,从 .[^a-f-]
0赞 Walter A 10/24/2023
if ( currChar <= prevChar )当您接受出现两次的字母时是可以的(恕我直言,这是对问题的正确解释),并且可以简单地更改为当您确实想接受双字母时。这种灵活性说明了 的优点。在我的解决方案中,这个小细节需要一个完全不同的解决方案。if ( currChar < prevChar )awk