计算输出在perl one-liner中消失

时间:2014-11-19 00:10:48

标签: perl

我尝试使用这个perl one-liner对制表符分隔文件的列进行一些计算:

perl -ape 'if (/^\d/) { s/$F[2]/$F[2]\/$F[4]/e && s/$F[3]/$F[3]\/$F[4]/e}' infile

这个想法是让A和B列除以C列

infile中:

X       Y       A       B               C
5001    3       1.03333 0.652549        4215
6001    4       1.2     0.723137        4870
7001    2       1       0.807843        5153
8001    2       1       0.807843        5355
9001    2       1       0.807843        5389
10001   2       1       0.807843        4955
11001   7       1.7671  1.05573 4966
12001   17      8.18802 4.72554 5124

但输出是这样的:

X       Y       A       B               C
5001    3       0.000245155397390273    0.000154815895610913    4215
6001    4       0.000246406570841889    0.000148488090349076    4870
7000.000194061711624297 2       1       0.000156771395303707    5153
8000.000186741363211951 2       1       0.000150857703081232    5355
9000.000185563184264242 2       1       0.000149905919465578    5389
0.0002018163471241170001        2       1       0.000163035923309788    4955
11001   7       0.000355839710028192    0.000212591623036649    4966
12001   17      0.00159797423887588     0.000922236533957845    5124

第3到第6行发生了什么?怎么设法解决这个问题? 感谢。

编辑: 我从substitute命令中删除了/ e选项,似乎正在错误的列上执行计算。

perl -ape 'if (/^\d/) { s/$F[2]/$F[2]\/$F[4]/ && s/$F[3]/$F[3]\/$F[4]/}' infile

X       Y       A       B               C
5001    3       1.03333/4215    0.652549/4215   4215
6001    4       1.2/4870        0.723137/4870   4870
7001/5153       2       1       0.807843/5153   5153
8001/5355       2       1       0.807843/5355   5355
9001/5389       2       1       0.807843/5389   5389
1/49550001      2       1       0.807843/4955   4955
11001   7       1.7671/4966     1.05573/4966    4966
12001   17      8.18802/5124    4.72554/5124    5124
13001   30      13.8763/5138    8.05385/5138    5138

2 个答案:

答案 0 :(得分:2)

您的基本问题是您要替换第3列和第4列中的值,无论它们出现在整行中。例如,对于第3行,您正在执行s/1/1\/5153/e,这会影响行中数字1的第一次出现,而不一定是第3列中恰好出现的1

试试这个:

perl -lane 'if ($F[4] =~ /[1-9]/) { $F[2] /= $F[4]; $F[3] /= $F[4] } print join "\t", @F' infile

如果要限制精度,请执行$F[2] = sprintf "%f", $F[2]/$F[4]; ...

之类的操作

答案 1 :(得分:2)

在替换和评估之后,您有类似s/1/0.000194061711624297/的内容。因此s运算符会查找1并将其作为第一列的一部分。哎呦。如果我们添加一些\b字边界标记,我们可以强制s运算符的匹配部分匹配完整列,而不仅仅是列的一部分:

perl -ape 'if (/^\d/) { s/\b$F[2]\b/$F[2]\/$F[4]/e && s/\b$F[3]\b/$F[3]\/$F[4]/e}' infile

但如果列X可能等于列AB,那么这仍然会遇到问题。最好只进行计算,然后通过分配$_

来替换整行
perl -ape 'if (/^\d/) { $F[2] /= $F[4]; $F[3] /= $F[4]; $_ = join(" ", @F); }'

如果您想要输出特定格式,请使用sprintf代替join