Host CL Scripting Capability

Host CL Scripting Capability

Updated: 06Mar2000


Starting with V2.11.2 IRAF CL scripts have had the capability of being executed as host commands. This is still a primitive feature of the OpenIRAF project in development but is a functional part of the system. This page will evolve with more information as problems and tips are discovered and as this facility becomes a realistic part of programming systems using IRAF.

The following is in response to a question about the basic usage of the #!cl capability and is meant as a preliminary documentation of this feature. Users who still have questions are encouraged to contact IRAF Site Support at iraf@noao.edu.


I was wondering if you could provide (perhaps as a link on your web page) a bit more info on how to use the new ability of the IRAF cl to act as a shell for Unix scripts (as described in v2112revs.html).

The #!cl feature was only added in V2.11.2 and we don't have much experience with it ourselves so unfortunately there is no formal documentation. I'll post something like this reply on our web pages and update it as as we learn more to answer your question since we've had other inquiries recently.

The short answer for your problem: You need to define an arch variable in the script to locate the binaries as well as logver (e.g. on a Solaris system where your IRAFARCH is "ssun" your definition of arch should be ".ssun", for "linux" it would be ".linux", etc). Normally this is defined by the cl.csh startup script, along with environment defs like $iraf and $IRAFARCH.


Now the details:

The thing to keep in mind is that a #!cl script does not make use of a login.cl file (and thus a loginuser.cl file) or the normal environment definitions that it sets or which are set in the cl.csh startup script. This means that #!cl script start really fast, but things taken for granted like package loading and foreign task definitions must be dealt with in each script itself. Scripts will accept command line arguments, which are passed as a space-delimited args script variable and are inserted into the cl.args parameter.

The form of a #!cl script should be something like:


        #!/usr/local/bin/cl.e -f		    [1]
        <environment definitions>		    [2]
        <parse args>			    	    [3]
        <task declarations>		    	    [4]
        <script body>				    [5]
        logout					    [6]

To explain each section:

[1] #!/usr/local/bin/cl.e -f

[2] <environment definitions> [3] <parse args> [4] <task declarations> [5] <script body> [6] logout

Obviously with some thought, #!cl scripts could be written differently, but it's important to remember that the default iraf environment used when writing scripts to be run under the CL be maintained when writing host scripts. This environment however is not always obvious, so users with questions should feel free to contact IRAF site-support at iraf@noao.edu with any problems or questions. The host scripting capability and our documentation of it will evolve in time, stay tuned or ask about updates.



Updated: 06Mar2000

Example scripts ?

In all the examples the only deviation I've made from my last reply is that I'm assuming that the "arch" variable is set by the user's envir- onment in the .cshrc file. Doing so along with the use of a generic path to the CL executable such as "/usr/local/bin/cl.e" means that there is no platform dependence in the script itself. All that is required to re-use the scripts on a different machine is that 1) a link to the CL binary be created and 2) the user define an 'arch' variable in their unix environment.

Example 1: A host DISPLAY command.

For the first example let's write a wrapper script around the DISPLAY command to allow images to be displayed from the command line. A simplified version of the script would look something like:

    #!/usr/local/bin/cl.e -f

    reset stdimage = imt1024		# default environment	
    logver = "IRAF V2.11 May 1997"	# needed for IMAGES package

    images				# load needed packages
    tv
    {
	# Execute the command.
	printf ("display %s\n", args) | cl()

        logout				# shut down
    }
The use of braces here and in following examples is purely stylistic, they are not required for the script to execute properly.

Note that we must define a 'stdimage' if we intend to use something other than the default imt512. The setting of 'logver' is required since we will be loading the IMAGES package to get to the task. In order to let the script execute as fast as possible we don't define environment vars we don't strictly need here, e.g. home$, uparm$, etc.

The task itself is executed by simply creating a command string we will pipe to a cl() call that executes it. This allows us to simply pass all the arguments on the command line to the task. For example,

	% display dpix.imh 1
	% display dpix.imh 1 zscale-
	% display dpix.imh 1 zs- zr- z1=100 z2=1200
	% display dev\$pix.imh 1
Notice that in the last case when displaying dev$pix it was necessary to escape the '$', otherwise the entire argument list is passed to the DISPLAY command as-is, allowing us to retain the CL syntax for setting parameters. This is one reason the command is executed using the "command mode" syntax, using the normal procedure script "program mode" would mean that each argument would have to be broken out separately in order to define the parameter.

Example 2: A host IMARITH or IMEXPR command.

The idea here is roughly the same as above:

    #!/usr/local/bin/cl.e -f

    set imdir = "HDR$pixels/" 		# default environment	
    logver = "IRAF V2.11 May 1997"	# needed for IMAGES package

    images				# load needed packages
    imutil
    {
	# Execute the command.
	printf ("imarith %s\n", args) | cl()

        logout				# shut down
    }
