【问题标题】:Linux: Substitute text fields with values from other fileLinux:用其他文件中的值替换文本字段
【发布时间】:2016-09-20 14:38:24
【问题描述】:

好吧,我不知道该怎么做。

我有一个看起来像这样的 FileA:

([7]RIMS_ID)                                            : "CNR"       
(refGain_A[7])                                          : 1           
(RIMSclockBias_A[7])                                    : -398015316.7
(RIMSclockDrift_A[7])                                   : -6442.29    
(RIMSclockSigma_A[7])                                   : .01         
(RIMSclockSigY_A[7])                                    : 0        

([8]RIMS_ID)                                            : "ABS"       
(refGain_A[8])                                          : 1           
(RIMSclockBias_A[8])                                    : -374515458
(RIMSclockDrift_A[8])                                   : -6442.29    
(RIMSclockSigma_A[8])                                   : .01         
(RIMSclockSigY_A[8])                                    : 0     

以此类推,[index] 从 0 到 71,每个站都有不同的 ID。

我想用我在另一个文件 B 上的值替换每个站的 RIMSclockBias_A 字符串对应的值,如下所示:

CNR -44163754.49
ABS 3417370.112
...

所以有:

([7]RIMS_ID)                                            : "CNR"       
(refGain_A[7])                                          : 1           
(RIMSclockBias_A[7])                                    : -44163754.49
(RIMSclockDrift_A[7])                                   : -6442.29    
(RIMSclockSigma_A[7])                                   : .01         
(RIMSclockSigY_A[7])                                    : 0        

([8]RIMS_ID)                                            : "ABS"       
(refGain_A[8])                                          : 1           
(RIMSclockBias_A[8])                                    : 3417370.112
(RIMSclockDrift_A[8])                                   : -6442.29    
(RIMSclockSigma_A[8])                                   : .01         
(RIMSclockSigY_A[8])                                    : 0 

我可以使用 for 循环中的 grep 和 awk 的组合来隔离 fileA 中的正确字段,但我不知道如何替换文件本身中的值。 也许我应该使用 Perl,但我没有任何经验。 任何帮助将不胜感激。

谢谢!

【问题讨论】:

    标签: linux awk scripting text-processing


    【解决方案1】:

    在我看来更具可读性:

    BEGIN { FS = " *:? *" }
    NR == FNR { bias["\"" $1 "\""] = $2; next }
    /RIMS_ID/ { key = $2; }
    /RIMSclockBias_A/ { sub($2, bias[key]); }
    { print }
    

    现在,如果您想将替换限制为特定范围的 ID,例如 57 到 64,您可以使用以下内容:

    BEGIN { FS = " *:? *" }
    NR == FNR { bias["\"" $1 "\""] = $2; next }
    /RIMS_ID/ { key = $2; }
    /\[57\]RIMS_ID/ { substitute = 1 }
    /\[65\]RIMS_ID/ { substitute = 0 }
    /RIMSclockBias_A/ && substitute { sub($2, bias[key]); }
    { print }
    

    所以,一旦遇到 ID 57,我们将 substitute 设置为 true,一旦遇到 ID 65,我们将 substitute 设置为 false,并且我们仅在该变量为 true 时执行替换.

    【讨论】:

    • 这个版本其实我读得更好。我把它写在一个文件 script.awk 中,然后调用: awk -f script.awk fileB fileA
    • 有没有办法仅对 fileA 的特定索引范围(假设在 0 到 30 之间)应用替换?忽略之后发生的所有其他事情?
    • 是的,这是可能的。您可以在遇到特定模式时设置和取消设置变量,并且仅在设置变量时执行替换。
    • 好的,我知道了!谢谢@Michael Vehrs
    【解决方案2】:

    一个不太健壮的 awk 脚本

    $ awk -v q='"' 'NR==FNR{a[q $1 q]=$2;next} 
                    $3 in a{v=a[$3]; f=1} 
       /\(RIMSclockBias_A\[[0-9]+\]\)/ && f {sub($3,v); f=0}1' map file
    
    ([7]RIMS_ID)                                            : "CNR"
    (refGain_A[7])                                          : 1
    (RIMSclockBias_A[7])                                    : -44163754.49
    (RIMSclockDrift_A[7])                                   : -6442.29
    (RIMSclockSigma_A[7])                                   : .01
    (RIMSclockSigY_A[7])                                    : 0
    
    ([8]RIMS_ID)                                            : "ABS"
    (refGain_A[8])                                          : 1
    (RIMSclockBias_A[8])                                    : 3417370.112
    (RIMSclockDrift_A[8])                                   : -6442.29
    (RIMSclockSigma_A[8])                                   : .01
    (RIMSclockSigY_A[8])                                    : 0
    

    file 是数据文件,map 是查找值。假设您的结构是固定的(相同数量的字段、字段的间距和顺序等)。

    【讨论】:

    • 您应该说明它在哪些方面不健壮,因为它不明显并且不会显示在示例输入中。例如,它会将(RIMSclockBias_A[7]) : 7 转换为(RIMSclockBias_A[-44163754.49]) : 7,并且在给定其他输入值的情况下,它还可以通过其他不明显的方式悄悄地做一些意想不到的事情。
    • 没有块级完整性。如果一个块缺少目标字段,而下一个块缺少匹配字段。替换将是错误的。我也认为您的脚本也是如此。也许,RS= 可以设置并遍历每条记录的字段,但我想让 OP 提出问题。
    • 恕我直言,假设所有字段都将存在是安全的,因为它们总是在发布的示例输入中,让 OP 告诉我们其他情况,但是不能假设任何关于随后数据的值发布的示例输入的相同格式(例如数字)而不说明这些假设。
    • 给出:“[index] 从 0 到 71 的位置”
    • 但没有给出的是“$3 的值不能是 0 到 71 之间的整数”,这是使脚本更健壮的要求之一。听着,我要说的是,如果发生某些不明显的事情会破坏解决方案,并且在真实数据中发生的可能性很小,那么我们应该说明它,仅此而已。
    【解决方案3】:
    $ cat tst.awk
    NR==FNR { map["\""$1"\""]=$2; next }
    /^\(\[/ { key = $NF }
    /^\(RIMSclockBias_A\[/ && key in map { sub(/[^[:space:]]+[[:space:]]*$/,map[key]) }
    { print }
    
    $ awk -f tst.awk fileB fileA
    ([7]RIMS_ID)                                            : "CNR"
    (refGain_A[7])                                          : 1
    (RIMSclockBias_A[7])                                    : -44163754.49
    (RIMSclockDrift_A[7])                                   : -6442.29
    (RIMSclockSigma_A[7])                                   : .01
    (RIMSclockSigY_A[7])                                    : 0
    
    ([8]RIMS_ID)                                            : "ABS"
    (refGain_A[8])                                          : 1
    (RIMSclockBias_A[8])                                    : 3417370.112
    (RIMSclockDrift_A[8])                                   : -6442.29
    (RIMSclockSigma_A[8])                                   : .01
    (RIMSclockSigY_A[8])                                    : 0
    

    【讨论】:

    • 感谢 Ed Morton,无论如何它不起作用。我按原样运行您的代码,它什么也没做(没有替换)。我是不是忘记了什么?
    • 如果我的解决方案不起作用,那么您的 awk 不符合 POSIX。例如,如果您使用的是 nawk 或 mawk,它们不支持像 [[:space:]] 这样的 POSIX 字符类,因此您需要使用像 [ \t] 这样的硬编码字符列表。尝试 sub(/[^ \t]+[ \t]*$/,map[key]) 而不是 sub(/[^[:space:]]+[[:space:]]*$/,map[key]) 并考虑获得 POSIX awk(最好是 GNU awk 4.1.*)。
    猜你喜欢
    • 2011-10-20
    • 2016-02-07
    • 1970-01-01
    • 1970-01-01
    • 2021-11-20
    • 1970-01-01
    • 2018-07-02
    • 2014-11-18
    • 2021-06-15
    相关资源
    最近更新 更多