Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

I am writing a script which can choose a file and print specific content. For example,

san#./script.sh

Expected Usage : ./script.sh --file1 --dns

(Here it checks for file1, search for dns name and prints. Basically there are sub-parameters under a parameter)

I tried for single parameter/Option as below :

options=$@

arguments=($options)

index=0;
for argument in $options
do
    index=`expr $index + 1`;
    case $argument in
    -a | --fun1 ) run_function1 ;;
    -b | --fun2 ) run_function2 ;;
    -c | --fun3 ) run_function3 ;;
    esac
done
exit;

[ ${1} ] || helpinfo

Can any one suggest for double parameter(sub options) ?

Expected target options :

./script.sh


OPTIONS : ./script.sh -h

./script --fun1 stackoverflow
        microsoft
        Google
     --fun2 Yahoo 

Basically each function will look into one file. I have looked into getopt or getopts, But it doesn't have long option (--long is not possible, instead we can use only -l). But again not sure of sub parameters. Can any one help on this ? I don't want to use getopt or getopts.

share|improve this question
    
Good example in this SO Q&A w/o using getopt or getopts: stackoverflow.com/questions/402377/… –  slm May 4 '13 at 8:10

2 Answers 2

up vote 3 down vote accepted

This is a version that is more convenient to use than the first one I gave here, in particular it avoids duplicated code for equivalent long and short options. It should handle anything you ever want for options: short options (-q), long options (--quiet), options with arguments, accumulated short options (-qlfinput instead of -q -l -f input), uniquely abbreviated long options (--qui instead of --quiet), end of options by --.

Most of the code is fixed; you only have to modify the marked parts.

#!/bin/bash

# Update USAGE (USAGE1, USAGE2, USAGE3 may remain unchanged):
USAGE='Usage: prog [-q|--quiet] [-l|--list] [-f file|--file file] [-Q arg|--query arg] args'
USAGE1='
Ambiguously abbreviated long option:'
USAGE2='
No such option:'
USAGE3='
Missing argument for'

# List all long options here (including leading --):
LONGOPTS=(--quiet --list --file --query)

# List all short options that take an option argument here
# (without separator, without leading -):
SHORTARGOPTS=fQ

while [[ $# -ne 0 ]] ; do
  # This part remains unchanged
  case $1 in
  --) shift ; break ;;  ### no more options
  -)  break ;;          ### no more options
  -*) ARG=$1 ; shift ;;
  *)  break ;;          ### no more options
  esac

  # This part remains unchanged
  case $ARG in
  --*)
    FOUND=0
    for I in "${LONGOPTS[@]}" ; do
      case $I in
      "$ARG")  FOUND=1 ; OPT=$I ; break ;;
      "$ARG"*) (( FOUND++ )) ; OPT=$I ;;
      esac
    done
    case $FOUND in
    0) echo "$USAGE$USAGE2 $ARG" 1>&2 ; exit 1 ;;
    1) ;;
    *) echo "$USAGE$USAGE1 $ARG" 1>&2 ; exit 1 ;;
    esac ;;
  -["$SHORTARGOPTS"]?*)
    OPT=${ARG:0:2}
    set dummy "${ARG:2}" "$@"
    shift ;;
  -?-*)
    echo "$USAGE" 1>&2 ; exit 1 ;;
  -??*)
    OPT=${ARG:0:2}
    set dummy -"${ARG:2}" "$@"
    shift ;;
  -?)
    OPT=$ARG ;;
  *)
    echo "OOPS, this can't happen" 1>&2 ; exit 1 ;;
  esac

  # Give both short and long form here.
  # Note: If the option takes an option argument, it it found in $1.
  # Copy the argument somewhere and shift afterwards!
  case $OPT in
  -q|--quiet) QUIETMODE=yes ;;
  -l|--list)  LISTMODE=yes ;;
  -f|--file)  [[ $# -eq 0 ]] && { echo "$USAGE$USAGE3 $OPT" 1>&2 ; exit 1 ; }
              FILE=$1 ; shift ;;
  -Q|--query) [[ $# -eq 0 ]] && { echo "$USAGE$USAGE3 $OPT" 1>&2 ; exit 1 ; }
              QUERYARG=$1 ; shift ;;
  *)          echo "$USAGE$USAGE2 $OPT" 1>&2 ; exit 1 ;;
  esac
done

# Remaining arguments are now in "$@":

echo "QUIETMODE = $QUIETMODE"
echo "LISTMODE = $LISTMODE"
echo "FILE = $FILE"
echo "QUERYARG = $QUERYARG"
echo "REMAINING ARGUMENTS:" "$@"
share|improve this answer
    
Hi @Uwe, I have tried this code. Though logic looks correct, I'm not able to see any output. Can you share sample output for the above code ? –  San May 6 '13 at 8:48
    
prog --que 123 -l 456 yields QUIETMODE = \n LISTMODE = yes\n FILE = \n QUERYARG = 123\n REMAINING ARGUMENTS: 456 (identical output for bash 3.2.39 and 4.1.5) –  Uwe May 6 '13 at 8:59
    
Thanks a ton! It works now. This script looks like super advanced featured. Thanks once again @Uwe :-) –  San May 6 '13 at 9:30

QUOTE. Always quote. When in doubt; quote. When not in doubt; quote.

E.g.: your test on $1, (which never is reached because of exit), would crash your script if called with e.g. ./myscript "file name with spaces".


When it comes to argument looping you could perhaps start out with something like this:

#!/bin/bash

prnt_help()
{
    printf "Usage: %s [OPTION]\n" $(basename "$1")
}

# A simple function only to visualize what gets passed and one of many
# ways to deal with it.
do_list()
{
    printf "do_list:\n"
    printf "OPT: %s\n" "$@"
    printf "My list:\n"
    while [[ -n "$1" && "${1:0:1}" != "-" ]]; do
        printf "Processing %s\n" "$1"
        shift
    done
}

main()
{
    while [[ -n "$1" ]];do
           # This test is not needed, but include it if you find use for it.
        if [[ "${1:0:1}" != "-" ]]; then
            shift
            continue
        fi
        # Check option and pass rest of arguments to matching function
        printf "Checking option %s\n" "$1"
        case "$1" in
        "-l"|"--list") shift; do_list "$@";;
        "-h"|"--help") prnt_help "$0";;
        "--") printf "Rest is not options even if starts with -\n"
            break;;
        esac
        shift
    done
    # If you use "--" to separate out e.g. filenames starting with -
    # then process them here.
}

main "$@"
share|improve this answer
    
And in addition to "QUOTE": Assigning an array $@ to a simple variable options and then looping over $options is also a bad idea, since you are no longer able to distinguish between a space in an argument and a space between arguments. Use for argument in "$@" ; do instead (and again: note the quotes!) –  Uwe May 4 '13 at 8:44
    
Thanks @sukminder. This helps a lot :-) –  San May 6 '13 at 8:53

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.