【问题标题】:Find delimiter and print second array查找分隔符并打印第二个数组
【发布时间】:2017-02-03 17:05:27
【问题描述】:

我有这样的数据:

A=5 B=8 . C=8
A=4 . . C=4
A=8 . . C=3

我需要检查文件中的所有列,如果找到letter=number(例如 A=5),我只需要打印number。如果缺少值 (.),请跳过它。

所以输出应该是:

5 8 . 8
4 . . 4
8 . . 3

我会使用 awk:

awk -v OFS="\t" '{split($0,arr2,"="); print arr2[2]}' infile.txt

【问题讨论】:

  • 不,它不是固定的。列数是可变的。

标签: arrays bash awk split


【解决方案1】:

使用 sed 是一种快速而肮脏的方法:

sed -e 's/[A-Z]\+=//g' infile.txt

这只是删除一个(一组)字母后跟等号的所有实例。

【讨论】:

  • 值得注意的是,\+ 是非标准的,因此在某些版本上需要-E+(或者只使用*,因为它也可以)。
【解决方案2】:

使用 GNU awk 你可以这样做:

$ awk -F= -vRS='[ \t\n]+' '{printf "%s%s", (NF>1?$NF:$0), RT}' file
5 8 . 8
4 . . 4
8 . . 3

Awk 将它的输入分隔到记录中,并将记录分隔到字段中。使用-vRS='[ \t\n]+',我们将记录分隔符设置为一系列空格、制表符或换行符。我们还将带有-F= 的字段分隔符设置为等号。所以本质上我们将输入分成记录,每条记录是一个单词,然后我们使用= 作为分隔符将记录分割成字段。

通过这部分'{printf "%s%s", (NF>1?$NF:$0), RT}',我们打印所需的输出。对于每条记录,我们可以有两种情况:如果字段数(变量NF)大于一个,这意味着我们有一个等于,所以我们将记录的最后一个字段保留为$NF。如果记录有一个字段(不等于),我们保持字段不变。我们先打印想要的值,然后打印分割字段时匹配的分隔符,存储在RT变量中。

【讨论】:

  • 您好,谢谢您的回复,看起来一切正常。但我需要复制/粘贴我的 infile.xls 的名称 - 当我按 TAB 时,它不会显示我的文件。哪里可能有问题? AWK 版本?
  • @Paul:awk 版本不应该与 tab-completion 有任何关系,我认为该功能来自 bash。
  • 但您的脚本仍然可以正常工作。可以评论部分吗?什么是 vRS?反转录? :))
  • @Paul RS 代表记录分隔符,它是一个正则表达式,用于指定如何拆分记录。 RT 是特定记录的记录分隔符的值。我添加了几段来解释这是如何工作的。如果还有什么不清楚的,请告诉我。
【解决方案3】:

awk 对字段进行交互,如果字段以字母开头,则从开头剥离到 = 的部分,然后是 =,然后是末尾的任何数字:

awk '{for(i=1; i<=NF; i++) if($i~/^[A-Za-z]=[0-9]+$/) sub("^.*=", "", $i)}{print}'

如果你是一个单词而不是一个单词:

awk '{for(i=1; i<=NF; i++) if($i~/^[A-Za-z]+=[0-9]+$/) sub("^.*=", "", $i)}{print}'

示例:

$ cat file.txt
A=5 B=8 . C=8
A=4 . . C=4
A=8 . . C=3

$ awk '{for (i=1; i<=NF; i++) if ($i~/^[A-Za-z]=[0-9]+$/) sub("^.*=", "", $i)} { print }' file.txt  
5 8 . 8
4 . . 4
8 . . 3

【讨论】:

  • 感谢您的回复。当我在大量数据上测试您的解决方案时,它不起作用。实际上在真实的数据集中不是 A、B、C...等,而是有真实的词,例如 - IMPACT=151、ENSP=544、PHENO=512...等。
  • 运行脚本后仍然有没有修改的文件:-(。文件是制表符分隔的。有什么想法吗?
  • @Paul 我们没有编辑文件,只是打印输出。如果你得到想要的结果,你可以这样做:awk '{for(i=1; i&lt;=NF; i++) if($i~/^[A-Za-z]+=[0-9]+$/) sub("^.*=", "", $i)}{print}' file.txt &gt;output.txt 将它保存在文件output.txt 或者如果你有GNU awkawk -i inplace '{for(i=1; i&lt;=NF; i++) if($i~/^[A-Za-z]+=[0-9]+$/) sub("^.*=", "", $i)}{print}' file.txt
  • 抱歉翻译错误。我的意思是仍然有没有变化的输出。当我删除 IF 语句时,它看起来你的脚本有效 - awk -v OFS="\t" '{for(i=1; i
  • @Paul 是的,就您的示例而言,应该可以工作。
【解决方案4】:

这个 awk 单行与 gawk 一起工作:

awk -F'\\S*=' -v OFS="" '{$1=$1}7' file
5 8 . 8
4 . . 4
8 . . 3

想法是:通过设置FS删除FOO=

【讨论】:

    【解决方案5】:
    awk '{gsub(/[A-Z]=/,"")}1' file
    
    5 8 . 8
    4 . . 4
    8 . . 3
    

    【讨论】:

    • gsub 功能很好用...谢谢回复!
    猜你喜欢
    • 2014-05-27
    • 1970-01-01
    • 1970-01-01
    • 2013-05-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多