Executing Other Programs

A common complaint from beginners trying to execute other programs from Tcl is “It works in the shell but not in Tcl.” Let’s suppose you write a small Bourne shell script to strip the first word of each line and return a count of unique words:

$ awk '{print $1}' somefile | sort-u | wc -l

This works fine when you execute it on your terminal. You then cut and paste the line into your Tcl program, setting a variable to the number of unique words:

set numWords [exec awk '{print $1}' somefile | sort -u | wc -l]

Tcl will report an error “can’t read ’1’: no such variable.” You might try to fix that error by quoting $1 as $1, but that causes another error message, “awk: syntax error near line 1.” You ask, “What gives? It worked as a shell command but fails under Tcl!”

Tcl’s exec command executes other programs directly without the use of the shell. Tcl’s exec goes about collecting arguments, building pipelines, and invoking another program, all according to Tcl syntax rules. A single quote character (') has no special significance, unlike in most user shells. Tcl applies its parsing rules and breaks up the command pipeline into Tcl words. Thus, the awk program in awk’s first argument is passed as:

'{print

and not as the desired string:

{print $1}

as it is passed with a command-line user shell (Bourne shell, C shell, Korn shell, Bash, etc.).

The simple fix is to use Tcl quoting instead of shell quoting! Replace the single quotes (') with Tcl braces:

set numWords [exec awk {{print $1}} somefile | sort -u | wc -l ]

Since Tcl strips off one layer of braces during parsing, the first argument to awk is now a Tcl quoted string whose value is the correct awk program.

Another difference between Tcl’s exec and typical shell processing is dealing with filename expansion. Most shells expand the wildcard characters *, ?, and [], matching filenames. Each matched filename becomes a separate argument to the program being executed. Tcl’s exec does not perform filename matching directly, but you can use the glob command to match filenames. The only trick to this method is that most programs still need to have each filename as a separate argument. Glob command expansion returns a single word, the list of filenames matched, as if the resulting value had been enclosed in quotes.

For example, trying to print all C source files might be attempted as:

set printRequest [exec lp [glob *.c] ]

but this fails, complaining “file not found.” The solution is to use the eval command, which adds one more round of Tcl command-line expansion. This effectively “unrolls” the filename list into separate word arguments:

set printRequest [eval exec lp [glob *.c] ]
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset