Checking arguments

I shoe here the start of a csh script I have written. I am trying to write some code to check the arguments and if the arguments don't match the tags, I will abort and display an error.

For example using

./script.csh -r=10/20.30/40 -xyz=2/3/4

will give an error as the -xyz tag doea not exist.

  set ierr = 0
  set iarg = 0
  set opt_jbase = 0
  set opt_range = 0
  set opt_annot = 0
  set opt_fout = 0
  set opt_vrb = 0
  set opt_usage = 0
  set opt_examples = 0
  set opt_help = 0

  set fullnames_list = ""
  set fnames_list = ""
  set fext_list = ""
  set nf = 0

  set Version = "V04"
  alias MATH 'set \!:1 = `echo "\!:3-$" | bc -l`'

  set narg = $#argv
  while ($iarg < $narg)

    MATH iarg = $iarg + 1
    set arg = $argv[$iarg]
    set opt = `echo $arg | awk 'BEGIN {FS="="} {print $1}'`
    set par = `echo $arg | awk 'BEGIN {FS="="} {print $2}'`

    switch ($opt)
    case "-jb":
        set Ajbase = $par
        set bwidth = `echo $Ajbase | awk '{split($1,a,"/"); print a[1]}'`
        set bheight = `echo $Ajbase | awk '{split($1,a,"/"); print a[2]}'`
        set opt_jbase = 1
        breaksw
    case "-r":
        set Arange = $par
        set xmin = `echo $Arange | awk '{split($1,a,"/"); print a[1]}'`
        set xmax = `echo $Arange | awk '{split($1,a,"/"); print a[2]}'`
        set ymin = `echo $Arange | awk '{split($1,a,"/"); print a[3]}'`
        set ymax = `echo $Arange | awk '{split($1,a,"/"); print a[4]}'`
        set opt_range = 1
        breaksw
    case "-a":
        set Aannot = $par
        set adx = `echo $Aannot | awk '{split($1,a,"/"); print a[1]}'`
        set ady = `echo $Aannot | awk '{split($1,a,"/"); print a[2]}'`
        set afx = `echo $adx | awk '{print $1/2}'`
        set afy = `echo $ady | awk '{print $1/2}'`
        set ax = "a"$adx"f"$afx
        set ay = "a"$ady"f"$afy
        set opt_annot = 1
        breaksw
    case "-fout":
        set Afout = $par
        set fout = `echo $Afout | awk 'BEGIN { FS=".ps" } { print $1 }'`
        set opt_fout = 1
        breaksw
    case "-v":
        set opt_vrb = 1
        breaksw
    case "-usg":
        set opt_usage = 1
        breaksw
    case "-eg":
        set opt_examples = 1
        breaksw
    case "-h":
        set opt_help = 1
        breaksw
    default:
        set filename = `echo $arg | awk 'BEGIN {FS="."} {print $1}'`
        set filextension = `echo $arg | awk 'BEGIN {FS="."} {print $2}'`
        set fullnames_list = "$fullnames_list $arg"
        set fnames_list = "$fnames_list $filename"
        set fext_list = "$fext_list $filextension"
        MATH nf = $nf + 1
    endsw

  end   # while

Move all command line arguments to appropriate variables, first, all, and then detect invalid combinations of variables.

What you mean? Suppose a user tries an option that does not exist.

Sorry. Couldn't find the question. What follows is just my guess at what you may want:
The processing under the 'default' tag in your switch looks like a pre-processing block that should be done before entering the switch. Your instructions inside the switch do something for each and every valid argument given, so your 'default' tag should be considered an 'otherwise' tag instead and include there whatever error processing you have in mind when the user inputs an illegal argument.

Problem is you may have already done some processing before an invalid tag is discovered. If the script then bombs out with an invalid arguments error some work has already been done. This different to how every other unix command operates, and is quite dangerous.

As DGPickett implied, try to validate all the arguments first and then do the processing. Consider using getopt to simplify the argument parsing.

Yes, some things would have already been done, but it will only be about processing the information given (declaration of variables). So it does not matter if I stop and exit.

I use the dafault to pass filenames (no tags are used when passing certain files).

Can you give a small example that I can follow Chub?

Yep, I didn't read you code closely enough. All the awk scripts are just pulling apart your arguments. Might be better to drop the = part and put parameter in next arg (this is the unix standard way) eg:

./script.csh -r 10/20.30/40 -xyz 2/3/4

This might get you started:

