How to evaluate arithmetic expression passed as argument in awk?
I have this in a file.
1*2*3
1+2*3
awk should output开发者_运维百科 6, 7, when this file is passed in.
awk(1)
is the wrong tool as it doesn't have an eval()
function. I suggest modifying the file into input for bc(1)
or using shell arithmetic expansion:
while read expr; do
echo "$(($expr))"
done < file
- awk doesn't have eval() funciton
- use bc or shell arithmetic expansion both can make it
- But, if you use it in hadoop scripts, consider the subprocesses problem
besides, you can try these ways:
- Consider to write an expression evaluator in AWK (from archive.org, search for calc3)
- Use eval
- Use Python's eval function
I know this is awful but we can:
awk '{system(sprintf("awk \"BEGIN {print " $0 "}\""))}'
as pointed out, bc, Python eval, bash $(( ))
, are better solutions
One last suggestion Perl:
perl -nE 'say eval'
Here's another hack inspired in part by @JJoao's answer, and feedback from @DracoMetallium of Twitter...
#!/usr/bin/env bash
calc() {
awk 'BEGIN { print '"${@//inf/(2 ** 1024)}"'; }'
}
calc '1/2'
#> 0.5
... this also handles instances of inf
being passed via Bash built-ins for search-and-replace, eg...
calc 'inf + inf'
#> inf
calc '-inf + -inf'
#> -inf
calc '-inf + inf'
#> -nan
Which may be useful within one's .bashrc
file for quick command-line based calculations.
And for completeness, here's an example how to preform the above in (mostly) pure Awk...
calc.awk
#!/usr/bin/awk -f
function calc(expression) {
gsub("inf", "(2 ** 1024)", expression)
system(sprintf("awk \"BEGIN {printf(" expression ")}\""))
}
{
print calc($0)
}
... as well as examples of usage...
calc.awk <<<'1 /2'
#> 0.5
printf '2*2\nsqrt(9)\n' | calc.awk
#> 4
#> 3
calc.awk <<'EOF'
22 / 7
(1 + sqrt(5)) / 2
EOF
#> 3.14286
#> 1.61803
tee -a 'input-file.txt' 1>'/dev/null' <<'EOF'
1*2*3
1+2*3
EOF
calc.awk input-file.txt
#> 6
#> 7
awk
code self-eval :
echo '1*2*3 1+2*3' | mawk ' function eval(_,__,___) { return substr("", (___=RS)*(RS="^$")*((__="mawk \47BEGIN { "\ "printf(\"%.f\","(_)") }\47")|getline _), close(__)^(RS=___)*__)_ } $++NF = eval($!_)'
1*2*3 6
1+2*3 7
And have non-GMP-enabled
variants of awk
handle bigints via gawk-gmp
:
echo '9^357' | mawk2 ' function eval(__,_,___) { return substr("",(___=RS) * (RS="^$") * ((_="gawk -Mbe"\ " \47BEGIN { printf("(__)") }\47")|getline __), close(_)^(RS=___)*_)__ } $++NF = eval($!_)'
9^357 46192968246584020379055552051071189505164865440669900464
39030285864012137741835863345354556100224446056419891013
64348709024164571890111337972631022968123699490725498380
48619487796915547325757427881925121757649463471671577403
93732287476951829673979533419257547784348206387576562750
0451665854873600139914343339972692154903156749530623670508969
As an example, consider what iftop
gives you:
Host name last 2s last 10s last 40s cumulative
1 10.150.1.1 => 650B 533B 533B 2.08KB
85.239.108.20 <= 16.0KB 12.9KB 12.9KB 51.5KB
Let's say you need the 2 up/down lines into one line and calculate KB/B into right byte values (*1024). You could have this:
iftop -i eth1 -ts 10 -Bn|egrep "<|>"| sed 's/^ //g;s/^[1-9]/x/g;s/KB/ 1024/g;s/B/ 1/g' | tr -d '\n'|tr "x" '\n'| grep .| awk '{print $1" "$11" - "$9*$10+$19*$20" "$9*$10" "$19*$20 }'
精彩评论