LinuxQuestions.org
LinuxAnswers - the LQ Linux tutorial section.
Go Back   LinuxQuestions.org > Forums > Non-*NIX Forums > Programming
User Name
Password
Programming This forum is for all programming questions.
The question does not have to be directly related to Linux and any language is fair game.

Notices

Reply
 
LinkBack Search this Thread
Old 05-31-2007, 07:49 AM   #1
pwc101
Senior Member
 
Registered: Oct 2005
Location: UK
Distribution: Slackware
Posts: 1,846

Rep: Reputation: 128Reputation: 128
Arrow bash case statement with multiple command line options


I'm trying to write a script which will read one or more command line options and run different parts of the script accordingly. I've had a look at http://tldp.org/LDP/Bash-Beginners-G...ect_07_03.html, and it seems a case statment is ideal, so:
Code:
#!/bin/bash

RETVAL=0

case "$1" in
   "") 
      echo "Usage: $0 [-p|--preprocess] [-i|--image]"
      RETVAL=1
      ;;
   --preprocess|-p)
      my_proprocessing_commands_here
      ;;
   --image|-i)
      my_imaging_commands_here
      ;;
esac

exit $RETVAL
This works fine if I want to do either the preprocessing or the imaging, but how do I write it so that I can do both?
 
Old 05-31-2007, 08:53 AM   #2
makyo
Member
 
Registered: Aug 2006
Location: Saint Paul, MN, USA
Distribution: {Free,Open}BSD, CentOS, Debian, Fedora, Solaris, SuSE
Posts: 718

Rep: Reputation: 72
Hi.
Code:
--both|-b) 
  my_proprocessing_commands_here
  my_imaging_commands_here
  ;;
cheers, makyo
 
Old 05-31-2007, 09:18 AM   #3
marozsas
Senior Member
 
Registered: Dec 2005
Location: Campinas/SP - Brazil
Distribution: SuSE, RHEL, Fedora, Ubuntu
Posts: 1,393
Blog Entries: 1

Rep: Reputation: 63
just another way....
process all command line options and take the proper actions.

Code:
#!/bin/bash

do_proprocessing() {
   my_proprocessing_commands_here
}

do_imaging() {
   my_imaging_commands_here
}


RETVAL=0
processing="false"
imaging="false"


while test -n "$1"; do
  case "$1" in
   "") 
      echo "Usage: $0 [-p|--preprocess] [-i|--image]"
      exit 1
      ;;
   --preprocess|-p)
      processing="true"
      shift
      ;;
   --image|-i)
      imaging="true"
      shift
      ;;
  esac
done

if [ "$processing" == "true" ]; then
  do_proprocessing
fi

if [ "$imaging" == "true" ]; then
  do_imaging
fi

exit $RETVAL

Last edited by marozsas; 05-31-2007 at 09:23 AM.
 
Old 05-31-2007, 09:21 AM   #4
pwc101
Senior Member
 
Registered: Oct 2005
Location: UK
Distribution: Slackware
Posts: 1,846

Original Poster
Rep: Reputation: 128Reputation: 128
edit: as I was composing my reply, you (maroszas) suggested the same as in this post http://www.linuxquestions.org/questi...00#post2506600. I should compose my replies quicker! I think this solution may work

Ah, yes. I'd thought of that, it's just that I wanted to avoid duplication within the script since I'm in the process of testing certain parameters to determine which is the best, and the equivalents of my_processing_commands_here and my_imaging_commands_here are quite long.

Could I create two variables: processing and imaging, which I set as all the processing commands, and all the imaging commands, which I then call from within the case statements, which would enable me to have a shorter version of the 'both' case?
Code:
#!/bin/bash

RETVAL=0

PROCESSING="$(my_proprocessing_commands_here)"
IMAGING=$(my_imaging_commands_here)"

case "$1" in
   "") 
      echo "Usage: $0 [-p|--preprocess] [-i|--image] [-b|--both]"
      RETVAL=1
      ;;
   --preprocess|-p)
      $PROCESSING
      ;;
   --image|-i)
      $IMAGING
      ;;
   --both|-b)
      $PROCESSING
      $IMAGING
esac

exit $RETVAL
Although obviously this won't work as all the (time consuming) preprocessing will be done before the case command is even reached.

Is there a goto statement?

Last edited by pwc101; 05-31-2007 at 09:25 AM.
 
Old 05-31-2007, 09:48 AM   #5
marozsas
Senior Member
 
Registered: Dec 2005
Location: Campinas/SP - Brazil
Distribution: SuSE, RHEL, Fedora, Ubuntu
Posts: 1,393
Blog Entries: 1

Rep: Reputation: 63
Quote:
Is there a goto statement?
Not I known...
 
Old 05-31-2007, 10:25 AM   #6
pwc101
Senior Member
 
Registered: Oct 2005
Location: UK
Distribution: Slackware
Posts: 1,846

Original Poster
Rep: Reputation: 128Reputation: 128
In the case statement, if I changed the $1 to $*, would that have the effect of reading in any variable I've put after the script name when I call it from the command line? So, that the following would be synonymous:
Code:
myscript.sh -p -i
myscript.sh -i -p
What I suppose I want to know is is the $1 the first option, $2 the second etc.? And would $* mean any option?
 
Old 05-31-2007, 10:39 AM   #7
marozsas
Senior Member
 
Registered: Dec 2005
Location: Campinas/SP - Brazil
Distribution: SuSE, RHEL, Fedora, Ubuntu
Posts: 1,393
Blog Entries: 1

Rep: Reputation: 63
Quote:
Originally Posted by pwc101
]What I suppose I want to know is is the $1 the first option, $2 the second etc.? And would $* mean any option?
Yes, you're right. $1 is the first argument in cmd line, $2 is the second one, etc.

"$*" is expanded for ALL arguments in cmd line, e.g. "arg1 arg2 arg3 arg4"

"$@" (with the quotes) is equivalent to "$1" "$2" ...

Last edited by marozsas; 05-31-2007 at 10:40 AM.
 
Old 05-31-2007, 10:46 AM   #8
pwc101
Senior Member
 
Registered: Oct 2005
Location: UK
Distribution: Slackware
Posts: 1,846

Original Poster
Rep: Reputation: 128Reputation: 128
Ok, so if I need to run the script with only certain of the options - sometimes with only one, sometimes with more than one, and sometimes with all of them, then my case statement (which has evolved a little since I first posted) would look a bit like this?
Code:
while test -n "$@"; do
   case "$@" in
      "")
         echo "Usage: $0 [-p|--preprocess] [-g|--gradient] [-i|--image] [-l|--plot]"
         RETVAL=1
         ;;
      --preprocess|-p)
         preprocessing="true"
         shift
         ;;
      --gradient|-g)
         gradient="true"
         shift
         ;;
      --image|-i)
         imaging="true"
         shift
         ;;
      --plot|-l)
         plot="true"
         shift
         ;;
   esac
done
The bit I'm least sure about is the while test -n "$@"; do... - should that be $1?
 
Old 05-31-2007, 12:22 PM   #9
marozsas
Senior Member
 
Registered: Dec 2005
Location: Campinas/SP - Brazil
Distribution: SuSE, RHEL, Fedora, Ubuntu
Posts: 1,393
Blog Entries: 1

Rep: Reputation: 63
No, it is "$1" in both while and case statements.
It will test one argument at time, and this argument is always the first argument.
The "shift" statement will (guess what ?) shift the 2nd argument to the first position, so when at begin of loop, the 2nd argument will be in $1 again until there is no arguments left to test.

This is a basic construction for parsing the command line.

There are others options too. If you are interested, there is the command getopt which is part of util-linux rpm package in my opensuse distro.
It is similar to getopt system call available to C programmers and offer a very sophisticate way to parse the command line.

cheers,
 
Old 05-31-2007, 01:26 PM   #10
pwc101
Senior Member
 
Registered: Oct 2005
Location: UK
Distribution: Slackware
Posts: 1,846

Original Poster
Rep: Reputation: 128Reputation: 128
Ah, perfect

There's still one small thing, and that's when there are no options specified after the script, it doesn't spit out the usage. Obviously not a major bug from my perspective, but it'd be nice to know how to get that to work too

Otherwise, it's working like a charm. Just in case anyone's interested, here's the whole script (maybe someone might need it?...)

For info, the majority of the programs being called are part of the GMT (Generic Mapping Tools) suite, which I believe is included in Ubuntu these days. We use it quite extensively here at work.
Code:
#!/bin/bash

# script to roughly plot the bathy from boldner cliff

##----------------------------------------------------------------------------##

# all the gumpf
area=-R606924/609044/5618607/5619770
proj=-Jx0.01