#!/bin/csh
set done=0
set opt_vrb
set ajbase
while ( $#argv != 0 && $done == 0 )
    switch ($1)
        case -jb:
            if ( $2 == "") then
                echo "option -jb requires argument"
                exit 2
            endif
            set ajbase=$2
            shift
            breaksw
        case --:
            set done=1
            breaksw
        case -v:
            set opt_vrb=1
            breaksw
        default:
            set F=`expr substr $1 1 1`
            if ( "$F" == "-" ) then
               echo "Invalid argument $1"
               exit 2
            endif
            set done=1
            breaksw
    endsw
    if ( $done == 0 ) then
        shift
    endif
end
if ( $opt_vrb == 1) then
   echo -v option set
endif
if ( "$ajbase" != "" ) then
   echo ajbase=$ajbase
endif
echo File Arguments left: $argv

Will scrutinize it and use this method. Thanks. :b:

---------- Post updated at 06:28 PM ---------- Previous update was at 06:25 PM ----------

I thought you were going to use getopts! Or it's unavailable in csh?

getopt works OK in csh but you will need to use single char flags (but I personally perfer single char args).

As you can see getopt cuts a lot of the mucking around out as it checks the parameters for you

#!/bin/csh
set ajbase
set arange
set opt_v
set opt_u
set opt_e
set argv=`getopt j:r:vue $*`
 
if ( $status != 0 ) then
    exit 1
endif
 
while ( "$1" != "--" )
    switch ($1)
        case -r:
            set arange=$2
            shift
            breaksw
        case -j:
            set ajbase=$2
            shift
            breaksw
        case -v:
            set opt_v=1
            breaksw
        case -u:
            set opt_u=1
            breaksw
        case -e:
            set opt_e=1
            breaksw
    endsw
    shift
end
shift # skip --
 
if ( $opt_v == 1) then
   echo -v option set
endif
if ( "$ajbase" != "" ) then
   echo ajbase=$ajbase
endif
echo File Arguments left: $argv

Bugger!!! I do need more than single args in what I'm doing. So I can't use getopts then anyway.

Check your man on getopt as some modern OS's (eg linux) support long options and have the -a flag to getop that allows long options with a single '-'. But then again if you were on a modern OS you probably wouldn't be using csh anyway.

eg:

set argv=`getopt -a -l jb: r:vue $*`
 
...
    case --jb:
            set ajbase=$2
....

There is -a option. I will be running things on ubuntu as well. Not sure if there will work. I'm on fedora at this time.

---------- Post updated at 07:17 PM ---------- Previous update was at 07:10 PM ----------

Have been doing some code in ksh. Have had a try with pyhton, but as I've only started, I get more productive just using csh.

---------- Post updated at 07:19 PM ---------- Previous update was at 07:17 PM ----------

For ksh I have something like this. Feel free to comment.

  narg=$#
  argv=$*

  ierr=0
  iarg=0
  opt_spath=0
  opt_fry=0
  opt_fxt=0
  opt_fdat=0
  opt_srmax=0
  opt_usage=0
  opt_example=0
  opt_help=0
  opt_info=0

  print "HELLO\n"
  print "dsfsdf"

  for arg in $argv; do

    opt=$(print -R $arg | awk 'BEGIN {FS="="} {print $1}'  \
                        | tr "[A-Z]" "[a-z]")
    par=$(print -R $arg | awk 'BEGIN {FS="="} {print $2}')

    case $opt in
    "-spath" )
        Aspath=$par
        spath=$Aspath
        opt_spath=1
        ;;
    "-fry"  )
        Afry=$par
        fry=$(print -R $Afry | awk 'BEGIN {FS=".ry"} {print $1}')
        opt_fry=1
        ;;
    "-fxt"  )
        Afxt=$par
        fxt=$(print -R $Afxt | awk 'BEGIN {FS=".xt"} {print $1}')
        opt_fxt=1
        ;;
    "-fdat"  )
        Afdat=$par
        fdat=$(print -R $Afdat | awk 'BEGIN {FS=".dat"} {print $1}')
        opt_fdat=1
        ;;
    "-srmax"  )
        Asrmax=$par
        srmax=$Asrmax
        opt_srmax=1
        ;;
    "-usage"  )
        Ausage=$par
        opt_usage=1
        opt_info=1
        ;;
    "-example"  )
        Aexample=$par
        opt_example=1
        opt_info=1
        ;;
    "-help"  )
        Ahelp=$par
        opt_help=1
        opt_info=1
        ;;
    *  )
        Aerr=$arg
        ierr=1
        ;;
    esac #end case

  done   # end for

The further you go, the more problems you will have with csh. Do yourself a favour and switch to a POSIX shell.

Top Ten Reasons not to use the C shell
Csh problems
Csh Programming Considered Harmful

The problem with posix is that I cannot use the new stuff. I need to do floating point calculations in my scripts as well. Any ideas?

---------- Post updated at 07:28 PM ---------- Previous update was at 07:25 PM ----------

Is there a scripting language that avoids me the problems when using different versions of UNIX?

Use ksh93 or awk.

POSIX-compliant scripts are portable.

In ksh you can you use parameter subsitution to do a lot of the stuff you are using awk for, also use typeset -u to force variable to lowercase:

$ arg="-fry=test.ry"
$ echo ${arg%%=*}
-fry
$ echo ${arg#*=}
test.ry
$ par=test.ry
$ echo ${par%.ry}
test
$ typeset -u opt
$ opt="-FRY"
$ echo $opt
-fry

I agree with cfajohnson that you will be much better off sticking to a POSIX shell, if your worried about ksh93 availablility use ksh88 and call bc or awk to do the floating point stuff.

You're right.

I do also have things like this where I input for example -rrms=12/30/23/67

    "-rrms"  )
        Arangerms=$par
        xminrms=$(print $Arangerms | awk '{split($1,a,"/"); print a[1]}')
        xmaxrms=$(print $Arangerms | awk '{split($1,a,"/"); print a[2]}')
        yminrms=$(print $Arangerms | awk '{split($1,a,"/"); print a[3]}')
        ymaxrms=$(print $Arangerms | awk '{split($1,a,"/"); print a[4]}')
        opt_rangerms=1
        ;;
$ echo "12/30/23/67" | IFS=/ read xminrms xmaxrms yminrms ymaxrms
$ echo $yminrms
23

In the csh script I have but something like this

If I pass some silly thing like ./myscript -xxx

    default:
        set error = `echo $arg | awk '/-/'`
        echo "$error"
        if ($error != "") set ierr = 1

I get error

-xxx
if: Missing file name.

put quotes around $error . Also will you ever want a filename with a hyphen in it (eg dump-data) perhaps awk should be awk '/^-/'