Reading a file in bash
While working on a maintenance script, I’ve found myself using bash a bit more than using perl for many tasks. While I don’t mind writing in perl or python, sometimes it just seems like overkill.
I needed to read one line of a /proc/loadavg for a monitoring system and was using some code that had been written by one of our programmers in 2002 or so. The old code was written using three scripts, two of which were called from cron. The first script was written in perl, actually included /proc/loadavg as chomp($value=`cat /proc/loadvg`);
Ugh!
Then some math was done, and a decision made of what values to write for nagios’s local process to report back. Suffice it to say that the math done then involved executing a shell script from time to time based on some condition checks and a separate cron that cleaned up the status for nagios.
A literal translation of the script from perl to bash ended up with a snippet of code that looked like this:
#!/bin/bash LOAD=`cat /proc/loadavg` LOADAVG=${LOAD%%.*} echo $LOADAVG
We’ll spare the details regarding how badly the existing script converted LOADAVG to an int in perl. While this method works, it does cause a process to be forked needlessly to read /proc/loadavg.
A quick rewrite and we end up with:
#!/bin/bash read LOAD < /proc/loadavg LOADAVG=${LOAD%%.*} echo $LOADAVG
While this doesn't seem overly bad for a process executed once a minute, it does cause a larger problem when someone copies that snippet of code and uses it elsewhere.
If we modify the code slightly to:
#!/bin/bash for i in {1..10000} do LOAD=`cat /proc/loadavg` LOADAVG=${LOAD%%.*} done echo $LOADAVG
and
#!/bin/bash for i in {1..10000} do read LOAD < /proc/loadavg LOADAVG=${LOAD%%.*} done echo $LOADAVG
and run the following benchmark:
root@tsavo:/home/cd34# time ./s.sh 0 real 0m13.641s user 0m0.636s sys 0m2.304s root@tsavo:/home/cd34# time ./t.sh 0 real 0m0.485s user 0m0.332s sys 0m0.148s
we can see that there is a substantial impact to doing things.
Taking the integer portion of a floating point number also yields a number of terrible solutions involving sed, awk and cut — all forking a separate process or two.