我尝试使用这个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
答案 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
可能等于列A
或B
,那么这仍然会遇到问题。最好只进行计算,然后通过分配$_
:
perl -ape 'if (/^\d/) { $F[2] /= $F[4]; $F[3] /= $F[4]; $_ = join(" ", @F); }'
如果您想要输出特定格式,请使用sprintf
代替join
。