【问题标题】:Separating a number within a column in awk or grep在 awk 或 grep 中分隔列中的数字
【发布时间】:2015-01-08 05:14:34
【问题描述】:

我正在编写一个脚本,它将处理所有列中相似的文件,但第一个,$1。我要重定向到另一个文件的内容取决于第三列的值,并且我知道如何区分,但我想重定向第一列中的“id”-number,而不是整个第一列:

不同的输入文件格式:

1452123_s_at        0.45609 1.55e-04    7.85    -2.89   2.657
145243_s_at         0.35709 1.46e-04    7.7     -2.9    2.713

Xl.15267.1.A1_at    0.45609 1.79e-04    7.66    -2.9    2.21
Xl.14257.1.A1_at    0.76509 1.67e-04    7.85    -2.87   2.23

160919_r_at         0.45609 1.83e-04    -7.63   -2.9    -2.888
145916_r_at         0.41869 3.82e-04    -7.56   -2.8    -2.798

162334_r_at         0.51869 2.49e-04    -7.24   -2.93   -2.095
15356_r_at          0.68229 1.79e-04    -7.45   -2.88   -2.5

160365_at           0.68223 3.82e-04    -6.72   -2.98   -1.795
16345_at            0.45623 2.94e-04    -5.99   -2.45   -1.568

26768               0.51869 1.83e-04    7.66    -2.9     2.21
30075               0.67749 1.46e-04    7.45    -2.89    2.34

期望的输出:

1452123     1.55e-04    
145243      1.46e-04    
15267       1.79e-04    
14257       1.67e-04    
160919      1.83e-04    
145916      3.82e-04    
162334      2.49e-04    
15356       1.79e-04    
160365      3.82e-04    
16345       2.94e-04    
26768       1.83e-04    
30075       1.46e-04    

这个数字几乎可以是 1-10 000 000 之间的任何值,整个第一列的结构可能比这个例子多一点,但它总是在其中的某个地方有这个数字。有没有办法写出足够通用的东西来识别和打印这个数字?通过使用 split 还是以某种方式可能?

不管使用哪个程序,awk、grep 或 sed,我只是在寻找最有效的方法。 我对命令行也很陌生,所以请解释清楚和不同的命令! 谢谢

【问题讨论】:

  • 除了 . 之外还能不能有其他符号?和 _ 将 id 与第一列的其余部分分开?
  • Xl.15267.1.A1_at 中,脚本如何判断15267 是所需的数字而不是1?因为它是行中的第一个数字?因为它最大?因为它有多个数字?还有什么?
  • 我认为除了 .和 _ 但我不能确定

标签: awk grep string-split


【解决方案1】:

只需使用gsub() 删除非数值然后打印:

awk 'NF{gsub(/[^0-9]/,"",$1); print $1, $3}' file

返回:

1452123 1.55e-04
145243 1.46e-04
1526711 1.79e-04
1425711 1.67e-04
160919 1.83e-04
145916 3.82e-04
162334 2.49e-04
15356 1.79e-04
160365 3.82e-04
16345 2.94e-04
26768 1.83e-04
30075 1.46e-04

说明

  • NF{} 中执行以下命令,只要NF 为真,即该行不为空。
  • gsub(/[^0-9]/,"",$1) 对于第一个字段,删除不在0-9 范围内的所有字符。也就是说,删除所有非数值。
  • print $1, $3 打印第一个和第三个字段。

【讨论】:

  • 第 2 行和第 3 行不匹配。
  • 它们包含一个额外的 1,来自 Xl.15267.1.A1_at 上的尾随 1
  • @fedorqui 我明白他们为什么在那里。但它们不符合 OP 的要求。
  • @fedorqui:也许前 2 位以上的数字可以解决问题。 SG。像match($1,/([0-9]{2,})/),然后substr($1,RSTART, RLENGTH) 给出正确的数字。对于gawk,可能需要--re-interval...
  • 好吧,是的,对不起,会的!我认为最后一个是我正在寻找的。谢谢
【解决方案2】:

我假设这些模式属于一个有限集。所以可以列出模式。为了简化流程,我创建了一个 版本:

awk '
NF && ( match($1,/^([0-9]+)((_[rs])?_at)?$/,a) ||
match($1,/^Xl\.([0-9]+)\.1\.A1_at$/,a) ) {
    printf("%-12s%-s\n", substr($1, a[1,"start"], a[1,"length"]), $3)
}
' inputfile

第一个 match 检查四种模式:<NUM>_s_at<NUM>_r_at<NUM>_at<NUM>。最后一个Xl.<NUM>.1.A1_at。然后切断匹配的数字并格式化输出。

输出:

1452123     1.55e-04
145243      1.46e-04
15267       1.79e-04
14257       1.67e-04
160919      1.83e-04
145916      3.82e-04
162334      2.49e-04
15356       1.79e-04
160365      3.82e-04
16345       2.94e-04
26768       1.83e-04
30075       1.46e-04

【讨论】:

    【解决方案3】:

    我将拆分非数字字符,然后选择剩下的最大数字。下面是我的实现,感谢@fedorqui 的 NF 技巧

    NF{n=split($1,a,/[^0-9]+/); v=a[1]; for(i=2; i<=n; i++) { if (v<a[i]) v=a[i]; } print v, $3}
    

    【讨论】:

      【解决方案4】:

      使用 GNU awk 进行 gensub():

      $ awk 'NF{ print gensub(/([^._]*[._])?([[:digit:]]+).*/,"\\2","",$1), $3 }' file
      1452123 1.55e-04
      145243 1.46e-04
      15267 1.79e-04
      14257 1.67e-04
      160919 1.83e-04
      145916 3.82e-04
      162334 2.49e-04
      15356 1.79e-04
      160365 3.82e-04
      16345 2.94e-04
      26768 1.83e-04
      30075 1.46e-04
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2020-12-02
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-11-28
        • 1970-01-01
        • 2018-08-23
        相关资源
        最近更新 更多