在标记字符

时间:2016-01-05 01:49:43

标签: regex linux bash awk sed

在我的文件中,每行包括由空格分隔的五个数字字段(前面​​和后面跟着更多字段)。通过shell脚本,我需要能够在这五个数字字段中选择具有3个,4个和5个相同条目的行(即三个单独的搜索,这样在这些字段中搜索具有3个匹配的行也不会返回具有这些领域中有4或5场比赛。)

要查找相关字段,我的搜索必须在一行上找到第一个打开和关闭的括号对。括号结束后,紧接着的五个字段是我感兴趣的字段。一个可能的复杂情况:有时一个或多个数字字段被替换为单个破折号/连字符而不是数字。一种可能的简化方法:五个字段将以(非严格)升序排列,任何连字符条目将始终位于其余数字字段之前。

我会对此提出一些sed / awk建议表示感谢。非常感谢!

[编辑]:我可以提取相关字段(详见下面的评论),因此上面的删除段落是不必要的。提取相关字段后,这是示例数据:

109 110 111 111 112
110 110 111 111 112
99 99 99 112 112
99 99 99 112 112
100 101 101 112 112
102 102 102 112 112
102 102 103 112 112
102 103 103 112 112
102 104 104 112 112
102 104 104 112 112
103 104 104 112 112
102 105 105 112 112
102 105 105 112 112
103 105 105 112 112
102 106 106 112 112
102 106 107 112 112
103 106 107 112 112
104 106 107 112 112
102 107 107 112 112
104 107 107 112 112
104 107 107 112 112
106 107 108 112 112
107 107 108 112 112
107 107 108 112 112
102 109 109 112 112
102 109 109 112 112
104 109 109 112 112
102 109 110 112 112
103 109 110 112 112
104 109 110 112 112
102 110 110 112 112
104 110 110 112 112
104 110 110 112 112
107 109 111 112 112
107 109 111 112 112
106 110 111 112 112
107 110 111 112 112
107 110 111 112 112
109 110 112 112 112
110 110 112 112 112
107 112 112 112 112
112 112 112 112 112

在这些行上n = 3时会产生命中:

99 99 99 112 112
99 99 99 112 112
102 102 102 112 112
109 110 112 112 112
110 110 112 112 112

此行n = 4时命中:

107 112 112 112 112

并且在此行上n = 5时命中:

112 112 112 112 112

4 个答案:

答案 0 :(得分:2)

以下是使用awk的Bash脚本解决方案。它逐行读取文件,并使用AWK associative array计算数字出现在线上的次数。将filename.txt更改为包含数字的文件。

n=3
while read line
do
    echo "$line" | awk -v n="$n" '
        {
            for(i=1; i <= NF; i++) {
                a[$i]++
            }
        }
        {
            for(o in a) {
                if (a[o] == n) {
                    print
                }
            }
        }
        '
done < filename.txt

答案 1 :(得分:2)

您也可以使用sed执行此操作。 您可以创建script

n=$(($1-1))
sed -n "/\([0-9]*\)\( \1\)\{$n\}/p" filename

然后像这样运行,只提供n作为脚本参数:

./script.sh 3

输出:

99 99 99 112 112
99 99 99 112 112
102 102 102 112 112
109 110 112 112 112
110 110 112 112 112

答案 2 :(得分:2)

另一个sed例子,并不希望我的工作浪费;)

#!/bin/bash
while (($1 > 0))
do
    n="${n} \1"
    set ${1}-1
done
sed -nr "\_\<([0-9]+)${n}\>_ p" 

编辑: 在BSD sed(OS X)上,您需要分别用引人注目的\<\>替换[[:<:]][[:>:]]

答案 3 :(得分:2)

仅作为单线的Awk解决方案:

awk -v n=3 '{for(i=1;i<=NF;i++)a[$i]++;for(o in a)if(a[o]==n)p=1} p; {p=0;delete a}' inputfile

拆分以便于阅读,这有点类似于badjr的解决方案。 (我使用他的变量来进行比较。)

{
  for (i=1;i<=NF;i++)     # populate an array with counts of unique elements
    a[$i]++
  for (o in a)            # check the array for a matching count & set flag
    if (a[o]==n)
      p=1
}

p;       # if we've set our flag, print the current line.

{        # clear our workspace for the next line.
  p=0
  delete a
}

如果您对仅使用bash的解决方案感兴趣,以下实现相同的awk逻辑,只有没有awk:

#!/usr/bin/env bash

n=5

while read -a a; do
  unset b
  for i in "${!a[@]}"; do
    (( b[${a[$i]}]++ ))
  done
  for i in "${b[@]}"; do
    [ "$i" -eq "$n" ] && echo "${a[@]}"
  done
done < inputfile

请注意,因为此处的输出是使用数组元素打印的,所以输入文件中的空格将不会被维护。

此解决方案仅限bash,因为它使用了数组。