【问题标题】:Search for the first occurrence of a keyword + print the next column in Linux在 Linux 中搜索关键字的第一次出现 + 打印下一列
【发布时间】:2020-06-10 04:04:43
【问题描述】:

假设我有一个这样的文件:

d,e,c,g,v,c,w,r
g,c,d,c,s,c,g,r
d,y,c,w,t,g,c,f

现在我想打印出现在每行中第一个“c”之后的列(不带逗号分隔符)。所以我的输出看起来像这样

g
d
w

我已经尝试了代码:

awk -F"," '{for (i=1;i<=NF;i++) if ($i == "c") {print $(i+1)};}' filename

但在输出中,我得到了出现在每个“c”之后的列。我只想要出现在第一个 'c' 之后的列。 如何解决问题最好使用awk

提前致谢

【问题讨论】:

  • 使用break....
  • ... 终止 for 循环 print $(i+1); break
  • 如果 c 是最后一列怎么办?
  • 您在主题中说first occurrence of a keyword - 请注意“keyword”。如果您的真实数据和目标字符串中的列可以超过单个字符,那么更新您的示例以显示由于否定多字符字符串与否定单个字符是一个非常不同的问题,所以大多数答案都是这样远不适用。还包括一行没有c 的情况,其中c 是第一个字符,c 是该行的最后一个字符。

标签: awk


【解决方案1】:

sed 解决方案:

sed -n 's/[^c]*c,\([^,]\).*/\1/p' filename

RegEx101 running this

【讨论】:

  • 据我所知,[^c]* 在这里没有任何区别,因为它也可以匹配零次
  • @Sundeep [^c]*c 有很大的不同。它保证只匹配第一个c 列。没有它,sed 将在最后一个 c 列中捕获。 (用 Gnu sed 4.7 版测试)。
  • 哎呀,我的错,我忘了这是替换..我在发表评论时考虑的是行匹配..所以这个解决方案只适用于单字符列
【解决方案2】:

使用 awk 关键字next 跳到每行第一个找到的“c”之后的下一行:

$ awk -F"," '{for (i=1;i<=NF;i++) if ($i == "c") {print $(i+1);next};}' filename
g
d
w

【讨论】:

    【解决方案3】:

    考虑到每行只有一个c 会出现。请您尝试以下操作。这不需要循环 + 它会在此处逐行查找小写或大写字母 c。

    awk 'match($0,/[cC],[^,]*/){
      print substr($0,RSTART+2,RLENGTH-2)
    }
    ' Input_file
    

    解释:在这里我提到正则表达式从小/大写字符匹配,然后是逗号,直到下一次出现逗号。如果此正则表达式具有匹配值,则将设置名为 RSTART 和 RLENGTH 的变量。其中 RSTART 告诉正则表达式的开始,而 RLENGTH 告诉匹配的正则表达式的总长度。从当前行获取这些值打印子字符串。

    【讨论】:

      【解决方案4】:

      另一种选择:

      $ awk -F'c,' '{ print $2 }' < filename |cut -d, -f1
      g
      d
      w
      

      【讨论】:

        【解决方案5】:

        使用 GNU awk:

        awk '{split($2,array,","); print array[2]}' FS="c" file
        

        输出:

        G d w

        我使用 awk 的字段分隔符 (FS) 将带有 c 的行分成两部分($1$2)。使用 split 我将第二部分 ($2) 拆分,然后将 , 拆分为数组中的多个部分 (array) 并打印第二个元素。

        【讨论】:

          【解决方案6】:

          ripgrep

          $ rg -No 'c,([^,]+).*' -r '$1' ip.txt
          g
          d
          w
          
          $ # if you only want to match whole column
          $ rg -No '(^|,)c,([^,]+).*' -r '$2' ip.txt
          g
          d
          w
          
          • -N 禁用输出中的行号前缀
          • ([^,]+)抓取栏目内容
          • .* 匹配之后的所有内容,以避免在一行中出现多个匹配
          • -r '$1' 仅将匹配部分替换为捕获组的内容
          • (^|,) 确保仅匹配整列

          【讨论】:

            【解决方案7】:

            sed / RegEx 答案类似于

            sed 's/[^c]*,c,\([^,]*\),.*/\1/' filename > outfile
            

            也应该适用于多字符条目。

            【讨论】:

            • 如果行不包含c,则打印所有行
            【解决方案8】:

            perl 一个:

            perl -pe 's/^.*?c,(.).*/\1/g,' filename
            

            如果不能保证输入的每一行都包含c,那么这个版本会过滤掉所有不包含c的行:

            perl -ne 'if (/c/) {s/^.*?c,(.).*/\1/g,; print}' filename
            

            【讨论】:

            • 如果行不包含c,则打印所有行
            • @LéaGris 这个问题是假设每一行都会有一个c,但为了回应你的评论,我添加了一个版本,它将排除任何不存在的行。
            猜你喜欢
            • 1970-01-01
            • 2014-02-15
            • 2020-03-09
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2023-03-09
            相关资源
            最近更新 更多