Bash - getopts - bulk arguments (operands) in first place in the command line argument

I use the following lines (hope this is the best, if not correcting me, please) to handle the command line options:

#!/usr/bin/bash read -r -d '' HELP <<EOF OPTIONS: -c Enable color output -d Enable debug output -v Enable verbose output -n Only download files (mutually exclusive with -r) -r Only remove files (mutually exclusive with -n) -h Display this help EOF # DECLARE VARIABLES WITH DEFAULT VALUES color=0 debug=0 verbose=0 download=0 remove=0 OPTIND=1 # Reset in case getopts has been used previously in the shell invalid_options=(); # Array for invalid options while getopts ":cdvnrh" opt; do echo "Actual opt: $opt" case $opt in c) color=1 ;; d) debug=1 ;; v) verbose=1 ;; n) download=1 ;; r) remove=1 ;; h) echo "$HELP" exit 1 ;; \?) invalid_options+=($OPTARG) ;; *) invalid_options+=($OPTARG) ;; esac done # HANDLE INVALID OPTIONS if [ ${#invalid_options[@]} -ne 0 ]; then echo "Invalid option(s):" >&2 for i in "${invalid_options[@]}"; do echo $i >&2 done echo "" >&2 echo "$HELP" >&2 exit 1 fi # SET $1 TO FIRST MASS ARGUMENT, $2 TO SECOND MASS ARGUMENT ETC shift $((OPTIND - 1)) # HANDLE CORRECT NUMBER OF MASS OPTIONS if [ $# -ne 2 ]; then echo "Correct number of mass arguments are 2" echo "" >&2 echo "$HELP" >&2 exit 1 fi # HANDLE MUTUALLY EXCLUSIVE OPTIONS if [ $download -eq 1 ] && [ $remove -eq 1 ]; then echo "Options for download and remove are mutually exclusive" >&2 echo "$HELP" >&2 exit 1 fi echo "color: $color" echo "debug: $debug" echo "verbose: $verbose" echo "download: $download" echo "remove: $remove" echo "\$1: $1" echo "\$2: $2" 

If I call the script method that bulk arguments (those that are not switches or switch arguments) are the last arguments, the correct operation:

 $ ./getopts.sh -c -d -v -rab Actual opt: c Actual opt: d Actual opt: v Actual opt: r color: 1 debug: 1 verbose: 1 download: 0 remove: 1 $1: a $2: b 

The problem is when I want to call the script, so there will be mass arguments first (or somewhere in the middle of the switches that don't use the arguments)

 $ ./getopts.sh ab -c -d -v -r Correct number of mass arguments are 2 OPTIONS: -c Enable color output -d Enable debug output -v Enable verbose output -n Only download files (mutually exclusive with -r) -r Only remove files (mutually exclusive with -d) -h Display this help 

or

 $ ./getopts.sh -cab -d -v -r Actual opt: c Correct number of mass arguments are 2 OPTIONS: -c Enable color output -d Enable debug output -v Enable verbose output -n Only download files (mutually exclusive with -r) -r Only remove files (mutually exclusive with -d) -h Display this help 

I think it should be OK according to the standards (POSIX), because the following syntax, which is basically the same, works on my system as expected:

 $ cp test1/ test2/ -r $ cp test1/ -r test2/ 

I have an internet search, but the only thing that was close to my problem was this one related to C.

+6
source share
2 answers

getopts automatically breaks the while loop as soon as it detects a parameter without a dash (not including the argument given to the dash parameters that take arguments). The POSIX standard is to have dotted options first and then have files. There's also none of that -- and + shit too. It is simple and simple.

However, Linux is not compatible with Unix or POSIX. It's just that GNU utilities are "better" than standard Unix utilities. More features, extra features and handling things a little differently.

On Linux, command line options may appear after files in many GNU utilities.

For instance:

 $ cp -R foo bar 

Work on my certified Unix Mac OS X and Linux, however

 $ cp foo bar -R 

Only works with Linux.

If you want getopts work as many Linux utilities, you need to do a little work.

First you must process your arguments yourself and not be dependent on $OPTIND to $OPTIND them. You also need to verify that you have an argument.

I came up with this as an example of what you want.

 #! /bin/bash while [[ $* ]] do OPTIND=1 echo $1 if [[ $1 =~ ^- ]] then getopts :a:b:cd parameter case $parameter in a) echo "a" echo "the value is $OPTARG" shift ;; b) echo "b" echo "the value is $OPTARG" shift ;; c) echo "c" ;; d) echo "d" ;; *) echo "This is an invalid argument: $parameter" ;; esac else other_arguments="$other_arguments $1" fi shift done echo "$other_arguments" 

Now I loop until $* is set. (Maybe I should use $@ ?) I should do a shift at the end of the loop. I also reset $OPTIND to 1 every time, because I transfer the arguments myself. $OPTARG is still installed, but I have to do another shift to make sure everything works.

I also need to check if the argument starts with a dash or not using the regular expression in the if .

Basic testing shows that this works, but I cannot say that this is a mistake, but it gives you an idea of ​​how you need to process your program.

There are still many features that you get from getopts , but this requires a bit more work.

+3
source

Bash provides two methods for parsing arguments.

The built-in getopts command is a newer, easier to use argument parsing mechanism, but it is not very flexible. getopts does not allow mixing parameters and bulk arguments.

The external getopt command is an older and more complex mechanism for parsing arguments. It allows you to use long / short options, and the gnu extension allows you to mix parameters and bulk arguments.

+1
source

Source: https://habr.com/ru/post/982674/


All Articles