# i/o
infile=./raw_data/all_bathy.txt
outfile=./images/bouldnor_cliff.ps	

# all the prettifying type stuff
gmtset ANNOT_FONT_SIZE 10
gmtset LABEL_FONT_SIZE 12
gmtset HEADER_FONT_SIZE 16
gmtset ANNOT_FONT_SIZE_SECONDARY 10

# number formatting thing
gmtset D_FORMAT %7.9lg

do_preprocessing() {
   echo -n "blockmeaning... " 
   blockmean $area $infile -I0.75 > ./raw_data/bmd_all_bathy.txt
   echo "done!"
}

do_surfacing() {   
   echo -n "surfacing... "
   surface $area -I0.75 -T0.25 ./raw_data/bmd_all_bathy.txt -Ghwtma_surface.grd
   echo "done!"
}

do_gradient() {
   # illumination
   echo -n "gradient... "
   grdgradient hwtma_surface.grd -A250 -N3 -Ghwtma_grad.grd
   echo -n "mask... "
   # make a mask and remove the interpolated areas
   grdmask $area ./raw_data/bmd_all_bathy.txt \
      -Ghwtma_mask.grd -I0.75 -N/NaN/1/1 -S1
   grdmath hwtma_surface.grd hwtma_mask.grd MUL = hwtma_bathy.grd
   echo "done!"
}

do_imaging() {
   # basemap
   echo -n "imaging... "
   psbasemap $area $proj \
      -Ba200f100:"Eastings":/a200f100:"Northings"::."Bouldnor Cliff Swath Bathymetry":WeSn \
      -Xc -Yc -K > $outfile
   # colour palette
   makecpt -Cwysiwyg -T-1/15/0.5 -I -Z > .hwtma.cpt
   # colour scale
   psscale -D22/5.5/5/0.5 -B2 -I-1/1 -C.hwtma.cpt -O -K >> $outfile
   # the image
   grdimage $area $proj -Bg200 -Ihwtma_grad.grd -C.hwtma.cpt \
      hwtma_bathy.grd -O >> $outfile
   echo "done!"
}

do_plot() {
   echo -n "conversion... "
   ps2pdf -sPAPERSIZE=a4 $outfile
   gs -sDEVICE=jpeg -r200 -sPAPERSIZE=a4 -dBATCH -dNOPAUSE \
      -sOutputFile=bouldnor_cliff.jpg $outfile > /dev/null
   echo "done!"
}

# set all script variables
RETVAL=0
preprocessing="false"
gradient="false"
imaging="false"
plot="false"

##----------------------------------------------------------------------------##

# start the case-ing
while test -n "$1"; do
   case "$1" in
      "") 
         echo "Usage: $0 [-p|--preprocess] [-s|--surface] [-g|--gradient] [-i|--image] [-l|--plot]"
         RETVAL=1
         ;;
      --preprocess|-p)
         preprocessing="true"
         shift
         ;;
      --surface|-s)
         surfacing="true"
         shift
         ;;
      --gradient|-g)
         gradient="true"
         shift
         ;;
      --image|-i)
         imaging="true"
         shift
         ;;
      --plot|-l)
         plot="true"
         shift
         ;;
   esac
done

# pick which one to do based on the result of the case statement
if [ "$preprocessing" == "true" ]; then
   do_preprocessing
fi

if [ "$surfacing" == "true" ]; then
   do_surfacing
fi

if [ "$gradient" == "true" ]; then
   do_gradient
fi

if [ "$imaging" == "true" ]; then
   do_imaging
fi

if [ "$plot" == "true" ]; then
   do_plot
fi

exit $RETVAL
 
Old 05-31-2007, 02:34 PM   #11
marozsas
Senior Member
 
Registered: Dec 2005
Location: Campinas/SP - Brazil
Distribution: SuSE, RHEL, Fedora, Ubuntu
Posts: 1,393
Blog Entries: 1

Rep: Reputation: 63
test how many arguments are in the command line and print the usage if there is less than a minimum. In this case I think you want at least one argument, right ?

Code:
usage() {
  echo "Usage: $0 [-p|--preprocessing] [-s|--surface]...."
  exit 1
}

