shell grep命令,按字母顺序匹配具有字母的行

brjng4g3  于 2023-10-23  发布在  Shell
关注(0)|答案(3)|浏览(208)

我需要帮助用grep找出一个正则表达式,它将搜索一个文件,并显示将[a-z]按字母顺序分别放在每个'set'中的行(通过示例将变得清晰)
有效匹配示例:

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
doinxwow

doinxwow1#

坚韧的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 '{
    split($1,fi,"-")
    for(f=1;f<=length(fi);f++) {
        gsub(/[^a-f]/,"",fi[f])
        if (length(fi[f])>1) {
            split(fi[f],ch,"")
            for(i=2;i<=length(ch);i++) {
                if(ch[i-1]>ch[i]) next
            }
        }
    }
} 1' file 
# same output
jgovgodb

jgovgodb2#

你的egrep对于在f之后有数字的子字符串失败。
当你用f?[^a-f]*-){4}替换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
8nuwlpux

8nuwlpux3#

您可以匹配下面的正则表达式,我已经写了工作与PCRE正则表达式引擎,虽然它将工作与其他引擎以及。

^                      # match the beginning of the string 
(                      # begin capture group 1
  (?:                  # begin a non-capture group
    \d*a               # match zero or more digits then 'a'
  )*                   # end non-capture group and execute it >= 0 times
  (?!                  # begin a negative lookahead
    [^-]*              # match >= 0 characters other than a hyphen 
    a                  # match 'a'
  )                    # end negative lookahead
  (?:\d*b)*(?![^-]*b)  # same as above except for 'b' 
  (?:\d*c)*(?![^-]*c)  # same as above except for 'c'
  (?:\d*d)*(?![^-]*d)  # same as above except for 'd'
  (?:\d*e)*(?![^-]*e)  # same as above except for 'e'
  (?:\d*f)*            # same as (?:\d*a)* above except for 'f' replacing 'a' 
  \d*                  # match zero or more digits
)                      # end capture group 1
(?:                    # begin a non-capture group
  -                    # match a hyphen
 (?1)                  # execute the instructions that comprise capture group 1
)*                     # end the non-capture group and execute it >= 0 times 
$                      # match the end of the string

PCRE以及其他几个引擎支持 * 子例程 * 或 * 子表达式 *。在这里,(?1)意味着token将被用于创建捕获组1的所有指令替换(尽管没有创建捕获组)。通过剪切((开始创建捕获组1)和)(终止捕获组代码)之间(但不包括()的所有代码,并将其粘贴到(?1)出现的位置,可以获得相同的效果。子程序缩短了代码,使其更容易阅读,并避免剪切和粘贴错误。
这个表达式是在设置了'x'标志的情况下编写的,调用 * 自由间距 * 模式。在自由间距模式下,所有的空白和注解('#'在一行上,以及后面的所有内容)。您可以看到,自由间距模式可用于使表达式自文档化。
Demo
我在demo链接中做了以下操作:

  • 设置m标志以使锚^$分别匹配行的开头和结尾,而不是字符串的开头和结尾;
  • 设置g标志以在每次匹配之后尝试附加匹配;和
  • 将每个[^-]替换为[^-\n],因为我正在针对多个示例测试该表达式,每个示例都在单独的一行中。

在匹配字符串的开头之后,正则表达式匹配零个或多个数字,后跟一个“a”。这是在一个非捕获组中完成的,它被执行零次或多次。无论是否找到任何'a',此时都有一个负向前看,确保在下一个连字符或字符串结尾之前没有更多的'a'。这确保了如果要匹配字符串,则已经找到了所有的“a”。
然后,正则表达式尝试匹配零个或多个"B",每个“B”前面都有零个或多个数字。请注意,最后一个'a'(如果没有'a',则为字符串的开头)后面的匹配项必须是数字(零或更多)。在考虑了零个或多个由零个或多个数字分隔的“B”之后,负向前看确保了--就像它对“a”所做的那样--在下一个连字符或字符串的结尾之前没有更多的“B”。这意味着如果要找到字符串的匹配,则已经找到了所有的“B”。
这个结构在'c'、'd '、'e'和' f '中重复,但是对于'f'不需要负先行,因为它后面只能跟零个或多个数字,这在表达式中已经考虑到了。
上面的正则表达式通常写为如下。

^((?:\d*a)*(?![^-]*)(?:\d*b)*(?![^-]*b)(?:\d*c)*(?![^-]*c)(?:\d*d)(?![^-]*d)(?:\d*e)*(?![^-]*e)(?:\d*f)*\d*)(?:-(?1))*$

相关问题