【问题标题】:Grep and returning only column of matchGrep 并仅返回匹配列
【发布时间】:2016-03-03 03:43:58
【问题描述】:

如果我想从具有不同列数的文件中进行搜索,如下所示:

ppl:apple    age:5    F    add:blabla    love:dog
ppl:tom    M    add:blablaa    love:cat
ppl:jay    age:3    M    love:apple
ppl:jenny    acc:jen    age:8   F   add:blabla

...

文件是制表符分隔的,我想要的输出是:

age:5
age:3
age:8
...

使用grep age: 将返回整行,而 使用cut -f2 会返回一些不需要的列:

age:5
M
age:3
acc:jen

cut -f2|grep age:grep age|cut -f2: 都不起作用

我的数据可能在 11-23 列之间, 有没有更简单的方法可以使用 grep sed 或 awk 来处理它, 非常感谢

【问题讨论】:

  • 你给的输入,第二行没有 age:x 。输入数据是这样的吗?
  • 用标签替换这里的 T:sed -n "s/^.*T\(age:[^T]*\)T*$/\1/p" < x.txt
  • 有些可能没有age:列,列总数不固定

标签: bash awk sed grep multiple-columns


【解决方案1】:

grep 本身可以通过使用-o/--only-matching 开关来执行此操作,无需其他工具。你应该能够做到:

grep -o '\<age:[0-9]\+'

解释正则表达式中不太常见的部分:

  • \&lt; 是一个零宽度断言,表明您位于单词的开头(也就是说,年龄前面有一个非单词字符或出现在行首,但它实际上并不匹配该非单词特点);这会阻止你匹配,比如image:123。它在技术上不需要空格,所以它会匹配 :age: 或类似的;如果这是一个问题,请匹配 \t 本身并使用 cuttr 稍后将其删除。
  • \+ 表示“匹配 1 次或多次出现的前一个字符类”(即 [0-9],因此它匹配一个或多个数字)。 \+ 相当于重复该课程两次,第二次复制后跟*,例如[0-9][0-9]*,除了它更短,一些正则表达式引擎可以更好地优化\+

【讨论】:

  • ++;确实是最简单的方法。虽然不符合 POSIX,但它适用于 GNU 和 BSD/OSX grep。 (-o 是一个非标准选项,POSIX ERE 不支持\&lt; 或任何其他类型的字边界断言)。
  • @mklement0:感谢您的可移植性说明。找不到 POSIX grep 规范。 \&lt; 主要是为了安全;如果不会出现该形式的子字符串(例如,不想匹配 image:),则可以省略它,或者您可以匹配 \t 而不是 \&lt; 并使用 cut 删除 @987654346 @来自结果。
  • @once: \&lt; 是一个零宽度的断言,表明你在一个单词的开头(也就是说,age 前面有一个非单词字符或出现在开头行,但它实际上并不匹配该非单词字符),\+ 表示“匹配前一个字符类的 1 次或多次出现”(即 [0-9],因此它匹配一个或多个数字)。 \+ 相当于重复该课程两次,第二次复制后跟*,例如[0-9][0-9]*,除了它更短,一些正则表达式引擎可以更好地优化\+
  • @mklement0:是的,我通常使用fgrep 的固定字符串或grep -P 的PCRE 正则表达式,因为POSIX 正则表达式(基本和扩展)只是一个PITA。在这种情况下,我试图坚持使用简单的grep,主要是为了最大限度地减少可移植性问题(尽管很明显,我仍然使用了一些非严格的 POSIX 扩展,哎呀)。
  • 关于 POSIX 的最后说明:\+ 是 BRE 的 GNU 扩展,它也不符合 POSIX(以符合标准的方式模拟它需要笨拙的 \{1,\});令我惊讶的是,BSD/OSX 确实支持它,但与大多数其他 BSD 实用程序不同。
【解决方案2】:

您可以使用以下脚本:

cat file|grep age|awk '{for(i=1;i<22;i++){if($i ~ /^age:/)print $i}}'

【讨论】:

    【解决方案3】:

    你也可以使用 sed

        sed -nr 's/^.*(age:.).*$/\1/p'  input_pattern.txt
    

    input_pattern.txt 包含您的数据。

    【讨论】:

      【解决方案4】:

      ShadowRanger's simple grep-based answer 可能是最好的选择。

      适用于 GNU sed 和 BSD/OSX sed 的解决方案:

      sed -nE 's/^.*[[:blank:]](age:[0-9]+).*$/\1/p' file
      

      使用 GNU sed 您可以简化为:

      sed -nr 's/^.*\t(age:[0-9]+).*$/\1/p' file
      

      两个命令都匹配整个输入行,如果它包含感兴趣的age: 字段,则将其替换为捕获的字段(\1),并打印结果;其他行被忽略。


      在明确要求之前的原始答案:

      假设在存在age: 的行上,它始终是第二 制表符分隔字段,awk 是最佳解决方案:

      awk '$2 ~ /^age:/ { print $2 }' file
      
      • $2 ~ /^age:/ 仅匹配第二个空格分隔字段以文字 age: 开头的行
      • { print $2 } 只是打印该字段。

      【讨论】:

      • @once:知道了。下次请提前说明这样的要求。
      【解决方案5】:

      将正则表达式的搜索限制在第 11 到 23 列:

      awk '{ for(i = 11; i <= 23; i++) { if ($i ~ /^age:/) print $i } }' file
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2012-09-30
        • 2021-03-19
        • 1970-01-01
        • 2019-02-08
        • 2020-01-07
        • 2020-09-30
        • 2021-09-26
        • 1970-01-01
        相关资源
        最近更新 更多