2

Im trying to pass arguments to a script i wrote, but cant get it right.

What i want is one mandatory argument without a flag, and two optional arguments with flags, so it can be called like this:

./myscript mandatory_arg -b opt_arg -a opt_arg

or

./myscript mandatory_arg -a opt_arg
./myscript mandatory_arg -b opt_arg

I looked into getopts and got this:

while getopts b:a: option
do
    case "${option}"
    in
        b) MERGE_BRANCH=${OPTARG};;
        a) ACTION=${OPTARG};;
    esac
done

if "$1" = ""; then
    exit
fi

echo "$1"
echo "$MERGE_BRANCH"
echo "$ACTION"

But it does not work at all.

1 Answer 1

3

Assuming that your mandatory argument appears last, then you should try the following code: [comments inline]

OPTIND=1
while getopts "b:a:" option
do
    case "${option}"
    in
        b) MERGE_BRANCH=${OPTARG};;
        a) ACTION=${OPTARG};;
    esac
done

# reset positional arguments to include only those that have not
# been parsed by getopts

shift $((OPTIND-1))
[ "$1" = "--" ] && shift

# test: there is at least one more argument left

(( 1 <= ${#} )) || { echo "missing mandatory argument" 2>&1 ; exit 1; };

echo "$1"
echo "$MERGE_BRANCH"
echo "$ACTION"

The result:

~$ ./test.sh -b B -a A test
test
B
A
~$ ./tes.sh -b B -a A
missing mandatory argument

If you really want the mandatory argument to appear first, then you can do the following thing:

MANDATORY="${1}"
[[ "${MANDATORY}" =~ -.* ]] && { echo "missing or invalid mandatory argument" 2>&1; exit 1; };

shift # or, instead of using `shift`, you can set OPTIND=2 in the next line   
OPTIND=1
while getopts "b:a:" option
do
    case "${option}"
    in
        b) MERGE_BRANCH=${OPTARG};;
        a) ACTION=${OPTARG};;
    esac
done

# reset positional arguments to include only those that have not
# been parsed by getopts

shift $((OPTIND-1))
[ "$1" = "--" ] && shift

echo "$MANDATORY"
echo "$MERGE_BRANCH"
echo "$ACTION"

The result is the following:

~$ ./test.sh test -b B -a A
test
B
A
~$ ./tes.sh -b B -a A
missing or invalid mandatory argument
Sign up to request clarification or add additional context in comments.

5 Comments

When trying to run this it prints out the mandatory argument and then "" for the optionals, if i run without the mandatory it replies with "missing mandatory argument"
What is the reason for not recommending having the mandatory argument first? and i think something is missing from the last edit
@A.Jac it's just a matter of convention and personal taste
@A.Jac Assume that your mandatory argument is the name of a file, and that your file name is -test, with the former code you can write ./test..sh -b B -a A -- -test and have your program work correctly, whereas with the last code you won't be able to parse it: -- is used to make getopt stop looking for options, but A) -b B -a A won't be recognized as options B) -- will fail the test [[ ${MANDATORY} =~ -.* ]]
Anyway I removed my personal opinions from the answer, because clearly there are workarounds for the issues I see with this approach, it is only a matter of implementing them with some more extra code.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.