【问题标题】:unix shell: replace by dictionaryUnix shell:用字典替换
【发布时间】:2011-06-17 12:16:53
【问题描述】:

我有一个包含一些数据的文件,像这样

2011-01-02 100100 1 
2011-01-02 100200 0
2011-01-02 100199 3
2011-01-02 100235 4

并在单独的文件中有一些“字典”

100100 Event1
100200 Event2
100199 Event3
100235 Event4

我知道

0 - warning
1 - error
2 - critical
etc...

我需要一些带有 sed/awk/grep 的脚本或其他可以帮助我接收此类数据的脚本

100100 Event1 Error
100200 Event2 Warning
100199 Event3 Critical
etc

将不胜感激如何以最佳方式做到这一点的想法,或工作示例

更新

有时我有这样的数据

2011-01-02 100100 1
2011-01-02 sometext 100200 0
2011-01-02 100199 3
2011-01-02 sometext 100235 4

其中 sometext = 任意 6 个字符(也许这是有用的信息)
在这种情况下,我需要整个数据:

2011-01-02 sometext EventNameFromDictionary Error

或者没有“sometext”

【问题讨论】:

    标签: shell sed awk grep


    【解决方案1】:
    awk 'BEGIN {
     lvl[0] = "warning"
     lvl[1] = "error"
     lvl[2] = "critical"
     }
    NR == FNR {
      evt[$1] = $2; next
      } 
    {
      print $2, evt[$2], lvl[$3]
      }' dictionary infile
    

    【讨论】:

    • 哇,权力太大了。 NR = FNR?唯一的缺陷是它处理不存在的查找键的方式相对无用。
    • 谢谢,很好的例子,这正是我需要的。如果有人有其他想法 - 将不胜感激(我会将它们用于自学)
    • @sehe, NR == FNR 仅在读取第一个非空输入文件(本例中为字典)时计算为 true。就不存在的键而言,OP提到了1、2 ...等,所以我认为它们都是现有的键。使用$2 in evt ? evt[$2] : $2 ... 可以轻松修复它
    • 如果有时我在 INFILE 中有更多列,我该如何更新该脚本?在示例中有 3 列,但在实际数据中有时我有 4 列(日期列之后的第二列),在这种情况下,我只得到第一列作为结果
    • @Vitaliy,请发布示例输入和所需输出的示例。
    【解决方案2】:

    为新要求添加新答案,因为评论中的格式选项有限:

    awk 'BEGIN {
     lvl[0] = "warning"
     lvl[1] = "error"
     lvl[2] = "critical"
     }
    NR == FNR {
      evt[$1] = $2; next
      } 
    {
      if (NF > 3) {
        idx = 3; $1 = $1 OFS $2
        }
      else idx = 2  
      print $1, $idx in evt ? \
        evt[$idx] : $idx, $++idx in lvl ? \
          lvl[$idx] : $idx
      }' dictionary infile
    

    如果您使用的是 GNU awk,则无需转义三级运算符中的新行。

    一些 awk 实现可能对这部分有问题:

    $++idx in lvl ? lvl[$idx] : $idx
    

    如果您使用其中之一, 将其更改为:

    $(idx + 1) in lvl ? lvl[$(idx + 1)] : $(idx + 1)
    

    好的,已添加 cmets:

    awk 'BEGIN {
     lvl[0] = "warning"       # map the error levels
     lvl[1] = "error"                
     lvl[2] = "critical"      
     }                        
    NR == FNR {               # while reading the first
                              # non-empty input file
      evt[$1] = $2          # build the associative array evt
      next                    # skip the rest of the program
                              # keyed by the value of the first column
                              # the second column represents the values
      }                       
    {                         # now reading the rest of the input
      if (NF > 3) {           # if the number of columns is greater than 3
        idx = 3               # set idx to 3 (the key in evt)
        $1 = $1 OFS $2       # and merge $1 and $2
        }                     
      else idx = 2            # else set idx to 2
      print $1, \              # print the value of the first column
        $idx in evt ? \    # if the value of the second (or the third,
                      \       # depeneding on the value of idx), is an existing
                      \       # key in the evt array, print its value
        evt[$idx] : $idx, \ # otherwise print the actual column value
        $++idx in lvl ?   \   # the same here, but first increment the idx 
         lvl[$idx] : $idx       # because we're searching the lvl array now     
      }' dictionary infile
    

    【讨论】:

    • 谢谢,它有效,但让我抓狂))我尝试用其他数量的列测试它,但不明白原理。无论如何 - 谢谢。
    • 谢谢,我已经简化了,现在符合我的标准。
    【解决方案3】:

    我希望 perl 也可以:

    #!/usr/bin/perl
    use strict;
    use warnings;
    
    open(DICT, 'dict.txt') or die;
    my %dict = %{{ map { my ($id, $name) = split; $id => $name } (<DICT>) }};
    close(DICT);
    
    my %level = ( 0 => "warning", 
                  1 => "error",
                  2 => "critical" );
    
    open(EVTS, 'events.txt') or die;
    
    while (<EVTS>)
    {
        my ($d, $i, $l) = split;
        $i = $dict{$i}  || $i;  # lookup
        $l = $level{$l} || $l;  # lookup 
        print "$d\t$i\t$l\n";
    }
    

    输出:

    $ ./script.pl
    2011-01-02      Event1  error
    2011-01-02      Event2  warning
    2011-01-02      Event3  3
    2011-01-02      Event4  4
    

    【讨论】:

    • 我只喜欢shell,但感谢您的帮助,我会保存它以备将来研究
    • Soooo...awk、sed、grep 什么时候变成了“shell”?我理解你,但称它为“仅限外壳”是用词不当