Saturday, November 7, 2009

Shell tricks: eval helps make dynamic scripting rock.

If you write shell scripts of any significant size, eval is a very good command to understand (well). It can make some code simpler, other code obsolete, and yet other code it makes possible that would not be otherwise.

What is eval?


According to the 2004 IEEE Std. 1003.1:

  • eval constructs commands by concatenating arguments.


The example given on the site is pretty simple but not very revealing.

What can I do with eval?


In practice eval lets you (better) write programs that write programs, or at least parts of programs. One common use of eval is to create variables with names relative to their execution environment at runtime. I won't go into all the reasons that this might be beneficial but some good ones are: tying the process to a user id it prevent file clobbering, process accounting based on parent process id, to avoid creating arrays or other complex data structures  (good if you want your scripts to be portable and conform to the POSIX standard).

Enough theory! How do I use eval?


Okay here's an example of how to use eval. Sometimes in a script you want to be able to name a variable dynamically. The following function does just that:
tackon(){
#simple function to tack arg2 ($2) on to the end of
#variable X (arg1)
error_msg="usage: tackon "
eval "${1:?${error_msg}}=\"\${$1} ${2:?${error_msg}}\""
}

This function is named tackon because it takes two arguments and appends arg2 to the end of (the value) of arg1. Here's an example of how it works:
gcw@gcwmbp:/Users/gcw>
$ students="Alex Mark Mike"
gcw@gcwmbp:/Users/gcw>
$ tackon students "Al Tim Steve"
gcw@gcwmbp:/Users/gcw>
$ echo $students
Alex Mark Mike Al Tim Steve

To understand what's going on you first have to know that arguments to the function are assigned to two variables named $1 and $2. When tackon() is called it takes whatever string is passed in as $1 and turns it into a variable. The diagnostics in the above version may be somewhat confusing so here's the function again stripped down to the bare essentials:
tackon(){
eval "${1}=\"\${$1} ${2}\""
}

Now we can more easily see how this works. Everywhere that we have $1 and ${1} it gets evaluated to the first argument. If we called our example, where we called tackon with "students" as the first argument it becomes "students=\"${students} ${2}\"". The quotes and other special characters may neeed to be escaped (based on what behavior you want).

From this point on there's no magic. Eval executes the interpolated command and you benefit from the heavy lifting of a very nifty command. There are many other cool ways to use eval and maybe I'll go over some of them in the future but for now I'll leave you to fiddle around with this.

No comments:

Post a Comment