IRAF paso a paso: 7 - CL scripts

This is a brief introduction to CL scripts, to show that they are not as difficult as it might seem at first sight, and that they might be useful from time to time, especially the terminal scripts.

Basic scripts

In its simplest form, a CL script is a logical, ordered sequence of CL commands. No variables or special syntax are used in this type of scripts.
In other words, instead of writing commands at the cl prompt, they are written in a file and then executed.

It is a good idea to always write down the commands in a file, and then either execute the file, or just cut-and-paste the commands from the file to the IRAF window.
In this way all you are doing is stored in a file, in a much better organized way than just using the CL logging option; editing is easier, and you can add your own comments.

Here is a list of some of my own (old) CL script files that I have used or am using. Comments help understand what is going on, and sometimes also the output of a task is recorded for future reference.
Usually a group of commands is brought to the IRAF terminal by cut-and-paste.
N.B: Modern browsers may not allow direct access to the link below, because of security reasons. If so, open a new tab o window, and copy-and paste the links below in the new address box.

To make CL execute your procedure, do (assume your procedure is in file
cl> cl <
The "cl <" thing means that what is on the right-hand side is given in input to CL.

Terminal CL scripts

Next step is to write a script which uses some of the CL language capabilities, as strings, numerical variables, ifs and whiles, etc. We are still far from writing a task-like script.
This kind of script often saves from repeating many times the same command, just changing the value of one parameter or two each time.

Example: a user has a calibrated 2-D spectrum, and wants to dump each spectral line on an ASCII file having the wavelength in the 1st column, the pixel intensity in the 2nd. wspectext is the right task; the problem is to execute it in turn for every line.
The script might be as follows:

# script to wspectext each spectral line in turn on a text file.
# change name of image and output file if necessary.
# use with: cl <

# assign file names to string variables s1 and s2:
  s1 = "ap96_"
  s2 = ""

# start loop:
for (i=1; i <= 111; i+=1)  {
  j = i+4
  s3 = s1//j
  print (s3)
  wspectext (s2//"[*,"//i//"]", s3, head-)

  1. A loop is performed, with variable i going from 1 to 111. Notice syntax
  2. Variable j is set to i+4
  3. The name of the output file, in string s3, is formed as a value of s1 plus the value of j (note concatenation of a string and an integer)
  4. The value of s3 is printed out
  5. The task wspectext is executed. Since there are string variables and string operations in its arguments, these are passed to the task using the parenthesized format (program mode). Notice how the image section is built by string/integer concatenation.

In this procedure, CL is used in the so-called program mode (or compute mode), which takes advantage of the full syntax of CL as a programming language, where, for instance, it can process strings and variables.
The other mode of CL, with more limited capabilities but a simpler user interface, is called command mode, and is the one generally used at the cl> prompt. It minimizes the need for command delimiters, quoted character strings, etc.

Here is a more complicate script to compute King models, and convolve them with the PSF.

# procedure to create King models and convolve them with
# the PSF
# A oversampling factor of 9 (good for Rcore>=0.15)
# is used to properly compute pixel values.
# use as:  Rcore=1.8 ; imname="King_180" ; suf="U" ;  cl <
# (Rcore and imname must be already defined)
string  expr, exprA, exprB, exprC
string  imnameC, outf, psfim
real    Tidterm, RcoS, Rtico, RtidS

set imtype = "fits"

# set variables
Rtico = 30                             # set tidal radius = 30 * core radius
Tidterm = 1.0/sqrt(1+(Rtico*Rtico))
outf = imname//suf//".prof"
RcoS  = 9.0*Rcore
RtidS = Rtico*RcoS                     # tidal radius
psfim = "psf.imh"

# compute King profile. The computed profile is oversampled
# 9 times (linearly), then is brought back to the original
# resolution by block-averaging
# King models must be set to 0 at R > Rtidal
# strings containing the matematical operations are defined, and then
# passed on to tasks imexpr and imcalc
exprA = "1.00+(((I-572.00)**2+(J-572.00)**2)/("//RcoS//"**2))"
exprB = "I*0.0+J*0.0+"//Tidterm
exprC = "(((I-572.00)**2+(J-572.00)**2) > "//RtidS**2//") ? 0.0 : a" 
expr = "((1.0/sqrt(im1))-im2)**2"


# normalize to total flux=1000.0
gstat(imname)                   # output in gstpar set
x = gstpar.sum/1000.0

# convolve with PSF (I-band, chip4)
# PSF must be normalized (sum=1)

# remove temporary images
imdel King_A.fits,King_B.fits,King_C.fits,King_D.fits veri-

# output radial profile onto a file; sort it
pradprof(imname//suf,64.00,64.00,radius=20,center=no,list+,logx-,logy-, > outf)
sort(outf,column=1,numeric_sort=yes, > "sort.sort")

# end of script 

To use this script, we should do:
cl < Rcore=1.8 ; imname="King_180" ; suf="U" ; cl <


  1. Variable used in the script have been declared; those used in the above command line also must have been declared.
  2. The name of the output file, in string ouf, is formed as a the concatenation of string variables imname, suf and the string constant ".prof".
  3. The powerful imexpr task is used; the mathematical operations are defined in the exprN variables, and then passed to the tasks. Also these expressions are a concatenation of strings.
  4. Various tasks are then executed. Since there are string variables and string operations in their arguments, these are passed to the tasks using the parenthesized format (program mode). When using the redirection (>), it must go inside the parenthesis.

Quiz 1:
There is a CL script that virtually every user is familiar with, and indeed uses pretty often. What is it?
Quiz 2:
A user wants to be able to start IRAF from any directory, and, once in IRAF, be right away in the directory he started from. Can you find a practical solution, using a Unix script + a CL script? (Just give the idea, not all the details)

Write a script that displays an image with z1=(mean-2*stddev) and z2=(mean+2*stddev), where mean and stddev are the mean and the standard deviation of an image. Of course these values must be computed inside the script.

CL scripts as new tasks

It is possible to write CL scripts that are similar in every aspect to genuine IRAF tasks. Actually, a good number of native CL tasks (i.e. present in the standard IRAF packages) are CL scripts themselves. Examples:
stsdas.analysis.gasp.regions - images.immatch.wcsmap - stsdas.describe   etc.
These new tasks have their own parameter set, share the piping and redirection capabilities, might be run in background, etc.

Very simple example:

#  DESCRIBE --  A shortcut task to see the "Description" section of a help   #
#               file.                                                        #
#                                                                            #
#       8/91    Initial code by  PAHodge and RAShaw                          #
# Modified for clarity (NC).

procedure example (taskname)

string  taskname = "" {prompt = "Enter task name > "}

        string  tasknm        # defines tasknm as a string variable
        tasknm = taskname     # reassign taskname to tasknm
        print ("\nDESCRIPTION for task: ", tasknm)    # print 1-line header
        help (tasknm, section="description")          # print help

This IRAF task simply displays the "description" section of a help file.
Taskname is a required parameter, as is present in the task definition. There are no hidden parameters.
Note that the taskname variable is reassigned to a new variable, tasknm; if they had used taskname in the following "print" and "help" commands, IRAF would have prompted for taskname.

Another more complex example:

#  script taken from the "arnica" package. Adapted to clarify various
#  points (NC).
#  Cleans bad pixels using bad pixel mask and 3x3 median smoothing
#  CLEANED image has prefix b

      procedure clean ( sfrm )

string sfrm          { prompt="Frame name to clean" }
string smask = "arnica$badpix/bpf.fix.93jun" {prompt="Bad pixel mask"}

# Notice there is one required parameter (sfrm) and one hidden one (smask),
# which has a default value.


        string msk1, msk0, junk0, intermed, sf, mask

        sf = sfrm
        mask = smask

# Make temporary images. Names will be like this: imf+PID+letter, and will
# be placed in the directory defined by the variable tmp

        intermed = mktemp ("tmp$imf")
        msk0     = mktemp ("tmp$imf")
        msk1     = mktemp ("tmp$imf")
        junk0    = mktemp ("tmp$imf")

# Median filter to smooth out image; output on temp file "ntermed"

        median ( sf, intermed, 3, 3  )

# Multiply smoothed image by mask
# Perform other mathematical operations, delete junk images, print output
# Notice how strings are concatenated.

        imarith ( intermed, "*", mask, msk1 )
        imarith ( sf, "*", mask, msk0 )
        imarith ( sf, "-", msk0, junk0 )
        imarith ( junk0, "+", msk1, "b"//sf )

        imdel (junk0//","//msk0//","//msk1//","//intermed, ver-, >& "dev$null")

        print (sf, ", ", "b"//sf)


A few comments to describe the above scripts:

  1. The title of the procedure (in this case "describe" or "clean") must be equal to the rootname of the file (i.e., file must be called ""/ "")
  2. All required parameters must be put between parentheses in the procedure naming line; hidden ones are the ones before the line "begin".
  3. Parameters (both types) are declared, an initial value and a prompting string can be assigned. Prompt string will appear doing lpar or epar or dpar.
  4. "begin" marks the start of the script body, "end" the end; both must be present.
  5. New variables must be declared as strings, integers, etc.
  6. Some files are created to store temporary images used during the processing. They are created in a temporary directory, and will be deleted at the end.
  7. The expressions sf = sfrm and mask = smask are to avoid that IRAF prompt for sfrm whenever it is named, even in "imarith". For sake of prudence, the same reassignment is performed for the hidden parameter.
  8. Arguments passed to the IRAF tasks are within parentheses, because tasks are used in program mode. Constant strings are within "", variables are not.

To let IRAF know that these are new scripts, we do:
cl> task describe =
cl> task clean =
Magically, doing cl> epar clean or cl> epar describe will show the task parameters, which can be edited. Now they are tasks like any other one.

A user wants to normalize her images, that is divide the images in a given list by their mean, computed only in a certain range (say 100 - 7000) to avoid the influence of bad pixels. Use task imstat or gstat. Can you devise a script to do that? (just the idea, not the whole script). What if the range is different for every image?

Further things to know about CL scripts

  1. To avoid confusion between string variables and constant, character strings should be quoted, expressions parenthesized.
  2. It is possible to assign a value to a variable when it it declared. Ex:
    string st = "file.dat" or real mag = "12.5"
  3. Never load a package within a script. Make sure all the relevant packages are loaded before starting the script.
    It is possible to check whether a package is not loaded and issue a warning with, for instance, the lines:
         if (! defpac ("proto")) 
         print ("Warning: package proto not loaded!") 
  4. Personal tasks may be arranged in a package, which will have the same characteristics as any other IRAF package. Also it is possible to add help files to personal tasks and packages.

Struct variables:
A struct variable is a special one with regards to reading from files using the "fscan" function. Basically, there are two type of this things:

  1. Character strings treated in a special way by the scan and fscan functions, which set struct to be the remainder of the line be scanned without further parsing. Ex:
         struct stru 
         fscan (list, s1, s2, stru) 
    will read the file associated to variable list, and assign the 1st string encountered to s1, the 2nd to s2, the whole rest of the line to variable stru.

  2. Variable usually associated with a file name, whenever we want to use fscan to read lines from the file. In this case the declaration is of the form: struct *file with the "*" before the variable name. file is said to be a list-directed struct

An example that contains the two structs:

procedure printfile (file_name)

  string filename
  struct *flist

   struct line
   flist = filename
   while (fscan(flist, line) != EOF)
      print (line)
   flist = ""


This procedure will read from file file_name (a required parameter) and print every line in turn. flist is a list-directed struct, line is a simple struct. For some reasons I do not fully understand, the "struct *flist" definition must go before the "begin" line.
It is recommended to always close the list-structured variable with flist = "" to avoid that the associated file remain open by mistake (it will close if the end-of-file is encountered by fscan, but sometimes an "if" jump might prevent fscan from reaching the file end).

More detailed information on how to write scripts, organize them in packages, write help files, etc. may be found in the Introductory User's Guide to IRAF scripts