3
./script.sh -abc hello

How can I write my script to use '-abc' as the option and 'hello' as the value to that option?

I should be able to pass this value to all the functions in this script. Lets say I have 2 functions: X and Y.

Jahid
  • 21,542
  • 10
  • 90
  • 108
skyrocker
  • 199
  • 2
  • 8
  • 20
  • what are you asking for? there's no built-in support in bash for handilng long options - both `getopt` and `getopts` only do single-character options. You'll have to parse it yourself by looping over `"$@"`. – Mark Reed Apr 29 '15 at 22:35
  • there is no way to achieve the above in shell programming ? – skyrocker Apr 29 '15 at 22:39
  • @MarkReed `getopt(1)` does support long options but using it safely requires knowing that you have a util-linux version (or it doesn't support options with spaces). – Etan Reisner Apr 29 '15 at 22:47
  • 1
    How are you currently handling script arguments? Are you doing so? [Bash FAQ 35](http://mywiki.wooledge.org/BashFAQ/035) covers some good suggestions for how to handle script arguments. – Etan Reisner Apr 29 '15 at 22:47
  • See http://stackoverflow.com/questions/402377/using-getopts-in-bash-shell-script-to-get-long-and-short-command-line-options – drizzt Apr 30 '15 at 14:11

3 Answers3

2

Use this in your script:

[[ $1 == -abc ]] && value="$2" || echo invalid option

If you don't want to print any messages on wrong option or no option, then omit the || echo ... part, value will be empty.

If you want to make the second argument a must, then:

[[ $1 == -abc ]] && [[ $2 != "" ]] && value="$2" || echo invalid option

Using if else loop will give you complete control over this:

if [[ $1 == -abc ]]; then
#if first option is valid then do something here
if [[ $2 != "" ]]; then
 value="$2"
 else
 #if second option is not given then do something here
 echo invalid option
 fi
 else
 echo invalid option
 #if first option is invalid then do something here
 fi

If you want to make the first argument a must too, then change the first if statement line to

if [[ $1 == -abc && $1 != "" ]]; then

If you want to pass as many arguments as you wish and process them, then use something like this:

#!/bin/bash
opts=( "$@" )
#if no argument is passed this for loop will be skipped
for ((i=0;i<$#;i++));do
case "${opts[$i]}" in
   -abc)

    # "${opts[$((i+1))]}" is the immediately follwing option
    [[ "${opts[$((i+1))]}" != "" ]] &&
    value="${opts[$((i+1))]}"
    echo "$value"
    ((i++))
    #skips the nex adjacent argument as it is already taken

    ;;

   -h)
   #dummy help option
   echo "Options are [-abc value], -h"

    ;;

   *)
   #other unknown options
   echo invalid option
    break

    ;;
esac
done

This is an example of handling multiple arguments with only two options available -abc value and -h

Jahid
  • 21,542
  • 10
  • 90
  • 108
  • 1
    This is helpful but lacks enough context to be a really full answer I think. Explaining how this needs to fit in to a larger argument parsing context would be helpful. – Etan Reisner Apr 29 '15 at 22:48
  • Thanks for your answer , but basically I want to verify that to this '-abc' there should be a value passed always ... i guess it will be under if-else loop but not sure how do I do that – skyrocker Apr 29 '15 at 22:50
  • Gotta agree with @EtanReisner here. This code is checking for a very rigid set and positioning of parameters. Usually, when a command line is parsed, options (and their arguments) may appear in any order, as long as they appear before non-options. – ctt Apr 29 '15 at 23:47
  • OP hasn't specified about having multiple options. – Jahid Apr 29 '15 at 23:48
  • @EtanReisner, added codes for multiple options, let me know if you have any suggestions.. – Jahid Apr 30 '15 at 01:21
1

bash doesn't have a built in command for processing long arguments. In order to parse long options in a shell script, you'll need to iterate over the arguments list yourself.

Here's one approach:

#!/bin/sh

is_option_arg () {
        case $1 in
                -*)
                        return 1
                        ;;
                *)
                        return 0
                        ;;
        esac
}

usage () {
        echo "$(basename "$0") -abc ARG -def ARG -verbose"
}



OPT_ABC=
OPT_DEF=
OPT_VERBOSE=false

while [ "$#" -gt 0 ]; do
        case $1 in
                -abc)
                        shift
                        { [ "$#" -ne 0 ] && is_option_arg "$1"; } || { usage >&2; exit 1; }
                        OPT_ABC=$1
                        ;;
                -def)
                        shift
                        { [ "$#" -ne 0 ] && is_option_arg "$1"; } || { usage >&2; exit 1; }
                        OPT_DEF=$1
                        ;;
                -verbose)
                        OPT_VERBOSE=true
                        ;;
                *)
                        break
                        ;;
        esac

        shift
done

echo "OPT_ABC=$OPT_ABC"
echo "OPT_DEF=$OPT_DEF"
echo "OPT_VERBOSE=$OPT_VERBOSE"

if [ "$#" -gt 0 ]; then
        echo "Remaining args:"

        for arg in "$@"; do
                echo "$arg"
        done
fi
ctt
  • 1,405
  • 8
  • 18
1

You pretty much have to implement it yourself manually. Here's one way:

abc=
while [[ "$1" == -* ]]; do
  opt=$1
  shift
  case "$opt" in
   -abc) 
    if (( ! $# )); then
      echo >&2 "$0: option $opt requires an argument."
      exit 1
    fi
    abc="$1"
    shift
    ;; 
   *) 
    echo >&2 "$0: unrecognized option $opt."
    exit 2
    ;;
  esac
done
echo "abc is '$abc', remaining args: $*"

Some sample runs of the above:

(0)$ ./script.sh
abc is '', remaining args:
(0)$ ./script.sh hello
abc is '', remaining args: hello
(0)$ ./script.sh -abc hello
abc is 'hello', remaining args:
(0)$ ./script.sh -abc hello there
abc is 'hello', remaining args: there
(0)$ ./script.sh -abc
./script.sh: option -abc requires an argument.
(1)$ ./script.sh -bcd
./script.sh: unrecognized option -bcd.
(2)$
Mark Reed
  • 91,912
  • 16
  • 138
  • 175