【问题标题】:Print only fields of matched header starting after matched records仅打印匹配记录之后开始的匹配标题的字段
【发布时间】:2012-02-15 13:14:22
【问题描述】:

我正在尝试从我的文件中提取特定字段。基本上,输出字段只包含匹配的表达式,在匹配的记录之后开始输出。

这是我输入的一个示例。有时这些字段的顺序不同,并且在我尝试匹配的标题之前有不同的行数。

我很难找到如何使用 cut 和 sed 命令来实现这一点,也找不到 awk 方法。

CGATS.17
FORMAT_VERSION  1
KEYWORD "SampleID"
KEYWORD "SAMPLE_NAME"
NUMBER_OF_FIELDS    45
WEIGHTING_FUNCTION "ILLUMINANT, D50"
WEIGHTING_FUNCTION "OBSERVER, 2 degree"
BEGIN_DATA_FORMAT
SampleID    SAMPLE_NAME CMYK_C  CMYK_M  CMYK_Y  CMYK_K  LAB_L   LAB_A   LAB_B   nm380   nm390   nm400
END_DATA_FORMAT
NUMBER_OF_SETS  182
BEGIN_DATA
1   1   40  40  40  0   62.5    6.98    4.09    0.195213    0.205916    0.212827
2   2   0   40  40  0   73.69   25.48   24.89   0.200109    0.211081    0.218222
3   3   40  40  0   0   63.95   12.14   -20.91  0.346069    0.365042    0.377148
4   4   0   70  70  0   58.91   47.69   35.54   0.080033    0.084421    0.087317
END_DATA

这是我使用的脏代码,主要完成了这项工作,但没有字段标题条件搜索。 awk 命令只是删除输出周围的空行。

cut -f 7-9 -s input.txt | 
sed -E 's/(LAB_.)//g' |
awk 'NF' > file.txt

我期望的输出是这样的。它仍然是制表符分隔的,仅包含直接在 (LAB_) 下开始的字段的值。

62.5    6.98    4.09
73.69   25.48   24.89
63.95   12.14   -20.91
58.91   47.69   35.54

【问题讨论】:

  • +1,一个完美组织和格式化的问题!祝你好运。

标签: unix sed awk grep cut


【解决方案1】:

脚本:

#!/usr/bin/awk -f

# We look for line starting with BEGIN_DATA_FORMAT do the getline function and 
# store location of fields that have "LAB" in their name on the next line.

/^BEGIN_DATA_FORMAT/{
        getline
            for (i=1;i<=NF;i++) 
                    if ($i~/LAB/) a[i]=$i
                } 

# In this regex range we look for lines that have more than 2 fields. For those 
# lines we loop thru each field and see if the location matches to the ones 
# captured in our earlier array (i.e location number of fields that have "LAB" 
# in their name). If we find a match we print those fields. 

/^BEGIN_DATA$/,/^END_DATA$/{
             s="";
             if (NF<2) next; else 
                for (j in a)
            s=s?s"\t"$j:$j
            print s; 
                 }

测试:

[jaypal:~/Temp] ./script.awk file
62.5    6.98    4.09    
73.69   25.48   24.89   
63.95   12.14   -20.91  
58.91   47.69   35.54   

【讨论】:

  • 感谢您的即时回复!它适用于我发布的带有缩短字段的输入数据,但不适用于我拥有的大文件。我发现这是因为我的行尾是 CRLF。它确实适用于LF!结果还删除了制表符间距以及顶部的一个空行和底部的两行空格字符。
  • 您可以根据您的文件进行修改。在 for 循环中使用值。请注意您要开始捕获的列和您想要的最后一列。然后您可以将这些值放入 for 循环中。
  • 您可以使用 dos2unix 实用程序删除 CRLF。对于空行,我可以在几小时内给你一个更新的答案。手边没有贝壳!
  • 对于空行试试这个/^BEGIN_DATA$/,/^END_DATA$/ &amp;&amp; NF {for... 如果它不起作用那么我回家后会给你一个有效的解决方案。
  • 感谢您帮助我。我不确定我是否以最好的方式描述它,但我正在尝试找到一种无需设置字段实际数量的技术。我对循环中语法的经验是全新的,所以也许你是说我可以通过匹配 (LAB_) 来玩弄它。
【解决方案2】:

另一个 awk 脚本:

 awk '/^BEGIN_DATA_FORMAT/{getline;f=NF;for(i=1;i<=NF;i++)if($i~/^LAB_[LAB]/)l[i]++;} 
/^BEGIN_DATA/,/^END_DATA/ && NF==f{s=""; for(x in l)s=s?s"\t"$x:$x; print s;}' input

示例输入的输出:

62.5    6.98    4.09
73.69   25.48   24.89
63.95   12.14   -20.91
58.91   47.69   35.54

上面 awk 脚本的一些注释:

  • 标头处理与@JayPal 的解决方案类似,但略有不同 不同:您提到列顺序可能不同,所以 对于标题匹配,我的 awk 脚本查找了下一行 “BEGIN_DATA_FORMAT”。因为标题行的第一列可以 SampleID 以外的其他内容。

  • 在输出中,如您所料,仅打印值([tab] 分隔), 但没有标题。如果你说列的顺序可能是可变的,你 可能会丢失标题信息。比如说,哪一列是 LAB_L,哪一列是 A?等等。 如果真的需要,这很容易做到。

【讨论】:

  • 再次感谢@Kent 的反馈。对此答案 +1。
  • QQ @Kent: 可以解释一下你使用三元运算符s=s?s"\t"$x:$x; print s;的部分吗?它比我在解决方案中使用printf 然后print "" 获得换行符要好得多。
  • @JaypalSingh 它将首先构造字符串,然后将其打印出来,并通过打印语句使用 '\n'。 s=s?s"\t"$x:$x 表示,如果 s 不为空,则在值之间连接一个 '\t' ($x);如果 s 为空,则 $x 将是第一个值,因此我们不需要前导 '\t'。 s 的初始值为空 ("")。所以第一次测试s?会是假的。
【解决方案3】:

这可能对你有用:

 sed '/^BEGIN_DATA\>/,/^END_DATA\>/{//d;s/\(\S*\s*\)\{6\}\(\S*\s*\S*\s*\S*\).*/\2/p};d' file

或留在cut

cut -f7-9 file | sed '/^\([-.0-9]*\s*[-.0-9]*\s*[-.0-9.]*$\)/!d'

或者(但我在这里猜测您输入文件的格式):

sed 's/\s*$//' file | cut -f7-9 | sed '/^BEGIN_DATA$/,/^END_DATA$/{//d;p};d'

【讨论】:

    猜你喜欢
    • 2010-12-18
    • 1970-01-01
    • 2020-06-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-06-05
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多