我想用乘数替换模式之间的数字并打印所有行。该文件是 newick 格式的树文件,仅包含一行。我的目标是 之前)
和之后的所有数字:
。我想将两个符号之间的所有数字乘以 100。
文件:
((((A_8:0.000846,(A_5:0.002449,(A_1:1e-06,((A_4:1e-06,((A_7:1e-06,A_6:0.001061)0.714000:1e-06,A_3:1e-06)0.314500:1e-06)0.358667:1e-06,A_2:1e-06)0.361000:1e-06)0.434800:1e-06)0.683500:0.001619)0.888571:0.001931,A_9:0.00069)0.688471:0.000691,...
对我来说最简单的方法似乎是通过首先用新行替换所有“:”符号来分割文件。所以我的所有目标数字现在都在单独的行中并出现在)
.然后,我使用下面的 awk 脚本将目标数字乘以 100,但无法保留没有目标数字的行。
脚本:
sed 's/:/\n/g' df9.tree | awk -F")" '{OFS=")"} $2=$2*100 {print $0}'
sed 's/:/\n/g' df9.tree | awk '$NF ~/)/ {$NF *=100}1'
在这种情况下,如何将后面的数字相乘)
并打印整个文件?或者是否有其他更简单的方法来直接查找:
和之间的数字)
,将它们乘以 100 并打印整个文件?
更新:预期输出
((((A_8:0.000846,(A_5:0.002449,(A_1:1e-06,((A_4:1e-06,((A_7:1e-06,A_6:0.001061)71.4000:1e-06,A_3:1e-06)31.4500:1e-06)35.8667:1e-06,A_2:1e-06)36.1000:1e-06)43.4800:1e-06)68.3500:0.001619)88.8571:0.001931,A_9:0.00069)68.8471:0.000691,...)
答案1
awk 'BEGIN {OFS=FS=":"; ORS=RS=")"} NR>1 {$1=sprintf("%.4f", $1 * 100)}1' df9.tree
如果您接受将 RS 记录和 FS 字段分开,则所需的数字将始终位于第一个记录之后的第一个字段中。
答案2
$ perl -pe 's/\)([-0-9.]+):/sprintf ")%.4f:", $1 * 100/eg' df9.tree
((((A_8:0.000846,(A_5:0.002449,(A_1:1e-06,((A:1e-06,((A_7:1e-06,A:0.001061)71.4000:1e-06,A:1e-06)31.4500:1e-06)35.8667:1e-06,A:1e-06)36.1000:1e-06)43.4800:1e-06)68.3500:0.001619)88.8571:0.001931,A:0.00069)68.8471:0.000691,...
)
替换紧随一个字符并以:
数字乘以 100 的字符结尾的所有数字(定义为一个或多个数字、句点或减号字符的序列) 。
例如)0.714000:
更改为)71.4000:
它使用perl的/e
正则表达式评估修饰符在运算符的RHS中执行perl代码s///
。查看man perlop
并搜索s\/PATTERN
详细信息。sprintf
用于将数字格式化为小数点后 4 位。
)
如果和之间的数字:
可以采用普通十进制表示法(“0.714000”)或“C float”式科学记数法(“1e-06”),则正则表达式需要稍微复杂一点才能匹配所有可能的变化:
$ perl -pe 's/\)(([+-]?)(?=\d|\.\d)\d*(\.\d*)?([Ee]([+-]?\d+))?):/sprintf ")%.4f:", $1 * 100/eg' df9.tree
((((A_8:0.000846,(A_5:0.002449,(A_1:1e-06,((A_4:1e-06,((A_7:1e-06,A_6:0.001061)71.4000:1e-06,A_3:1e-06)31.4500:1e-06)35.8667:1e-06,A_2:1e-06)36.1000:1e-06)43.4800:1e-06)68.3500:0.001619)88.8571:0.001931,A_9:0.00069)68.8471:0.000691,...)
以下也可能有效,但可能有一些数字不匹配:
perl -pe 's/\)([-0-9.eE+]+):/sprintf ")%.4f:", $1 * 100/eg'
答案3
使用 perl's s///e
,您可以使用评估步骤来确定匹配是否是数字,并进行相应的替换:
perl -MScalar::Util=looks_like_number -pe '
s{\)\K.*?(?=:)}{ looks_like_number($&) ? $&*100 : $& }ge' file
使用 GNU awk,使用正则表达式作为记录分隔符:
gawk 'prevRT==")" && RT==":" && $0+0 == $0 {$0 *= 100} {ORS = prevRT = RT}
1' RS='[):]' file
这通过比较来测试记录的数字性$0+0 == $0
。
答案4
POSIX sed
与桌面计算器和 bash shell 结合使用,dc
我们可以获得如图所示的结果。首先,我们计算要合成的字符串的形式,然后通过将其推送到 bash 来合成它。
echo 'echo "'"$(sed -e '
s#)\([^:)]\{1,\}\):#)$(echo "4k100 \1*1/p"|dc):#g' < file)"'"'|sh
或者,我们可以评估已预先填充命令的变量以生成输出。
var='echo "'$(sed -e '
s#)\([^:)]\{1,\}\):#)$(echo "4k100 \1*1/p"|dc):#g' < inp)\"
eval "$var"
使用 4 参数split
函数来GNU awk
跟踪字段和分隔符。
awk '{
split($0, a, /[:)]/, s)
for (i=1; i in a; i++)
print (s[i-1] s[i] == "):" ? sprintf("%.4f",a[i]*100):a[i]) s[i]
$0=RS
}1' ORS= file