3

I want to know what principles I should use in order to write a bash script that takes its parameters in any given order. For example:

  • Let's name my script: script.sh
  • And let's say that I want it to take either no or at least two parameters.
  • Now suppose that one standard parameter is the -f which specifies that the very next parameter is the name of the file I should process.
  • Once more suppose that the given file is named: input.dat
  • And finally (for the sake of the example), suppose that the last two parameters I can add are named: -print and -delete

What I am asking here is:

Is there a specific way (or even programming technique) I can use so that the parameters can be passed in any given order (besides the fact that the filename should always follow the -f parameter?

Here are some invoking examples:

  1. ./script.sh -f input.dat -print
  2. ./script.sh -print -f input.dat

The above two executions should produce the very same example!

When answering please do keep in mind that the real problem has many more parameters and different outcomes!

2 Answers2

4

Here's the script I wrote to achieve this:

#!/bin/bash

# The code below was written to replace the -print and -delete options for
# -p and -d respectively, because getopts can't handle long args, I suggest
# you only use arguments of single letters to make your code more simple, but if 
# you can't avoid it then this is a workaround

for ARGS in "$@"; do
shift
        case "$ARGS" in
                "-print") set -- "$@" "-p" ;;
                "-delete") set -- "$@" "-d" ;;
                *) set -- "$@" "$ARGS"
        esac
done
# getopts works like this: you put all your arguments between single quotes
# if you put a ':' character after the argument letter (just like I did with
# the 'f' letter in the example below), it means this argument NEEDS an extra
# parameter. If you just use letters without the ':' it means it doesn't need
# anything but the argument itself (it's what I did for the '-p' and '-d' options)

while getopts 'f:pd' flag; do
        case "${flag}" in
                f) FILE=${OPTARG} ;;
                p) COMMAND="print" ;;
                d) COMMAND="delete" ;;
        esac
done

echo "$COMMAND $FILE"

And below examples of it running:

$ ./script.sh -f filename -print
print filename
$ ./script.sh -print -f filename
print filename
$ ./script.sh -f filename -delete
delete filename
$ ./script.sh -delete -f filename
delete filename
AFAbyss
  • 195
  • 2
  • 10
  • Workaround for the long args problem found here: http://stackoverflow.com/a/30026641/6931233 – AFAbyss Oct 07 '16 at 11:35
  • Only one more question: if we knew that the second parameter after the -f is a parameter (like the filename) that we need to read as well and we knew that it always came as the second parameter after the -f (which means: -f filename extraParameter), then how would I read that one using getopts as well?? – Christos Maris Oct 12 '16 at 12:35
  • I'm not sure I understood your question, but I think what you mean is that you want to store 2 parameters inside of the variable "FILE" as exampled above, right? If so, you can simply use -f "filename extraParameter". Put your whole parameter between double quotes. – AFAbyss Oct 13 '16 at 23:53
1

The previous solution is a good starting point, I played around with it for being able to accept multiple arguments to a flag, and I think this is a bit better style (less unused variables too):

#!/bin/bash
while [ $# -gt 0 ]; do
    case "$1" in
        --file|-f) filename=$2 ; shift;;
        --print|-p) printCommand ;;
        --delete|-d) deleteCommand ;;
        *) break
    esac
shift
done

Also note shift can take number of moves as an argument, this helps to keep it tidy. eg:

--two-files) filename1=$2; filename2=$3; shift 2;;
FuzzyJulz
  • 2,714
  • 1
  • 16
  • 18