计算文本文件中模式的所有唯一实例

Count all unique instances of a pattern in text file

提问人:Jason 提问时间:11/13/2023 最后编辑:Jason 更新时间:11/15/2023 访问量:151

问:

我有一个文件,里面有几行文本,所有文本都以六个十六进制数字、一个空格和一些文本开头。有时,此剩余文本以字符 或 开头,后跟 4 位数字和冒号。例:XY

72a0bf Lorem ipsum dolor sit amet, consectetur adipiscing elit. 
703e80 X-2310: Duis nibh sem, sollicitudin vel nulla eu, facilisis pulvinar purus. 
21b6ac Nam ornare blandit consequat. Vivamus tincidunt eros consequat, egestas 
8961cb Y-0110: dui at, lobortis nibh. Sed sed felis vel felis euismod dapibus in vel erat. 
ce5dfe X-2310: Curabitur facilisis felis nec ante euismod ultricies at eget turpis. Duis 
fa5e8b ac dui ut elit volutpat accumsan quis quis justo. Donec luctus suscipit sem,

我的目标是找到这些和字符串的所有唯一实例。现在,如果我的所有记录都是上述形式,则可以通过例如一些 和其他工具来实现:X***Y***grepawkbash

$ cat file.txt | grep -E '^[a-z|0-9]{6}[[:space:]](X|Y)-[0-9]{4}' | awk '{print substr($2, 1, length($2) -1)}' | sort | uniq
X-2310
Y-0110

问题在于,在一些包含我正在 ping 的模式的记录中并且仅在这些记录中),不幸的是,我在剩余文本中有一些这些和字符串的实例,这些实例可以是 1、2、3,...无论数字如何:grepX***Y***

c8edc6 X-0101: at tempor tellus commodo sit amet. X-2489 Nunc id gravida est, in rhoncus metus.
fa5e8b Y-9410: ac X-1320 X-0101 dui ut elit Y-9416 volutpat accumsan X-0101 quis X-2000 quis justo.
e29ac0 Y-5751: Vivamus Y-0110 vehicula Y-2021 dolor X-0101 a pretium.

这在上面的脚本中引起了麻烦,因为我只在寻找行首附近的模式。我希望能够找到字符串和 .有什么想法吗?唯一的限制是,由于环境限制,它必须是一个解决方案。X***Y***bash

bash awk 模式匹配

评论

0赞 user1934428 11/13/2023
您可以使用此模式的所有出现(逐行),然后通过管道将其用于查找唯一模式。grep -osort -u
0赞 Ed Morton 11/14/2023
对任何解决方案都保持警惕,因为它可能会从您不想匹配的位置输出匹配的字符串,例如,针对包含第一行或类似内容的示例输入测试任何潜在的解决方案。 当您想在特定字段上匹配时,这不是一个好的选择,AWK 更适合于此,因为它旨在处理字段,并且 SED 通常也可以提供一个不错的解决方案,只要它只需要在单个行上进行简单的替换。grep72a0bf Lorem ipsum X-9999: dolorgrep
0赞 Ed Morton 11/14/2023
在模式匹配中,匹配所需的字符串几乎总是比不匹配不匹配不需要的类似字符串要容易得多,因此发布示例输入/输出非常重要,这些输入/输出不仅涵盖您希望在您期望找到它们的位置匹配的字符串的晴天情况。
0赞 Andrej Podzimek 11/15/2023
旁注:并且不是“工具”,它们是独立于 .grepawkbashbash

答:

1赞 Jason 11/13/2023 #1

$ grep -E --only-matching '(X|Y)-[0-9]{4}' file.txt | sort | uniq

做诀窍。

评论

0赞 Ed Morton 11/14/2023
这将不理想地匹配以下任何一项:( 不以 ) 结尾 ,或 (第二个字段与 ) 不完全匹配,或 ( 不是第二个字段)。foo X-1111 barX-1111:foo X-1111:2222 bar^[XY]-[0-9]{4}:$foo bar this X-1111: thatX-1111:
2赞 Sash Sinha 11/13/2023 #2

请尝试以下命令:

$ grep -oE '(X|Y)-[0-9]{4}' file.txt | sort -u
X-2310
Y-0110

解释:

  • -o仅输出线路的匹配部分。
  • -E用于扩展正则表达式。
  • (X|Y)-[0-9]{4}匹配以四位数字开头或后跟四位数字的任何字符串的正则表达式。X-Y-
  • sort -u:这将对匹配项进行排序并过滤掉重复项,保留唯一项目。

评论