Note that the environment and necessary packages will be different here so there is a little customization required for each script. In this case since we'll be creating an output image of some kind we'll need to define an 'imdir' of some kind.

Again we pass in the entire command line but with a task like this we not only need to be careful about escaping the '$' of an IRAF logical, but other special chars such as '*'. For example,

	% imarith dev\$pix \* 10 newimg 
The host csh regards '*' as a special char and so it too must be escaped so as not to be expanded on the command line.

The same example could be used as a wrapper for the IMEXPR command by simply changing the command name from 'imarith' to 'imexpr' since the rest of the script is identical. The thing to note here however is again the shell escapes required. For example, the expression argument to this task is often put in quotes on the CL command line, and those quotes need to be passed thru to the script in order for the command to execute properly. To do this you would use a command such as

	% imexpr '"(a > 100 ? 0 : a)"' newimg a=dev\$pix

Example 3: A host graphics command.

Graphics commands can also be used in #!cl scripts, but note that in this case we need to set the terminal type so the plot appears correctly. This can either be some hardwired value or it can set using an envget() call on the TERM environment variable as is done in the login.cl. In this case it may also be desirable to set the 'stdplot' variable so the '=' or ':.snap' hardcopy commands work as needed. For example, a script around IMPLOT might look something like:

    #!/iraf/iraf/bin.ssun/cl.e -f

    reset stdplot = "lw8"		# default environment
    stty xgterm				# set the terminal type

    plot				# load needed packages.
    {
	# Execute the command.
	printf ("implot %s\n", args) | cl()

    	logout				# shut down
    }

Example 4: A host HELP command.

As a last example let's see write a #!cl script that parses the args list to make the command look more like a unix command. In this case we'll write a wrapper for the HELP command that uses '-r' and '-p' command line options to modify the task behavior slightly.

    #!/usr/local/bin/cl.e -f

    set     home    = "/u2/fitz/"	# default environment
    set     uparm   = "home$uparm/"

    stty xgterm				# set terminal type for pager
    lists				# need LISTS for arg parsing

    {
	# Declare local variables.
	string	arglist			# arg values
	string	command = "help"
	string  task			# options

	# Parse unix command-line args.
	arglist = mktemp ("tmp$help")
	print (args) | words ("STDIN", > arglist)
	list = arglist
	while (fscan (list, s1) != EOF) {
	    if (s1 == "-r") {  
		command = "references"
	    } else if (s1 == "-p") {  
		command = "phelp"
	    } else {
		task = s1
	    }
	    ;				# needed to fix CL grammar quirk
	}
	delete (arglist, ver-)		# clean up
	list = ""
	
	# Finally, execute the requested command on the task.
	printf ("%s %s mode='q'\n", command, task) | cl()

        logout				# shut down
    }
For this script the default behavior is to simply call the HELP task on the named argument (i.e. the task). By setting the '-p' flag we can instead call PHELP in order to page the output, or by using '-r' call REFERENCES to do a subject search for a given keyword.

In this case we do define a uparm$ local because we might wish to make use of the quick-reference file normally stored there and used by the REFERENCES task (and assuming the references.usequick param has been set in a CL session earlier). Again we need to define the terminal type because we may be using the file pager.

The LISTS.WORDS task is used to break an 'args' string such as "-p imexamine" (i.e. we want to run "phelp imexamine") into a list of arguments written to a temporary file. We fscan the file to get each argument and act accordingly to set the command to be run or the task name. Note there is no method here for setting specific parameters that might be desired such as the help 'option' or 'section', but they could be easily added in the same way.

To use this task it could be called as

	% help -r mask			# to find all 'mask' tasks
	% help -p imexam		# page help for IMEXAMINE
In this way we can combine several similar tasks into one.

The "script body" in these examples are all relatively primitive. As long as the proper environment is defined and required packages loaded, the body of the script can declare it own variables or tasks and be written in much the same way as any other procedure script. Users should note however that the same rules against multiply loading packages in scripts which call scripts (and lead to "dictionary full" errors) still apply, to be safe packages can be checked before loading using something like

    	if (!defpac ("images")) {
       	    images
	}
This example also points out a bug in the CL in which a null ';' statement is required following an if-clause if there is no other valid statement following the closing brace (see the arg parsing).

Lastly, if no arguments are passed at all to any of these scripts the normal parameter prompting will still be in effect, but the escapes for special characters will not be required. For example,

	% implot
	image to be plotted: dev$pix

The earlier posting and these examples, along with revisions to them, will be made available from our web page. Feel free to followup with questions and anyone who develops a handy #!cl script they'd like to distribute should feel free to drop if off in our /contrib directory on iraf.noao.edu (along with a readme saying what it does) or post it to adass.iraf.sources.


[IRAF logo] IRAF [NOAO logo] NOAO

Last updated: 06Mar2000