上下文:我有一个输入文件,其中包含带有关联值的参数,后跟文字数学表达式,例如:
PARAMETERS DEFINITION
A = 5; B = 2; C=1.5; D=7.5
MATHEMATICAL EXPRESSIONS
A*B
C/D
...
我希望将第二部分的字符串解释为数学表达式,以便在输出文件中得到表达式的结果:
...
MATHEMATICAL EXPRESSIONS
10
0.2
...
我已经做了什么:到目前为止,使用awk,我将所有参数名称及其对应的值存储在两个不同的数组中。然后我用它的值替换每个参数,这样我现在处于与this thread的作者类似的情况。 但是,他/她得到的答案不是awk,除了最后一个非常特定于她/他的情况,并且很难理解我作为awk和shell脚本的初学者。
我后来尝试了什么:由于我不知道如何在awk中执行此操作,我的想法是将新字段值存储在变量中,然后在awk中使用shell命令像这样的脚本:
#!bin/awk -f
BEGIN{}
{
myExpression=$1
system("echo $myExpression | bc")
}
END{}
遗憾的是,这不起作用,因为变量以某种方式不被echo命令识别。
我想要的是什么: 我更喜欢单独使用awk而不调用外部函数的解决方案,但是,如果它更简单,我不会使用shell命令。
编辑考虑到目前为止的所有评论,我会更精确,我的输入文件看起来更像是这样:
PARAMETERS_DEFINITION
[param1] = 5
[param2] = 2
[param3] = 1.5
[param4] = 7.5
MATHEMATICAL_EXPRESSIONS
[param1]*[param2]
some text containing also numbers and formulas that I do not want to be affected.
e.g: 1.45*2.6 = x, de(x)/dx=e(x) ; blah,blah,blah
[param3]/[param4]
参数的名称足够复杂,因此文档中字符串:"[param#]"
的任何匹配对应于我想要为其值更改的参数。
以下是我设置存储参数的方式,它们在数组中的值如下:
{
if (match($2,/PARAMETERS_DEFINITION/) != 0) {paramSwitch = 1}
if (match($2,/MATHEMATICAL_EXPRESSIONS/) != 0) {paramSwitch = 0}
if (paramSwitch == 1)
{
parameterName[numOfParam] = $1 ;
parameterVal[numOfParam] = $3 ;
numOfParam += 1
}
}
答案 0 :(得分:1)
而不是:
{
myExpression=$1
system("echo $myExpression | bc")
}
我想你想要这个:
{
myExpression=$1
system("echo " myExpression " | bc")
}
那是因为在awk中,赋值不会最终成为环境变量,并且将字符串放在一起会将它们连接起来。
答案 1 :(得分:1)
你问 awk:将字符串解释为数学表达式 - 这个功能通常称为eval
,而不,(AFAIK)awk
不知道这样的功能。因此,您的问题是典型的XY problem
正确的工具是bc
,您(几乎)不需要修改任何内容,只需输入bc
输入,只确保变量是小写的,例如以下输入(编辑你的例子)
#PARAMETERS DEFINITION
a=5; b=2; c=1.5; d=7.5
#MATHEMATICAL EXPRESSIONS
a*b
c/d
使用like
bc -l < inputfile
产生
10
.20000000000000000000
用于编辑,用于新输入数据。以下
grep '\[' inputfile | sed 's/[][]//g' | bc -l
输入
PARAMETERS_DEFINITION
[param1] = 5
[param2] = 2
[param3] = 1.5
[param4] = 7.5
MATHEMATICAL_EXPRESSIONS
[param1]*[param2]
some text containing also numbers and formulas that I do not want to be affected.
e.g: 1.45*2.6 = x, de(x)/dx=e(x) ; blah,blah,blah
[param3]/[param4]
产生以下输出:
10
.20000000000000000000
e.g。只展示包含[
的内容 - 任何参数定义或表达式,删除任何[]
,例如创建以下bc
程序:
param1 = 5
param2 = 2
param3 = 1.5
param4 = 7.5
param1*param2
param3/param4
并发送整个&#34;程序&#34;到bc
...
答案 2 :(得分:0)
$ cat test
PARAMETERS DEFINITION
A=5; B=2; C=1.5; D=7.5
MATHEMATICAL EXPRESSIONS
A*B
C/D
$ awk -vRS='[= ;\n]' '{if ($0 ~ /[0-9]/){a[x] = $0; print x"="a[x]}else{x=$0}}/MATHEMATICAL/{print "MATHEMATICAL EXPRESSIONS"}{if ($0~"*") print a[substr($0,1,1)] * a[substr($0,3,1)]}{if ($0~"/") print a[substr($0,1,1)] / a[substr($0,3,1)]}' test
A=5
B=2
C=1.5
D=7.5
MATHEMATICAL EXPRESSIONS
10
0.2
格式良好:
$ cat test.awk
# Store all variables in an array
{
if ($0 ~ /[0-9]/){
a[x] = $0;
print x " = " a[x] # Print the keys & values
}
else{
x = $0
}
}
# Print header
/MATHEMATICAL/ {print "MATHEMATICAL EXPRESSIONS"}
# Do the maths (case can work too, but it's not as widely available)
{
if ($0~"*")
print a[substr($0,1,1)] * a[substr($0,3,1)]
}
{
if ($0~"/")
print a[substr($0,1,1)] / a[substr($0,3,1)]
}
{
if ($0~"+")
print a[substr($0,1,1)] + a[substr($0,3,1)]
}
{
if ($0~"-")
print a[substr($0,1,1)] - a[substr($0,3,1)]
}
$ cat test
PARAMETERS DEFINITION
A=5; B=2; C=1.5; D=7.5
MATHEMATICAL EXPRESSIONS
A*B
C/D
D+C
C-A
$ awk -f test.awk -vRS='[= ;\n]' test
A = 5
B = 2
C = 1.5
D = 7.5
MATHEMATICAL EXPRESSIONS
10
0.2
9
-3.5
答案 3 :(得分:0)
这有点类似于awk的eval,它在上下文需要时是一个神奇的转换,这里添加+0会进行转换。
我为您提供的内容(下面已详细说明),其中包含一个名为awkinput的文件,其中包含您的示例输入
awk '/[A-Z]=[0-9.]+;/ { for (i=1;i<=NF ;i++) { print "working on "$i; split($i,fields,"="); sub(/;/,"",fields[2]); params[fields[1]]=strtonum(fields[2]) } }; /[A-Z](*|\/|+|-)[A-Z]/ { for (p in params) { sub(p, params[p],$0); }; system("echo " $0 " | bc -ql") }' awkinput
的相关详细:
/[A-Z]=[0-9.]+;?/ { # if we match something like A=4.2 with or wothout a ; at end
for (i=1;i<=NF ;i++) { # loop through the fields (separated by space, the default Field Separator of awk)
print "working on "$i; # inform on what we do
split($i,fields,"="); # split in an array to get param and value
sub(/;/,"",fields[2]); # Eventually remove the ; at end
params[fields[1]]=strtonum(fields[2]) # new array of parameters where the values are numeric
}
}
/[A-Z](*|\/|+|-)[A-Z]/ { #when the line match a math operation with one param on each side (at least)
for (p in params) { # loop over know params
sub(p, params[p],$0); # replace each param with its value
};
system("echo " $0 " | bc -ql") # print the result (no way to get of system call here)
}
缺点:
AB*C
形式的数学运算将解析为52*1.5
答案 4 :(得分:0)
以BIDMAS为基础,我在awk中创建了这个数学函数 我还没有包括括号(或索引),因为它们需要一些额外的努力,但我可能会在以后添加它们 这个awk脚本可以像bc一样有效地工作 无需系统调用,全部都是awk。
awk '{split($0,a,"+")
for(i in a){
split(a[i],s,"-")
for(j in s){
split(s[j],m,"*")
for(k in m){
split(m[k],d,"/")
for(l in d){
if(l>1)d[1]=d[1]/d[l]
}
m[k]=d[1]
delete d
if(k>1)m[1]=m[1]*m[k]
}
s[j]=m[1]
delete m
if(j>1)s[1]=s[1]-s[j]
}
a[i]=s[1]
delete s
}
for(i in a)b=b+a[i];print b}{b=0}' file
awk '
/MATHEMATICAL_EXPRESSIONS/{z=1}
NR>1&&!z{split($0,y," = ");x[y[1]]=y[2]}
z&&/[\+\-\/\*]/{
for (n in x)gsub(n,x[n])
split($0,a,"+")
for(i in a){
split(a[i],s,"-")
for(j in s){
split(s[j],m,"*")
for(k in m){
split(m[k],d,"/")
for(l in d){
if(l>1)d[1]=d[1]/d[l]
}
m[k]=d[1]
delete d
if(k>1)m[1]=m[1]*m[k]
}
s[j]=m[1]
delete m
if(j>1)s[1]=s[1]-s[j]
}
a[i]=s[1]
delete s
}
for(i in a)b=b+a[i];print b}{b=0}' file