sed 特殊字符替换挑战

sed 特殊字符替换挑战

我正在尝试使用 sed 将 TB 转换为 GB。带有 TB 的字段要么是一位数(2T、8T),要么是多位数字(2.001T、1.501T)。我以为我的替换工作正常,但我遇到了错误,而且我似乎无法弄清楚。

文件样本:

ftwepsiprdsql02,ftwepsiprdsql02_F,2.001T,1.680T
ftwepsiprdsql02,ftwepsiprdsql02_G,801G,176.786G
ftwepsiprdsql02,ftwepsiprdsql02_H,501G,6.565G
ftwepsiprdsql02,ftwepsiprdsql02_I,1.001T,539.504G
ftwepsiprdsql02,ftwepsiprdsql02_J,501G,478.211G
ftwepsiprdsql02,ftwepsiprdsql02_X,1.501T,68.021G
rxi0738_foc_cl1,rxi0738_foc_cl1_lun248,8T,4.450T
rxi0738_foc_cl1,rxi0738_foc_cl1_lun250,8T,5.857T
rxi0738_foc_cl1,rxi0738_foc_cl1_lun252,8T,4.681T
rxi0738_foc_cl1,rxi0738_foc_cl1_lun254,8T,4.657T

我正在分别替换第三个和第四个字段(已分配/已用容量),我最初的方法是将单位数容量数字更改为与多位数字(2.000T、8.000T)一致。

替换第三个字段中的个位数的代码:

for i in `awk -F , '{print $3}' $TMPRPT| grep \[0-9\]T | grep -v "\." `
do
TVAL=$(echo $i | sed 's/T/.000T/')
sed -i .tmp "s/$i/$TVAL/" "$TMPRPT"
done

理论上,这应该改变所有单位数字以匹配多位数字,因此从 TB 到 GB 的最终转换很简单。 awk 命令执行完美,并给了我只是个位数的输出,但是一旦我将它放入 for 循环和 sed -i 命令中,它就会进行替换全部带有 T 名称的字段,包括多位数字:

ftwepsiprdsql02,ftwepsiprdsql02_F,2.001.000T,1.680T
ftwepsiprdsql02,ftwepsiprdsql02_G,801G,176.786G
ftwepsiprdsql02,ftwepsiprdsql02_H,501G,6.565G
ftwepsiprdsql02,ftwepsiprdsql02_I,1.001.000T,539.246G
ftwepsiprdsql02,ftwepsiprdsql02_J,501G,478.211G
ftwepsiprdsql02,ftwepsiprdsql02_X,1.501.000T,68.021G
rxi0738_foc_cl1,rxi0738_foc_cl1_lun248,8.000T,4.450T
rxi0738_foc_cl1,rxi0738_foc_cl1_lun250,8.000T,5.857T
rxi0738_foc_cl1,rxi0738_foc_cl1_lun252,8.000T,4.681.000T
rxi0738_foc_cl1,rxi0738_foc_cl1_lun254,8.000T,4.657T

我知道有一种更简单的方法可以完成此任务,因此我对替代方案持开放态度,但我也很想知道如何使 sed -i 按照我想要的方式运行。

(请注意,该平台是 Isilon,它运行缩小版的 Linux 内核。大多数命令都可用,但不是全部。)

答案1

如果您有numfmtGNU Coreutils:

$ numfmt -d, --field=3,4 --from=auto --round=nearest < file | 
    numfmt -d, --field=3,4 --to-unit=G --suffix=G
ftwepsiprdsql02,ftwepsiprdsql02_F,2001G,1680G
ftwepsiprdsql02,ftwepsiprdsql02_G,801G,177G
ftwepsiprdsql02,ftwepsiprdsql02_H,501G,7G
ftwepsiprdsql02,ftwepsiprdsql02_I,1001G,540G
ftwepsiprdsql02,ftwepsiprdsql02_J,501G,479G
ftwepsiprdsql02,ftwepsiprdsql02_X,1501G,69G
rxi0738_foc_cl1,rxi0738_foc_cl1_lun248,8000G,4450G
rxi0738_foc_cl1,rxi0738_foc_cl1_lun250,8000G,5857G
rxi0738_foc_cl1,rxi0738_foc_cl1_lun252,8000G,4681G
rxi0738_foc_cl1,rxi0738_foc_cl1_lun254,8000G,4657G

或者,使用 Perl:

perl -F, -pe '
  $_ = join ",", map { $_ =~ s/^(\d*(\.\d+)?)T$/sprintf "%.0fG", 1000*$1/e; $_ } @F
' file

答案2

假设1T=1000G。

sed 已完成,因此理论上比乘法所需的功能更强大,但我保持简短,只是通过简单的文本替换而不使用循环来进行更改。

让我们分解一下所需的改变。首先将带有 T 但没有句点的单个字段更改为带有“.000”的字段。需要“g”,因为有 2 个字段。请注意,因为我们有一个很好的逗号来开始该字段,所以要利用它。

  s/\(,[0-9]*\)T/\1.000T/g

现在进行一些清理,以防存在没有 3 位小数的字段。如果句点后没有任何内容,则添加 3 个零;如果有一位数字,则添加 2 个零;如果有两位数字,则添加零;并删除第三个数字后的任何多余数字(即截断而不是四舍五入)。

s/\(,[0-9]*\.\)T/\1000T/g
s/\(,[0-9]*\.[0-9]\)T/\100T/g
s/\(,[0-9]*\.[0-9][0-9]\)T/\10T/g
s/\(,[0-9]*\.[0-9][0-9][0-9]\)[0-9]*T/\1T/g

现在将 T 更改为 G,去掉句点。

s/\(,[0-9]*\)\.\([0-9][0-9][0-9]\)T/\1\2G/g

现在为了友善一点,去掉前导零

s/,0*\([1-9][0-9]*G\)/,\1/g

将所有命令放在一起并给出示例数据结果

ftwepsiprdsql02,ftwepsiprdsql02_F,2001G,1680G
ftwepsiprdsql02,ftwepsiprdsql02_G,801G,176.786G
ftwepsiprdsql02,ftwepsiprdsql02_H,501G,6.565G
ftwepsiprdsql02,ftwepsiprdsql02_I,1001G,539.504G
ftwepsiprdsql02,ftwepsiprdsql02_J,501G,478.211G                                                                                                        
ftwepsiprdsql02,ftwepsiprdsql02_X,1501G,68.021G
rxi0738_foc_cl1,rxi0738_foc_cl1_lun248,8000G,4450G
rxi0738_foc_cl1,rxi0738_foc_cl1_lun250,8000G,5857G
rxi0738_foc_cl1,rxi0738_foc_cl1_lun252,8000G,4681G
rxi0738_foc_cl1,rxi0738_foc_cl1_lun254,8000G,4657G

相关内容