0

I have script with several parameters that have required argument:

while [ "$1" != "" ]; do
    case $1 in
        -f | --first )          shift
                                first=$1
                                ;;
        -s | --second )         shift
                                second=$1
                                ;;
        * )                     break
    esac
    shift
done

echo "first: "$first" second: "$second

This works properly:

$ ./script.sh --first a --second b
first: a second: b

But when someone forget about argument of parameter then getting parameters works incorrectly.

For example this not works:

$ ./script.sh --first --second a
first: --second second: 

and this was bad (first was get, but second mandatory not handled):

$ ./script.sh --first a --second
first: a second: 

I try modyify:

* )                     break

to:

* )                     echo "Bad arguments"
                        exit 1

In this case works (handling mandatory first):

$./script.sh --first --second b
Bad arguments

But in this case doesn't works properly (not handling second):

$ ./script.sh --first a --second
first: a second: 
  • If the number of required arguments is always the same you could test for number of parameters given with $#, e.g. if [ "$#" -ne 4 ]; then .... – eblock Aug 1 '19 at 15:05
1

Use the shell built-in getopts. This uses an optstring where you can list all of the valid option characters AND whether they require an argument or not (by following the option character with a :). For example:

while getopts 'f:s:' opt; do
  case "$opt" in 
    f) first="$OPTARG" ;;
    s) second="$OPTARG" ;;

    :) usage 1 "-$OPTARG requires an argument" ;;
    ?) usage 1 "Unknown option '$opt'" ;;
  esac
done

shift $((OPTIND -1))

Note that the built-in getopts does not support --long options. If that is a requirement, use the getopt program from the util-linux package - do NOT use any other version, the others all have serious flaws.

TEMP=$(getopt -o 'f:s:' --long 'first:,second:' -n "$0" -- "$@")
if [ $? != 0 ] ; then echo "Terminating..." >&2 ; exit 1 ; fi
eval set -- "$TEMP"

while true ; do
  case "$1" in
    -f|--first)    first="$2" ; shift 2 ;;
    -s|--second)   second="$2" ; shift 2 ;;
    --)            shift ; break ;; 
     *)            echo 'Internal error!' ; exit 1 ;;
  esac
done

NOTES:

  • getopts (with an s) is built-in to POSIX shells and is portable and standard.
  • getopt (without an s) is non-standard, with several conflicting versions, most of which have serious flaws. The version from the util-linux package is safe to use if you don't care about portability.
  • both of them will throw an error if an option that requires an argument is not given one.
|improve this answer|||||
0

That is happening because shift does shifting of $2 to $1 irrespective of what the text you have for cases. Exact purpose of your scripting is unclear but for instance if you want to avoid this, you can do something like:

while [[ "$1" != "" && "$2" != -* ]]; do
    case $1 in
        -f | --first )          shift
                                first=$1
                                ;;
        -s | --second )         shift
                                second=$1
                                ;;
        * )                     break
    esac
    shift
done

echo "first: "$first" second: "$second

This will avoid manipulation if argument is missing.

Example output:

$ ./script.sh --first a --second b
first: a second: b
$ ./script.sh --first --second b
first:  second: 
|improve this answer|||||

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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