Output float variable as decimal to file in BASH

2019-08-20 07:58发布

The result of BASH time (run 5 times) is stored in a text file as decimal. I then read back in the values and compute the average using bc. Finally, I output the resulting average as a decimal to a file. My script seems to work, (no errors in Mate Terminal on Linux Mint, both .txt files are created) except the final output to file is "0".

TIMEFORMAT=%R
tsum=0

for i in {1..5}
do
(time sh -c \
'openssl des3 -e -nosalt -k 0123456789012345 -in orig.jpg -out encr.enc; '\
'openssl des3 -d -nosalt -k 0123456789012345 -in encr.enc -out decr.dec'\
) 2>&1 | grep 0 >> outtime.txt
done

avgDES3=0
cat outtime.txt | \
while read num
do
tsum=`echo $tsum + $num | bc -l`
done

avgDES3=`echo "$tsum / 5" | bc -l`
echo "DES3 average is: " $avgDES3 >> results.txt

I've also tried replacing the last line with: printf "DESCBC average is: " $avgDESCBC >> results.txt

the outtime.txt is:

0.220
0.218
0.226
0.223
0.217

and results.txt is:

DES3 average is:  0

I'd appreciate help getting the resulting average to be a decimal. Perhaps I'm not using the correct value of the tsum variable in the next to last line (eg. if tsum global isn't changed by the expression within the loop)?

EDIT: Issue (as pointed out by rbong and Arun) was piping to a subshell (global variable not changed after loop expression). Origninally script was producing appropriate outtime.txt on my system (no errors, just didn't get tsum value from loop).

标签: bash shell
2条回答
做个烂人
2楼-- · 2019-08-20 08:43

Try this script. Using { , } around time command you can capture the OUTPUT of "time" command and use "2" identifier for creating outtime.txt file in append mode. Before starting the script, this file should be created fresh OR you can comment the ">outtime.txt" line. A ';' character just before closing '}' brace is important OR it won't end the begining '{'. This will FIX your outtime.txt file contents/data issue.

Other issue is with while loop as you are using "|" before while loop and due to that a new subshell is getting created for while loop and tsum variable is loosing its value when it's out of the while loop. Feed while loop "outtime.txt" file like shown below in my reply.

#!/bin/bash

TIMEFORMAT=%R
tsum=0

#create fresh outtime.txt and results.txt files
>outtime.txt

#if you want this file to be appended for future runs, comment the following line.
>results.txt

for i in {1..5}
do
{ time sh -c \
'openssl des3 -e -nosalt -k 0123456789012345 -in orig.jpg -out encr.enc; '\
'openssl des3 -d -nosalt -k 0123456789012345 -in encr.enc -out decr.dec'\
} 1>/dev/null 2>&1; } 2>> outtime.txt
done
## Now at this point, outtime.txt will contain only x.xxx time entries per line for 5 runs of "for" loop.
echo File outtime.txt looks like:
echo ----------
cat outtime.txt
echo;
## Now lets calculate average.
avgDES3=0
while read num
do
tsum=`echo $tsum + $num | bc -l`
done < outtime.txt; ## Feed while loop this outtime.txt file


avgDES3=`echo "$tsum / 5" | bc -l`
echo "DES3 average is: " $avgDES3 > results.txt
## Logically you should usr a single > re-director for results file. Instead of >> which is used for appending to file while creating results.txt.

#Show the output/average
echo; echo File results.txt looks like:
cat results.txt

OUTPUT:

File outtime.txt looks like:
----------
0.112
0.108
0.095
0.084
0.110


File results.txt looks like:
DES3 average is:  .10180000000000000000
查看更多
Animai°情兽
3楼-- · 2019-08-20 08:47

Executing your script with the bash -x option for debugging reveals that the tsum variable is acting as expected in your while loop, then its value is reset to zero after the loop exits.

This happens because you are creating a new subprocess when you use the | operator just before while, and the subprocess has its own copy of the variable. You can avoid this by not piping the output from cat into a loop, but instead using a redirect operator to achieve the same result without creating a subprocess.

This is done by changing this

cat outtime.txt | \
while read num
do
tsum=`echo $tsum + $num | bc -l`
done

to this

while read num
do
    tsum=`echo $tsum + $num | bc -l`
done < outtime.txt

With this simple change, your new output becomes

DES3 average is:  .22080000000000000000

To learn more, read here.

http://www.gnu.org/software/bash/manual/html_node/Redirections.html

查看更多
登录 后发表回答