Search code examples
bashparameter-passing

Bash - what is the point of restoring positional arguments with `set`?


Considering the following code to parse bash script arguments (let's say example.sh):

#!/bin/bash
positional=()
while [ $# -gt 0 ]
do
    arg="$1"
    case ${arg} in
        -h|--help|\?)       show_usage;         exit 0      ;;
        -v|--verbose)       verbose=1;          shift       ;;
        -f|--file)          theFile="$2";       shift; shift;;
        *)                  positional+=("$1"); shift       ;;
    esac
done
set -- "${positional[@]}"

arg1a="${positional[0]}"; arg1b="$1" # same result, $arg1a == $arg1b
arg2a="${positional[1]}"; arg2b="$2" # same result, $arg2a == $arg2b

With the following call to the script:

./example.sh pos1 -v -f toto.txt pos2

  • Before the loop, "$1" == "pos1" and "$5" == "pos2"
  • After the loop, before the set --, $1, $2 and $5 are undefined
  • After the set --, "$1" == "pos1"and "$2" == "pos2"

But also, after the set --, ${positional[0]} == "pos1"and ${positional[1]} == "pos2".

So, the question is : what is the actual point of using set -- "${positional[@]}" ?

We do can get the values of our positional arguments (with ${positional[i]}) in the same order that it was provided, without restoring it to $1 and $2. Therefore, I don't understand the point of using set --. If we don't use it here, then $# -eq 0 and that's it...

Please, provide a real life example where the use of set -- is mandatory, even with this ${positional[]} array.


Solution

  • After the 'while' loop, the 'positional' array will capture all the command line options and arguments, other than the '-v', '--verbose', '-f' and '--file' (and the parameters to the -f/--file).

    At that point, the rest of the script will continue to run WITHOUT those parameters.

    Possible reason for this coding is that the above block extend the original behavior of the script to include two additional options (verbose, and file), which are not supported by other parts of the script.

    This can be useful, if the script will than execute another command (e.g., ls), which does not support the verbose/file options, with user provided command line arguments.

    # Code to strip verbose/file command line options
    while 
    do
        ...
    done
    set --
    # Do something above $verbose, $theFile
    ...
    # Original script here - use any unprocessed parameters from command line
    ls "$@"
    

    EDIT: from comments:

    With modern bash, you can use array instead of positional arguments for almost any operation. Few exceptions: the built-in: shift, getopt, work only on positional arguments