if [ $# -lt 1 ]; then
    echo "There are no enought arguments in command line." > /dev/stderr
    usage
fi

...your program follows here
# script to roughly plot the bathy from boldner cliff

##----------------------------------------------------------------------------##

# all the gumpf
inside the case statement, remove the option "". It does not make sense anymore.

Instead, add at end of case options:
Code:
 *)
            echo "Unknown argument: $1"
            usage
            exit 1
            ;;
esac

Last edited by marozsas; 05-31-2007 at 02:38 PM.
 
Old 05-31-2007, 04:10 PM   #12
makyo
Member
 
Registered: Aug 2006
Location: Saint Paul, MN, USA
Distribution: {Free,Open}BSD, CentOS, Debian, Fedora, Solaris, SuSE
Posts: 718

Rep: Reputation: 72
Hi, pwc101.

Good development from a rough script to a very nice tool.

See also http://www.tldp.org/LDP/abs/html/index.html for additional information.

Shell scripting is your friend ... cheers, makyo
 
Old 06-01-2007, 04:25 AM   #13
pwc101
Senior Member
 
Registered: Oct 2005
Location: UK
Distribution: Slackware
Posts: 1,846

Original Poster
Rep: Reputation: 128Reputation: 128
Thanks to everyone for helping me out

I've got this script which I shall use as a template for my future scripts. It'll save me an enormous amount of commenting large sections out, as I used to do!
 
Old 06-01-2007, 06:50 AM   #14
marozsas
Senior Member
 
Registered: Dec 2005
Location: Campinas/SP - Brazil
Distribution: SuSE, RHEL, Fedora, Ubuntu
Posts: 1,393
Blog Entries: 1

Rep: Reputation: 63
Just a last comment to make it perfect.

A general unix command should works without any options. Options are not mandatory.
In your program, the user must choose an option, but it shouldn't.

So, to make it more like a regular unix command, set a default option. If the user does not specify one, the default option wins.

let say, this default option is "preprocess":

Code:
usage() {
  echo "Usage: $0 [-p|--preprocessing] [-s|--surface]...."
  echo "in absence of any options, the default option is preprocessing."
  exit 1
}

preprocessing="false"
gradient="false"
imaging="false"
plot="false"

if [ $# -lt 1 ]; then
   preprocessing="true"
fi

...your program follows here
# script to roughly plot the bathy from boldner cliff

##----------------------------------------------------------------------------##

# all the gumpf
now your program looks like a real unix program !
 
Old 06-01-2007, 08:07 AM   #15
pwc101
Senior Member
 
Registered: Oct 2005
Location: UK
Distribution: Slackware
Posts: 1,846

Original Poster
Rep: Reputation: 128Reputation: 128
Thanks

I've implemented a default, as you've suggested, which just runs all the commands. That way something useful will come out at the end, even if it does take an age.

We (my colleague has now taken an interest in what I've written!) have begun to expand this so that the script will do this process for almost all types of input data we work with, so it's automating some of the variables (such as $area and $proj) which I had originally manually set.

I may try and get it so that I can read in a file from the command line (so that the variables $infile and $outfile can be specified). The ultimate aim is to be able to run the script as though it were a program without editing the actual script itself at all, whilst still maintaining some of the flexibility. Obvioiusly there is a trade-off to be made, but I'm sure we can get it so that the trade-off isn't too great.

It's getting to be a bit of a monster now, but it should save time in future (at least that's what I'll tell my supervisor!... )

Once more, thanks for all your help marozsas and makyo.
 
  


Reply


Thread Tools Search this Thread
Search this Thread:

Advanced Search

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is Off
HTML code is Off
Trackbacks are Off
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
help with command line options jaepi Linux - Newbie 7 04-29-2007 07:26 PM
Pattern matching with a "case" statement in BASH King V Programming 8 04-27-2006 04:19 PM
Command Line Options AUSanders79 Linux - Software 8 03-10-2005 10:24 AM
kmail command line options randyasu Linux - Software 10 09-30-2003 03:02 AM
Command Line Options in C++ crichards Programming 5 03-08-2003 03:42 PM


All times are GMT -5. The time now is 08:01 PM.

Main Menu
My LQ
Write for LQ
LinuxQuestions.org is looking for people interested in writing Editorials, Articles, Reviews, and more. If you'd like to contribute content, let us know.
Main Menu
Syndicate
RSS1  Latest Threads
RSS1  LQ News
Twitter: @linuxquestions
identi.ca: @linuxquestions
Facebook: linuxquestions Google+: linuxquestions
Open Source Consulting | Domain Registration