i’m so full of ideas

Archive for the 'programming' category

bash: timing long-running processes

February 3, 2009 1:44 pm

The innertubes have spoken. According to the search terms I’m seeing in my access logs, you interwebbernauts want to see more bash articles. Very well, then.

Every now and then I write a bash script that might take an hour or more to run. It’s nice to know exactly how long it took, so you can plan accordingly when you want to run it the next time. So I formalized some bash timing concepts I’ve been using for years and packaged them up into a code library you can use in your own scripts.

If you’re just interested in the code, you can skip to the bottom of this entry and cut-and-paste the parts you want into locations of your choosing. If you’ve got a minute, I’ll explain some of the non-obvious bits.

bash functions can only return small integers, typically not larger than 255 or so. They are intended for use as values you’d return to the shell, when the script is finished running. If you want to write a function that returns a string or a large integer, you’re out of luck. The way I get around that one is to write functions that use echo to print a string to stdout. The output can be captured by a calling function using this rather awkward syntax:

    returnValue=$(functionName)

Unfortunately, variables in bash scripts are by default global, and therefore available to all functions. I don’t like that very much, so my local variable declarations are all prefixed with the keyword local, which is supposed to make them visible only within the current function. I have a sneaking suspicion that supposedly local variables can still “leak” into other functions, however. I suppose I should investigate that situation and use it as the basis for a future blog post.

Finally, you can use the source keyword to pull functions from one script into another, like this:

    source timerlib.sh

Now, on to the code. First up is timerlibtest.sh, a script that demonstrates use of the timerlib functions.

#!/bin/sh # timerlibtest.sh -- test for timerlib.sh # by allen brunson  allen.brunson@gmail.com  february 3 2009 # pull in timerlib functions source timerlib.sh # this function shows how to time a long-running process function main() {     # save current time at the beginning     local time=$(timerStart)         # put whatever you want to time here     echo "starting long-running process ..."     sleep 2         # display how long it took     timerStop $time "elapsed time:" } # execution start main $*

Here is the text for timerlib.sh:

#!/bin/sh # timerlib.sh -- functions for timing long-running operations # by allen brunson  allen.brunson@gmail.com  february 3 2009 # return seconds since 1970 # does not work on some unix variants.  check 'man date' for details function timerCurrent() {     date "+%s" } # inputs a number of seconds, outputs a string like "2 minutes, 1 second" # $1: number of seconds function timerLengthString() {     local days=$((0))     local hour=$((0))     local mins=$((0))     local secs=$1     local text=""         # convert seconds to days, hours, etc     days=$((secs / 86400))     secs=$((secs % 86400))     hour=$((secs / 3600))     secs=$((secs % 3600))     mins=$((secs / 60))     secs=$((secs % 60))         # build full string from unit strings     text="$text$(timerLengthStringPart $days "day")"     text="$text$(timerLengthStringPart $hour "hour")"     text="$text$(timerLengthStringPart $mins "minute")"     text="$text$(timerLengthStringPart $secs "second")"         # trim leading and trailing whitespace     text=${text## }     text=${text%% }         # special case for zero seconds     if [ "$text" == "" ]; then         text="0 seconds"     fi         # echo output for the caller     echo ${text} } # formats a time unit into a string # $1: integer count of units: 0, 6, etc # $2: unit name: "hour", "minute", etc function timerLengthStringPart() {     local unit=$1     local name=$2         if [ $unit -ge 2 ]; then         echo " ${unit} ${name}s"     elif [ $unit -ge 1 ]; then         echo " ${unit} ${name}"     else         echo ""         fi     } # useful for testing timerLengthString function timerLengthStringTest() {     local days=$((86400))     local hour=$((3600))     local mins=$((60))     local secs=$((1))         timerLengthString  0     timerLengthString 20     timerLengthString $(( ($hour * 3) + ($mins * 1) + ($secs * 52) ))     timerLengthString $(( ($days * 1) + ($secs * 14) )) } # synonym for timerCurrent function timerStart() {     timerCurrent $* } # display final elapsed time # $1: value returned from an earlier call to timerStart() # $2: optional descriptive string, such as "total wait time:" function timerStop() {     local desc=$2     local secs=$((0))     local stop=$(timerCurrent)     local text=""     local time=$1         if [ "$desc" != "" ]; then         text="$desc "     fi         secs=$(( $stop - $time ))     text="$text$(timerLengthString $secs)"         echo $text }

bash scripts: glue for unix

January 16, 2009 5:01 pm

At my last full-time job, I wrote some hideously complex bash scripts. They started out small and kind of got away from me. In hindsight, that’s pretty much the way it had to be. I was in charge of dozens of linux computers running hundreds of programs that I wrote, interacting with each other in complex ways. I didn’t fully understand the problem space, and even if I had, I didn’t have time to write all that logic in a more formal language. bash scripts are the perfect medium for spitballing solutions to problems you’re kind of fuzzy about.

This was my first attempt to solve big problems with bash, so I had a lot to learn. For example, I was surprised to discover that this script doesn’t work:

#!/bin/sh function_test function function_test() {     echo "test" }

This is an attempt to call a function before it’s defined. Trivial in C++, my native language, but not allowed in bash.

I’m kind of embarrassed to admit that I struggled with this for a long time. I wrote more and more bash functions, which became increasingly interdependent, so this problem got more troublesome as time went on.

For this reason and many others, I decided to adopt a more C-like structure for my bash scripts. Like this:

#!/bin/sh function main() {     test1     test2 } function test1() {     echo "test1" } function test2() {     echo "test2" } main $*

main() is calling test1() and test2() before they are defined.  But it doesn’t matter anymore, because all functions are defined before the program starts running. The bash interpreter has read the entire script before execution begins at the very last line, when main() is called.

I would have done this right from the beginning, but I didn’t know about the $* trick. That passes all command-line parameters received by the script to the main() function, so it can do whatever it likes with them.

command-line terminal for flash

December 12, 2008 7:20 am

My first, and possibly only, article on Flash/ActionScript. I wrote about adding a command-line terminal to a Flash applet. I got up to speed on Flash very quickly, thanks to lots of interesting blog posts I googled up. So it seemed only fair to write about something I learned in the process.