0赞 Ed Morton 11/14/2023
这将不理想地匹配以下任何一项:( 不以 ) 结尾 ,或 (第二个字段与 ) 不完全匹配,或 ( 不是第二个字段)。foo X-1111 barX-1111:foo X-1111:2222 bar^[XY]-[0-9]{4}:$foo bar this X-1111: thatX-1111:
0赞 Sash Sinha 11/14/2023
@EdMorton 添加一个否定的前瞻性断言,即前面的模式后面没有紧跟冒号,即 ?这对你的例子有用吗?(?<!:)(X|Y)-[0-9]{4}:grep -oE '(X|Y)-[0-9]{4}(?<!:)' file.txt | sort -u
0赞 Ed Morton 11/14/2023
BRE(默认由 sed 和 grep 使用)和 ERE(默认由 awk 使用,sed 和 grep 使用 )都不支持任何类型的环视。你必须使用 GNU grep 支持的 PCRE 来查看,但那时你最好使用,因为除了匹配正则表达式之外,你还需要做更多的事情(限制它在一个字段上匹配并只输出该字段的一部分)。-E-Pperl
2赞 markp-fuso 11/13/2023 #3

一个想法:awk

awk '$2 ~ /^[XY]-[0-9]{4}:$/ && !seen[$2]++ { print substr($2,1,6) }' file.txt

哪里:

  • 如果第二个字段的格式为 和 ...[XY]-####:
  • 我们以前从未见过第二个字段(第二个字段用作数组的索引;我们第一次遇到这个测试,所以计算为“真”;连续的匹配有,所以是假的)seen[$2] == 0!0seen[$2] > 0!(1+)
  • 剥离结肠print/substr()

这将生成:

X-2310
Y-0110

注意:这会在处理记录时生成输出;如果需要保证输出的顺序,则通过管道传递给sort

0赞 Mohommad Belal 11/13/2023 #4

根据这个问题,我所理解的是:

从给定文件中找到所有以“X-”或“Y-”开头的唯一字符串,后跟 4 位数字。

我希望这能有所帮助:

grep -oE '\b[XY]-[0-9]{4}\b' file.txt | sort | uniq

解释:

格雷普

  • -o 或 --only-matching :P rint 仅匹配行的匹配非空部分,使用
  • -E 或 --extended-regexp :将模式解释为扩展正则表达式 (ERE)

正则表达式:

  • \b...\b :d消除文本的边界。
  • [XY] :X 或 Y 字符
  • '-':字符连字符“-”
  • [0-9]{4} : 任意长度的数字 4.

财政年度:

  1. https://www.gnu.org/software/grep/manual/grep.html#grep-Programs
  2. https://regexr.com/7n3ua

评论

0赞 Ed Morton 11/14/2023
这将不理想地匹配(不以 ) 结尾,并且不理想地匹配(不是第二个字段)。foo X-1111 barX-1111:foo bar this X-1111: thatX-1111:
0赞 Diego Torres Milano 11/13/2023 #5

类似的想法,使用 as 分隔符awk:

awk -F '[ :]' '$2 ~ /[XY].*/&&!a[$2]++ {print $2}'

评论

0赞 Ed Morton 11/14/2023
这将不受欢迎地匹配(不以 )。foo X-1111 barX-1111:
0赞 ufopilot 11/13/2023 #6
$ awk 'match($2,/^[XY]-[0-9]{4}:$/) && !seen[$2]++ && sub(/:$/,"",$2) && $0=$2' file 
X-0101
Y-9410
Y-5751
X-2310
Y-0110
0赞 RavinderSingh13 11/13/2023 #7

使用您展示的示例和尝试,请尝试以下,从 Mark 的回答中汲取灵感。简单的解释是:

  • 检查条件,如果第 2 个字段等于^[XY]-[0-9]{4}:$
  • 并确保数组之前没有任何当前值 $2,然后打印当前行的 $2。seen
awk '
$2 ~ /^[XY]-[0-9]{4}:$/ && !seen[$2]++{
  sub(/:$/,"",$2)
  print $2
}
'  Input_file
2赞 Ed Morton 11/14/2023 #8

我通常会去这个或其他任何涉及字段匹配的东西,但只是为了完整性:使用 GNU for 和 plus :awksed-E\s/\Ssort

$ sed -En 's/^\S+\s+([XY]-[0-9]{4}):(\s.*)?$/\1/p' file | sort -u
X-2310
Y-0110
0赞 Andrej Podzimek 11/15/2023 #9

在没有外部程序的 Bash 中:

declare -Ai matches=();
while IFS= read -r line; do
  read -ra tokens <<< "$line"
  for token in "${tokens[@]:1}"; do
    [[ "$token" = [XY]-[0-9][0-9][0-9][0-9] ]] && ((++matches["$token"]))
  done
done < /path/to/input.txt
printf '%s\n' "${!matches[@]}"