5

Given a program that requires some parameters, e.g. program -in file.in -out file.out, what would be the simple-most approach to write a bash script that could be called with or without any of these parameters and use default values for each?

script -in otherfile would run program -in otherfile -out file.out,
script -out otherout -furtherswitch would run program -in file.in -out otherout -furtherswitch etc.

4 Answers 4

7

A default value is easy to define in Bash:

foo="${bar-default}" # Sets foo to the value of $bar if defined, "default" otherwise
foo="${bar:-default}" # Sets foo to the value of $bar if defined or empty, "default" otherwise

To process your parameters, you can use a simple loop:

while true
do
    case "${1-}" in
        -in)
            infile="${2-}"
            shift 2
            ;;
        -out)
            outfile="${2-}"
            shift 2
            ;;
        *)
            break
            ;;
    esac
done

program -in "${infile-otherfile}" -out "${outfile-otherout}" "$@"

Useful reads:

I also recommend using getopt instead, because it is able to handle many special cases which would very quickly complicate and clutter your code (Non-trivial example).

2
  • Thanks! A while $# -gt 0 (or similar) and *) others="${others-} $1"; shift 1 ;; would survive other parameters before the default-able ones. But maybe I should learn getopt instead... Feb 7, 2013 at 11:12
  • Of course I could also simply write a python script... Feb 7, 2013 at 11:24
3

l0b0's answer shows how to set a default value through assignment and checking another variable's state (although, of course, you can also do this operating on the same variable you are assigning to), but there is a more concise way to do the same thing:

: "${foo=bar}" # $foo = bar if $foo is unset
: "${foo:=bar}" # $foo = bar if $foo is unset or empty
1
  • Pass all parameters ($*) passed to script to program too
  • Check each parameter you interested, if it's already in parameters passed, then ignore it. otherwise use default parameter value

Sample code

interested_parameter_names=(-in -out)
default_parameter_values=(file.in file.out)

program=echo
cmd="$program $*"

for ((index=0; index<${#interested_parameter_names[*]}; index++))
do
    param="${interested_parameter_names[$index]}"
    default_value="${default_parameter_values[$index]}"
    if [ "${*#*$param}" == "$*" ]   # if $* not contains $param
    then
        cmd="$cmd $param $default_value"
    fi
done

echo "command line will be:"
echo "$cmd"

echo
echo "execute result:"
$cmd

You can easily add more default parameters/values by add more array element in $interested_parameter_names and $default_parameter_values

Sample output

$ ./wrapper.sh -in non-default.txt -other-params
command line will be:
echo -in non-default.txt -other-params -out file.out

execute result:
-in non-default.txt -other-params -out file.out

Note

When passing parameters which contains space, it should be escaped by \, not just quoted them. Example:

./script -in new\ document.txt
3
  • Neat! I think the quotes issue can be solved by using $@ instead of $*. What does ${*#*$param} do? Feb 7, 2013 at 11:23
  • @TobiasKienzler, "${*#*$param}" == "$*" is used to check if $* NOT contains $param, it's Remove Smallest Prefix Pattern parameter expansion. Feb 7, 2013 at 11:28
  • 1
    ./script -in new\ document.txt would not work, and there's not way to make it work with that flawed approach. By concatenating the arguments into a string, you're losing the information about how those arguments are separated. Feb 7, 2013 at 13:02
1

You have two ways, as usual, simple and hard. Simple is to use inner variables, for example

program -in otherfile -out file.out

Here variable are

$0 = The script name
$1 = -in
$2 = otherfile, etc.

The Hard way is to use getopt, more information you can find here.

1
  • Using $1 etc will fail if I omit parameters, and manually parsing them would be annoying. Is there no midway between that and getopt? Feb 7, 2013 at 10:23

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .