Cloud
Cloud

Reputation: 19331

Separating "flags" from "arguments" in bash script

I'm working with a legacy shell script that's used for testing certain features in binaries (i.e. calls nm, objdump, ldd, etc; and does some parsing on the results). The shell script currently is very "touchy", and large, so I'd like to minimize changes I make to it.

It currently starts with a check for the number of parameters, each one being a path, i.e.:

if [ "$#" -lt 3 ]
then
  echo "Error. Invalid number of arguments."
  exit
fi

I would like to add some additional arguments and their long-form equivalents to disable certain features internal to the script, without invalidating the above test, i.e.:

-h | --help: Print shell usage and expected arguments.
-x | --xfer: Do some extra copy logic.
-d | --dry:  Do a dry-run, and don't actually change any files.

However, I do not want the flags (i.e. arguments that begin with a hyphen) to be counted as arguments (i.e. doesn't affect the assignment of parameters to $1, $2, and $3). For example, the following is currently a valid invocation of my script:

bash ./test.sh ./ ./out ./logs

I would like the following possible invocations to also work:

bash ./test.sh ./ ./out ./logs --dry
bash ./test.sh --xfer ./ ./out ./logs
bash ./test.sh --help
bash ./test.sh ./ --xfer ./out --dry ./logs

How would I setup a script to "filter out" arguments that begin with hyphens (one or two), and maintain the same assignments to $1, $2,and $3?

Upvotes: 2

Views: 2210

Answers (1)

Charles Duffy
Charles Duffy

Reputation: 295815

You can modify the argument list after-the-fact:

set -- one two three

Thus, you can put your own parsing up at the top, and use set -- to put whatever arguments you want in place into their desired positions.


Consider as an example of filtering:

#!/usr/bin/env bash
#              ^^^^- not /bin/sh, as arrays are bash-only

help_and_exit() {
  local retval=${1:-1}
  cat <<EOF
${0##*/} [-d|--dry-run] [-x|--xfer] input output whatever

...put your extended help here...
EOF
  exit "$retval"
}

args=( )
dry_run=0; xfer=0
while (( $# )); do
  case $1 in
    -d|--dry)  dry_run=1 ;;
    -x|--xfer) xfer=1 ;;
    -h|--help) help_and_exit 0 ;;
    -*)        printf 'Unknown option: %q\n\n' "$1"
               help_and_exit 1 ;;
    *)         args+=( "$1" ) ;;
  esac
  shift
done
set -- "${args[@]}"

Upvotes: 3

Related Questions