0

In my bash script, I parse arguments. I want to try to allow only one argument to be without specific pattern, which would be the "path". Any other argument that doesn't fit the cases should cause the script to exit. Here is an example:

#!/bin/bash
while [ "$#" -gt 0 ]; do
    case "$1" in
        -v|--verbose) verbose=1 ;;
        -h|--help)
            echo "Usage: $(basename "$0") [OPTIONS] [PATH]"
            echo "Does something interesting with the given path."
            echo ""
            echo "OPTIONS"
            echo "  -v,   --verbose                 Prints verbose information."
            echo "  -h,   --help                    Prints this help message."
            exit 0
            ;;
        *) { test -z $path && path=$1 } || { echo "Invalid command line flag $1" >&2 && exit 1 } ;;
    esac
    shift
done

However the line { test -z $path && path=$1 } || { echo "Invalid command line flag $1" >&2 && exit 1 } ;; fails with:

myscript: line 14 syntax error near unexpected token `;;'
myscript: line 14: `           *) { test -z $path && path=$1 } || { echo "Invalid command line flag $1" >&2 && exit 1 } ;;'

I'm aware that I can simply build an if-else statement, but I wonder why I cannot group it inline. If I remove the curly braces exit 1 will always be reached.

3
  • 1
    You are perhaps missing some semicolons to terminate the lists inside your compound commands - see for example Separator between command list and } Commented Aug 10, 2022 at 20:15
  • Thank you that's it. I'm dealing with brace expansions of bash that are simply lists. I need to terminate those commands within my lists to execute them. You could add it as answer so I can mark it properly. Maybe others will benefit from my question and your answer in the future. Commented Aug 10, 2022 at 20:33
  • OK thanks - done Commented Aug 10, 2022 at 20:48

2 Answers 2

1

The command lists inside a single-line command group must themselves be terminated with semicolons, as { list; } not { list }

So for example whereas

$ bash -c 'case $1 in *) { echo foo && echo bar } ;; esac' bash baz
bash: -c: line 0: syntax error near unexpected token `;;'
bash: -c: line 0: `case $1 in *) { echo foo && echo bar } ;; esac'

fails,

$ bash -c 'case $1 in *) { echo foo && echo bar; } ;; esac' bash baz
foo
bar

succeeds.

See Separator between command list and } and also the bash manual's section on Command Grouping.

1

The previous answer and comments deal with the main source of your error.

I'm posting a suggestion to move your multi-line usage message into another part of the script, like a subroutine, and have your -h|-help) clause simply call the subroutine:

USAGE () {
  echo "This is the first line of my usage message"
  echo "This is another line"
  echo "Etc."
}

(farther down the script)

case "$1" in
    -v|--verbose) verbose=1 ;;
       -h|--help) USAGE ; exit 0 ;;
               *) # your code here ;;
esac

Or you can use a here document to assemble the multi-line usage message into a string variable and the case statement clause displays the variable and exits.

# fill $USAGE with a here document that isn't indented
USAGE=$(cat - <<EOUSAGE
Usage: $(basename "$0") [OPTIONS] [PATH]
Does something interesting with the given path.

OPTIONS
  -v,   --verbose                 Prints verbose information.
  -h,   --help                    Prints this help message.
EOUSAGE
)  # USAGE=$( ... ) ends here

(farther down the script)

case "$1" in
  -v|--verbose) verbose=1 ;;
     -h|--help) echo "$USAGE" ; exit 0 ;;
             *) # your code here ;;
esac

I tend to use the here document approach because writing a multi-line message is far less messy than multiple echo commands, and you don't have trouble with empty lines or quotes inside your text. Also since it's not indented, you can see how far across the terminal window each line stretches and can wrap your longer lines for better legibility.

0

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .