今天看到论坛中有人问:[url=http://bbs.chinaunix.net/viewthread.php?tid=737798&extra=page%3D2] awk如何计算命令行的输入?
仔细想了想用awk确实不好做。因为awk中没有像其它语言
中的eval函数,于是自己试着编了一个eval函数,用来进
行表达式求值。
用stack对运算符号及数值进行处理是这个脚本的关键。
由于时间有限,我只实现了+-*/%五种运算。代码写的比较
粗糙,欢迎大家批评指正。
用法(表达式必须以"@"结束):
echo "(3-1)*2.3-8.5%3-(9.12+2*3)/2@" | awk -f eval
echo 1+2+3*2 @| awk -f eval
awk -f eval <( seq 1 100|tr "\n" "+"|sed 's/+/@/100' )
代码:
#! /usr/bin/awk
# simple expression evaluator awk version
# author : dbcat at chinaunix.net
# email: deeperbluecat@gmail.com
# usage: example :
# echo "(3-1)*2.3-8.5%3-(9.12+2*3)/2@" | awk -f eval
# echo 1+2+3*2 @| awk -f eval
# awk -f eval <( seq 1 100|tr "\n" "+"|sed 's/+/@/100' )
# expression must be end with "@"
# have fun
BEGIN{
# 运算符集合
symbol="+-*%/()@";
# 构造运算符优先级关系
oppr["++"]=">";oppr["+-"]=">";oppr["+*"]="<";oppr["+/"]="<";oppr["+%"]="<";oppr["+("]="<"; oppr["+)"]=">";oppr["+@"]=">";
oppr["-+"]=">";oppr["--"]=">";oppr["-*"]="<";oppr["-/"]="<";oppr["-%"]="<";oppr["-("]="<"; oppr["-)"]=">";oppr["-@"]=">";
oppr["*+"]=">";oppr["*-"]=">";oppr["**"]=">";oppr["*/"]=">";oppr["*%"]="<";oppr["*("]="<"; oppr["*)"]=">";oppr["*@"]=">";
oppr["/+"]=">";oppr["/-"]=">";oppr["/*"]=">";oppr["//"]=">";oppr["/%"]="<";oppr["/("]="<"; oppr["/)"]=">";oppr["/@"]=">";
oppr["%+"]=">";oppr["%-"]=">";oppr["%*"]=">";oppr["%/"]=">";oppr["%%"]=">";oppr["%("]="<"; oppr["%)"]=">";oppr["%@"]=">";
oppr["(+"]="<";oppr["(-"]="<";oppr["(*"]="<";oppr["(/"]="<";oppr["(%"]="<";oppr["(("]="<"; oppr["()"]="=";
oppr[")+"]=">";oppr[")-"]=">";oppr[")*"]=">";oppr[")/"]=">";oppr[")%"]=">"; oppr["))"]=">";oppr[")@"]=">";
oppr["@+"]="<";oppr["@-"]="<";oppr["@*"]="<";oppr["@/"]="<";oppr["@("]="<";oppr["@%"]="<"; oppr["@@"]="=";
}
{
gsub(/[-*%+()/@]/," & ",$0);
expression=$0;
}
END{
# 分解表达式
split(expression,Ex," ");
print evaluate(Ex);
}
# 表达式求值函数
function evaluate(Ex,OP,OF,c,k,t,x,str,y,r)
{
k=1;
push(OP,"@");
c=Ex[k];
while(c!="@" || gettop(OP)!="@")
{
if(index(symbol,c)==0)
{
push(OF,c);
k=k+1;
c=Ex[k];
}else
{
t=priority(oppr,gettop(OP),c);
if(t=="<")
{
push(OP,c);
k=k+1;
c=Ex[k];
}else if(t=="=")
{
pop(OP);
k=k+1;
c=Ex[k];
}else{
str=pop(OP);
y=pop(OF);
x=pop(OF);
r=calculate(x,str,y);
push(OF,calculate(x,str,y));
}
}
}
return gettop(OF);
}
# 二元运算函数
function calculate(x,st,y)
{
if(st=="+")
return x+y;
else if(st=="-")
return x-y;
else if(st=="*")
return x*y;
else if(st=="%")
return x%y;
else if(st=="/")
return x/y;
}
# 优先级别比较
function priority(oppr,op1,op2)
{
return oppr[op1""op2];
}
# 取栈顶
function gettop(arr,k,t)
{
k=0;
for(t in arr)
{
k++;
}
return arr[k-1];
}
# 进栈函数
function push(arr,x,t,k)
{
k=0;
for(t in arr)
{
k++;
}
arr[k]=x;
}
# 出栈函数
function pop(arr,va,t,u)
{
u=0;
for(t in arr)
{
u++;
}
va=arr[u-1];
delete arr[u-1];
return va;
}
xlink 回复于:2006-04-13 21:01:52
dbcat你真厉害!
我也问了一个问题:
我知道怎么逐行读取一个文件,但怎么逐字读取一个文件?
帮帮我。
dbcat 回复于:2006-04-13 21:06:53
你想干虾米?
xlink 回复于:2006-04-13 21:11:25
引用:原帖由 dbcat 于 2006-4-13 21:06 发表
你想干虾米?
我想知道方法。暂时还没想到有什么用,是一个字符一个字符地去读,就是相当于:
cat 1.txt>2.txt的功能。
[ 本帖最后由 xlink 于 2006-4-13 21:14 编辑 ]
dbcat 回复于:2006-04-13 21:14:37
我猜用dd可以
xlink 回复于:2006-04-13 21:24:54
引用:原帖由 dbcat 于 2006-4-13 21:14 发表
我猜用dd可以
dd好象是用来cp文件的,我不是想去cp一个文件。
如果把一个文件全部读进一组变量里,你想想,多好的事情。
无奈何 回复于:2006-04-13 22:21:07
谢谢 dbcat 费心写出的这个函数,我在windows下只有 GAWK 能够通过, awk95、nawk、mawk 都不能通过。
你的代码我现在还看不懂,看来我自己是不能通过简单修改来满足最初的需求了。先收藏起来,努力学习吧。
johnny_jiang 回复于:2006-04-13 23:02:14
引用:原帖由 xlink 于 2006-4-13 21:24 发表
dd好象是用来cp文件的,我不是想去cp一个文件。
如果把一个文件全部读进一组变量里,你想想,多好的事情。
dd是可以的,小弟试了一下,不过又缺陷
# while char=`dd bs=1 count=1 2>/dev/null`; do echo $char; done <file
由于小弟学艺不精,导致当dd读完了整个文件后,还在继续读,望高手指点
用awk实现了一下,请试试
# awk 'BEGIN { FS="" } { for (i=1;i<=NF;i++) print $i }' file
寂寞烈火 回复于:2006-04-14 11:19:44
dbcatMM is A awk's master ~ ^_^
苏蓉蓉 回复于:2006-04-14 11:42:00
如果把乘阶运算符“^” , 和"sin cos exp ...." 加进去就好了
r2007 回复于:2006-04-14 11:50:00
功能加多了的话,一不小心一个新的脚本语言就此诞生了
dbcat 回复于:2006-04-14 13:47:44
呵呵,如果加sin log...的话,用一棵expression binary tree 来分析比较好。
[url=http://bbs.chinaunix.net/viewthread.php?tid=744577&extra=page%3D2]更新链接
#! /usr/bin/awk
# expression evaluator awk version
# author : dbcat at chinaunix
# email: deeperbluecat@gmail.com
# 用法:
# echo "4*arctan(sin(1))+sqrt(1.2)*(1-23*exp(3))-log(2+cos(3))"| awk -f eval
# echo "PI+E^sin(32.1%3.1)"|awk -f eval
# awk -f eval <( seq 1 100|tr "\n" "+"|sed 's/+//100' )
# echo '1!=2&1|0'|awk -f eval
# echo '1<=2+3>=(!2-1.1)+sin(1.2*(PI!=E))' | awk -f eval
# 支持的算术运算符号 : + - * / % ^ ( )
# 支持的函数运算 : sin cos tg log exp arctan sqrt int
# 支持的逻辑运算符号 : < > ! != <= >= & |
# 内置的常数 : PI E
# 运行环境 : GNU Awk 3.1.4
# have fun
BEGIN{
# 运算符集合
symbol="+-*%<=!=>==|&>^SCLAEQTI/()@";
# 构造运算符优先级关系
consop(oppr);
}
#主函数
{
split(funclib($0"@"),Ex," ");
print evaluate(Ex);
}
# 表达式求值函数
function evaluate(Ex,OP,OF,c,k,t,x,str,y,r)
{
k=1;
push(OP,"@");
c=Ex[k];
while(c!="@" || gettop(OP)!="@")
{
if(index(symbol,c)==0)
{
push(OF,c);
k=k+1;
c=Ex[k];
}else
{
t=priority(oppr,gettop(OP),c);
if(t=="<")
{
push(OP,c);
k=k+1;
c=Ex[k];
}else if(t=="=")
{
pop(OP);
k=k+1;
c=Ex[k];
}else{
str=pop(OP);
y=pop(OF);
x=pop(OF);
r=calculate(x,str,y);
push(OF,calculate(x,str,y));
}
}
}
return gettop(OF);
}
# 二元运算函数
function calculate(x,st,y)
{
if(st=="+")
return x+y;
else if(st=="-")
return x-y;
else if(st=="*")
return x*y;
else if(st=="%")
return x%y;
else if(st=="/")
return x/y;
else if(st=="^")
return x^y;
else if(st=="S")
return sin(y);
else if(st=="C")
return cos(y);
else if(st=="L")
return log(y);
else if(st=="E")
return exp(y);
else if(st=="Q")
return sqrt(y);
else if(st=="A")
return atan2(y,1);
else if(st=="T")
return sin(y)/cos(y);
else if(st=="I")
return int(y);
else if(st=="<")
return x<y;
else if(st==">")
return x>y;
else if(st=="<=")
return x<=y;
else if(st==">=")
return x>=y;
else if(st=="==")
return x==y;
else if(st=="!=")
return x!=y;
else if(st=="&")
return x&&y;
else if(st=="|")
return x||y;
else if(st=="!")
return !y;
}
# 算符优先关系函数
function consop(oppr,t,x,y,z,i,k,j,xl,yl,zl,tl)
{
xl="+-";
yl=" * / % ^ < <= ! & | > >= == != ";
zl="SCALEQTI";
tl="+ - * / % ^ < > ! != == >= <= & | S C A L E Q T I";
split(tl,t," ");
split(xl,x,"");
split(yl,y," ");
split(zl,z,"");
for(k=1;k<=length(xl);k++)
{
for(j=1;j<=length(xl);j++)
{
oppr[x[k]""x[j]]=">";
oppr[x[j]""x[j]]=">";
}
for(j=1;j<=length(yl);j++)
{
oppr[x[k]""y[j]]="<";
oppr[y[j]""x[k]]=">";
for(i=1;i<=length(yl);i++)
{
oppr[y[j]""y]=">";
oppr[y""y[j]]=">";
}
for(i=1;i<=length(zl);i++)
{
oppr[y[j]""z]="<";
oppr[z""y[j]]=">";
}
}
for(j=1;j<=length(zl);j++)
{
oppr[x[k]""z[j]]="<";
oppr[z[j]""x[k]]=">";
for(i=1;i<=length(zl);i++)
{
oppr[z[j]""z]=">";
oppr[z""z[j]]=">";
}
}
}
for(j=1;j<=length(tl);j++)
{
oppr[t[j]"("]="<";
oppr[t[j]")"]=">"
oppr[t[j]"@"]=">";
oppr[")"t[j]]=">";
oppr["("t[j]]="<";
oppr["@"t[j]]="<";
}
for(j=1;j<=length(yl);j++)
{
oppr[y[j]"^"]="<";
}
oppr["(("]="<";oppr["()"]="=";
oppr["))"]=">";oppr[")@"]=">";
oppr["@("]="<";oppr["@@"]="=";
}
# 优先级别比较
function priority(oppr,op1,op2)
{
return oppr[op1""op2];
}
# 分解表达式
function funclib(expr)
{
gsub(/PI/,"3.141592653589793238460",expr);
gsub(/E/,"2.718281828459045235360",expr);
gsub(/sin/," & S ",expr);
gsub(/cos/," & C ",expr);
gsub(/log/," & L ",expr);
gsub(/exp/," & E ",expr);
gsub(/arctan/," & A ",expr);
gsub(/sqrt/," & Q ",expr);
gsub(/tg/," & T ",expr);
gsub(/int/," & I ",expr);
gsub(/[-*%+>=!&|<^()SCLAEQTI/@]/," & ",expr);
gsub(/<[ ]*=/,"<=",expr);
gsub(/>[ ]*=/,">=",expr);
gsub(/=[ ]*=/,"==",expr);
gsub(/![ ]*=/,"!=",expr);
gsub(/&/," & ",expr);
gsub(/[|]/," & ",expr);
gsub(/![^=]/," N ! ",expr);
return expr;
}
# 取栈顶
function gettop(arr,k,t)
{
k=0;
for(t in arr)
{
k++;
}
return arr[k-1];
}
# 进栈函数
function push(arr,x,t,k)
{
k=0;
for(t in arr)
{
k++;
}
arr[k]=x;
}
# 出栈函数
function pop(arr,va,t,u)
{
u=0;
for(t in arr)
{
u++;
}
va=arr[u-1];
delete arr[u-1];
return va;
}
[ 本帖最后由 dbcat 于 2006-4-25 11:08 编辑 ]
waker 回复于:2006-04-14 16:03:00
来个马后炮
[waker@proxy ~]$ echo "1+2*3
4/6
2^3+5*6"|
awk '{a=$0;cmd="awk \047BEGIN{print "$0" }\047";cmd|getline ;print a"="$0}'
1+2*3=7
4/6=0.666667
2^3+5*6=38
waker 回复于:2006-04-14 16:06:59
忘记及时回收垃圾了
awk '{a=$0;cmd="awk \047BEGIN{print "$0" }\047";cmd|getline ;close(cmd);print a"="$0}'
dbcat 回复于:2006-04-14 16:51:20
waker真棒!
用getline实现awk自身调用,这应该是最简单的办法了!
xstart 回复于:2006-04-18 19:00:47
复杂的程序,我觉得用C/C++实现更好,用python或者php(CLI模式)也可以,如果你喜欢perl还可以用perl,不过我不太喜欢。毕竟shell,awk都不是一个全功能的语言,解决复杂问题比较费劲
likfan 回复于:2006-04-19 11:57:59
真是精华啊。
|