3

The code below is an mwe based on a script mwe I've written where I specify flags in what I think is the usual way. But I'm seeing really wierd behavior. If I type mwe -e or mwe -n it thinks there are no arguments and returns no arg. If I type mwe -k or mwe -i it thinks argType is not "-" and returns breaking. If I comment out the four lines that end with a # the code works as expected. That suggests the problem is being caused by the while loop. Could somebody please explain what's happening?

#!/bin/bash
foo=0
argType=`echo "$1" | cut -c 1`
while [ 1 -gt 0 ] ;   #
    do   #
        if [ $# -eq 0 ] ; then
            echo no arg
            exit
        elif [ "$argType" != "-" ] ; then
            #No more flags
            echo breaking
            break  #
        elif [ "$1" = "-n"  ] ; then
            foo=1
            shift
        elif [ "$1" = "-e"  ] ; then
            foo=2
            shift
        elif [ "$1" = "-i"  ] ; then
            foo=3
            shift
        elif [ "$1" = "-k"  ] ; then
            foo=4
            shift
        fi
done  #
echo This is foo:  $foo
Leo Simon
  • 453

2 Answers2

6

Third line should be

argType=$(printf "%s" "$1" | cut -c 1)

As mentioned in comments, echo interprets arguments such as -e as an option, so that -e would not be passed to cut. Worse, as a special case, end-of-options flag -- is not available to echo. Then, you need printf, which is usually better anyway.

Since you are in bash, you could adopt @steeldriver's suggestion and use argType=${1:0:1} (which means: For parameter 1, start at char 0 and get 1 char) instead of the pipeline. Notice that is not available in the POSIX shell, however.

Also prefer $() instead of backticks, since the latter harm readability, especially when nesting.

Finally, notice that you are shifting, so, even after that correction, if you try ./myscript -e -i, eventually [ $# -eq 0 ] will be true and the execution will be terminated by exit. Maybe that is intended, maybe not, but at the end of the day the last echo would not be triggered.

Quasímodo
  • 18,865
  • 4
  • 36
  • 73
  • 1
    Also, the assignment to argType is before the loop, so it doesn't get updated as options are processed; it needs to be moved inside the loop to work right. Or, since this is bash, you could just use elif [[ "$1" != "-"* ]]; ... (note that this type of pattern matching requires a double-bracket conditional expression instead of just a single-bracket test). – Gordon Davisson May 31 '20 at 22:33
  • 1
    If they're using a POSIX shell, they could use getopts... Also, there's the hilarious "${1%"${1#?}"}" that works in stead of ${1:0:1} – ilkkachu May 31 '20 at 22:33
2

From your question it is not clear what you want !

Anyhow it seems that you want the number corresponding to the last argument

#!/bin/bash
foo=0;

while [[ $# -gt 0 ]]; do case "${1}" in '-n') foo=1; shift ;; '-e') foo=2; shift ;; '-i') foo=3; shift ;; '-k') foo=4; shift ;; *) echo "Invalid flag"; exit 1; ;; esac done

echo "This is foo: $foo"

If instead you want a mechanism that treats and validates arguments before being processed, you can use something like

#!/bin/bash

inputf=''; outputf=''; text=''; format='';

while [[ $# -gt 0 ]];do case "${1}" in '-i') inputf="${2}"; shift 2 ;; '-o') outputf="${2}"; shift 2 ;; '-t') text="${2}"; shift 2 ;; '-f') format="${2}"; shift 2 ;; esac done

AdminBee
  • 22,803
Yunus
  • 1,684
  • first suggestion is great, thanks. would you mind adding an extra case for me, which would deal with unintended flags, e.g., if I typed -G it would return "flag not recognized" and exit – Leo Simon Jun 16 '20 at 04:21
  • for other cases we use glob star * that will catche anything other than valid flags – Yunus Jun 16 '20 at 11:48