How to group shorthand operators in shell script?

281 Views Asked by At

This shell script takes one -P optional option.

#!/bin/sh

usage() {
        echo "Usage: $0 [-P path] URL [URL...]";
        exit 0;
}

P='/default_path/'

getopts "P:" OPT &&  [ "$OPT" == 'P' ] && P=$OPTARG || usage

If there are options, do the followings.

If the option is 'P', then assign the $OPTARG path to the variable P.

If the option is not 'P', print the usage and exit.

The statement above fails the definition when there is no option. When no option is given, getopts "P:" OPT returns false, then usage is called. By idea, I should group the later parts of the expression, as below. But it gives me syntax error.

getopts "P:" OPT &&  { [ "$OPT" == 'P' ] && P=$OPTARG || usage }

How to group shorthand operators in shell script?

1

There are 1 best solutions below

0
midnite On

Short answer: Add a semi-colon after usage.

getopts "P:" OPT &&  { [ "$OPT" == 'P' ] && P=$OPTARG || usage; }

Side note: Actually I opted to use getopt instead of getopts. It is much more versatile and definitely worth learning it.

Side note 2: There are hundreds of threads in SE talking about shell scripts, some with thousands of upvotes. But knowledge is so scattered, while some may focus on usage and lack authentic explanations. Be reminded that man bash is a very cool resource to begin with (which I did not know I can actually man bash as I thought bash was not a command).

Explanation, with reference to [`man bash`][2]:

Simple Command:

getopts "P:" OPT

A command is also a Pipeline:

A pipeline is a sequence of one or more commands ...

The first && forms a List:

getopts "P:" OPT && # ... another pipeline

A list is a sequence of one or more pipelines separated by one of the operators ;, &, &&, or ||, and optionally terminated by one of ;, &, or .

Although the first && behaves like a logical AND, I try to not see it in that way.

Let us break to the second part. This is a test expression, which is a Compound Command:

[ "$OPT" == 'P' ]

[[ expression ]]

When the == and != operators are used, the string to the right of the operator is considered a pattern and matched according to the rules described below under Pattern Matching, as if the extglob shell option were enabled. The = operator is equivalent to ==.

Again, these are three pipelines joining by && and ||, which explained above:

[ "$OPT" == 'P' ] && P=$OPTARG || usage

If we need to group these pipelines, we need to use the following format. It must be terminated with a newline or semicolon.

{ list; }

list is simply executed in the current shell environment. list must be terminated with a newline or semicolon. This is known as a group command. The return status is the exit status of list. Note that unlike the metacharacters ( and ), { and } are reserved words and must occur where a reserved word is permitted to be recognized. Since they do not cause a word break, they must be separated from list by whitespace or another shell metacharacter.

Finally joining the first Simple Command && Compound Commands:

getopts "P:" OPT &&  { [ "$OPT" == 'P' ] && P=$OPTARG || usage; }

Cannot think of this header name ...

Using round brackets is syntactically correct. But it cannot set the the variable P. It is because the assignment will be running in a subshell.

getopts "P:" OPT &&  ( [ "$OPT" == 'P' ] && P=$OPTARG || usage )

(list)

list is executed in a subshell environment (see COMMAND EXECUTION ENVIRONMENT below). Variable assignments and builtin commands that affect the shell's environment do not remain in effect after the command completes. The return status is the exit status of list.