在 AWK 中编辑带有空格的浮动特定点字段 Preserving/Padding?

Edit Floating Specific Point Field Preserving/Padding w/ Whitespace in AWK?

我有一个包含以下行的文件:

CH1 12.30 4.800 12 !

我想用 [0.0,1.0) 上选定的浮点标量缩放的一些等效项替换特定字段...比如 </code>。但是,我想保留相同的小数位数,并进一步在前端填充空格以保持原始长度。</p> <p>我认为 <code>awk 中的某些组合 len/gsub/printf 可以实现这一点。

举个我目前尝试过的例子:
scalar=0.00; echo 'CH1 12.30 4.800 12 !' | awk -v sc=$scalar '/CH1/{gsub(/[0-9]*\.[0-9]*/,*sc,);} {print;}'

输出:

CH1 0 4.800 12 !

输出: 正确输出缩放的 #,但空格不仅从字段 </code> 中删除,而且从整行中删除。</p> <p><code>scalar=0.00; echo 'CH1 12.30 4.800 12 !' | awk -v sc=$scalar '/CH1/{gsub(//,*sc,[=21=]);} {print;}'

输出:

CH1 12.30 4.800 12 !

备注: 什么也没做!输出不变。

假设:

  1. 字段 </code> 和 <code> 可能相同,但我只想更改字段 </code>.</li> <li>字段 <code> 仅包含字母数字字符。
  2. 字段 </code> 和 <code> 是具有任意小数位数的浮点数,通常小数位数在 [1,4] 范围内。整个部分不超过3位数..
  3. 字段 </code> 是 <code>[8,99].
  4. 范围内的整数
  5. 字段 之后的任何内容都是注释,可能包含特殊字符。

在搜索类似问题时,我遇到了一些与保留空格有关的问题,这些问题给了我一些想法……但我的有点不同,因为我实际上想添加空格,以保留小数位有效锁定在线的同一位置,使目标文件中的用户格式保持良好。

gsub(//,...) 表达式失败,因为 // 正在寻找文字 </code> 字符串,而不是字段 2 中的任何内容。(并且 <code>gsub 是矫枉过正,因为我们只改变一个实例,所以简单的 sub 就足够了,但是 gsub 在这里是无害的。)

我们可以只使用 </code>(没有斜杠,尽管它将被视为正则表达式而不是文字字符串):</p> <pre><code>$ scalar=0.00; echo 'CH1 12.30 4.800 12 !' | awk -v sc=$scalar '/CH1/{gsub(,*sc);} {print;}' CH1 0 4.800 12 !

这也失去了小数位的东西,所以仍然不是我们想要的,但表明你的方法是可行的。

鉴于sprintf()可以根据"%5.2f"这样的格式指令生成一个字符串(这是我们想要得到的12.30),我们需要做的就是图得出字段的总长度 </code> 和小数部分的长度(在 <code>. 之后),这很容易使用 splitlength。构建替换字符串甚至比乍看起来更容易,因为我们可以使用 * 来提取整数参数,而不是文字 52。因此:

$ cat foo.sh
#! /bin/sh

scalar=0.00
echo 'CH1        12.30       4.800      12   !'
echo 'CH1        12.30       4.800      12   !' |
    awk -v sc=$scalar '
 ~ /[0-9]*\.[0-9]*/ {
    split(, parts, /\./)
    ofraclen = length(parts[2])
    repl = sprintf("%*.*f", length(), ofraclen,  * sc)
    sub(/[0-9]*\.[0-9]*/, repl)
}
{print}
'
$ sh foo.sh
CH1        12.30       4.800      12   !
CH1         0.00       4.800      12   !

我输入了额外的 echo 以便我们可以看到字段仍然排成一行。我将匹配条件更改为 ~ ... 以保证 </code> 将正确拆分。我们将其拆分为整数和小数部分,获取小数部分的长度,生成替换字符串,然后在(第一次)出现浮点数时使用 <code>sub(安全当且仅当字段 </code> 从不匹配,没有 $1 匹配的测试,如果是这样,我们将 <code>sub 匹配错误。

(我其实很喜欢每条语句后面的分号,但我把它们都去掉了,因为它们不是严格要求的。另外,大部分临时变量都可以去掉,只保留 parts,但是结果将很难理解。)

这是在对某些字段进行操作后从输入中再现填充的一般方法:

$ cat tst.awk
NR==1 {
    # Find the width of each space-padded, right-aligned field:
    rec = [=10=]
    for (i=1; i<=NF; i++) {
        match(rec,/[^[:space:]]+/)
        w[i] = RSTART - 1 + RLENGTH
        rec = substr(rec,w[i]+1)
    }

    # Find the precision of the target field:
    match(,/\..*/)
    p = RLENGTH - 1
}

{
    # print the original just for comparison
    print

    # do the math:
     = sprintf("%.*f", p,  * scalar)

    # print the updated record:
    for (i=1;i<=NF;i++) {
        printf "%*s", w[i], $i
    }
    print ""
}

.

$ awk -v scalar=0 -f tst.awk file
CH1        12.30       4.800      12   !
CH1         0.00       4.800      12   !

$ awk -v scalar=0.5 -f tst.awk file
CH1        12.30       4.800      12   !
CH1         6.15       4.800      12   !

$ awk -v scalar=9 -f tst.awk file
CH1        12.30       4.800      12   !
CH1       110.70       4.800      12   !

无论标量的值是多少或您要更改哪个浮点字段(如果需要,也可以轻松调整以适用于十进制字段)以及无论 的值是多少,以上内容都将起作用。