2

I'm reading my command line parameters using getopt, and I'm reading a configuration file using .:

test.sh:

#!/bin/bash

set -- `getopt C:a:b:c: "$@"`

C="default.cfg"
. $C

while [ $# -gt 0 ]; do
    case "$1" in
    -a) cfg1="$2"; shift;;
    -b) cfg2="$2"; shift;;
    -c) cfg3="$2"; shift;;
    -C) C="$2"; #you'll see what this is for later
        shift;;
    --) shift;
        break;;
    -*) echo "invalid option";
        exit 1;;
    *) break;;
    esac
    shift
done

echo "cfg1 = $cfg1"
echo "cfg2 = $cfg2"
echo "cfg3 = $cfg3"

exit 0

default.cfg::

cfg1=hello
cfg2=there
cfg3=friend

This all works as expected:

$ ./test.sh
cfg1 = hello
cfg2 = there
cfg3 = friend
$ ./test.sh -b optional
cfg1 = hello
cfg2 = optional
cfg3 = friend

This issue is I want configurations to be prioritized in the following manner:

  1. options given on the command line
  2. options defined in the config file defined by the -C option
  3. options defined in the default config file

So if I have this:

test.cfg:

cfg1=custom_file_1
cfg2=custom_file_2

I want to get this:

$ ./test.sh -b command_line -C test.cfg
cfg1 = custom_file_1
cfg2 = command_line
cfg3 = friend

I just can't figure out how to load the default config file, then search the options for -C, then load the custom config file, overwriting the default, then search the command line parameters AGAIN and overwrite the configs again. I'm pretty new to shell scripting, so forgive me if I'm missing something obvious.

codeforester
  • 39,467
  • 16
  • 112
  • 140
ewok
  • 20,148
  • 51
  • 149
  • 254

3 Answers3

1

To overwrite variables, try to replace :

-C) C="$2";

with :

-C) . "$2";

And invoke it with :

./test.sh -C test.cfg -a command_line1 -b command_line2

Update :

For options in any order, you can try this :

C="default.cfg"
. $C

while getopts C:a:b:c: OPTION
  do
    case $OPTION in
      a) cfg1_override=$OPTARG;;
      b) cfg2_override=$OPTARG;;
      c) cfg3_override=$OPTARG ;;
      C) . $OPTARG;;
      -) break;;
      -*) echo "invalid option";
          exit 1;;
      *) break;;
    esac
  done
shift $(($OPTIND - 1))

cfg1="${cfg1_override-${cfg1}}"
cfg2="${cfg2_override-${cfg2}}"
cfg3="${cfg3_override-${cfg3}}"

echo "cfg1 = $cfg1"
echo "cfg2 = $cfg2"
echo "cfg3 = $cfg3"

exit 0

Based on Is it possible to specify the order getopts conditions are executed?

Community
  • 1
  • 1
SLePort
  • 15,211
  • 3
  • 34
  • 44
  • so what happens if I execute `./test.sh -a command_line1 -b command_line2 -C test.cfg`? – ewok Mar 15 '16 at 20:19
  • I updated my answer. You must add -̀C test.cfg`as first param. – SLePort Mar 15 '16 at 20:28
  • So this option requires -C to be the first option on the command line? I'm hoping for a solution that doesn't put that kind of restriction on the user. – ewok Mar 15 '16 at 20:30
  • Same with `find` command for instance (try `̀find -type f .`). You may also reorder parameters in script. – SLePort Mar 15 '16 at 20:35
  • how do i do that? If I can reorder parameters within the script, I would think I could just as easily search for -C and load it, then run the rest of the options. – ewok Mar 15 '16 at 20:36
  • You can have more control on your options using `getopts`. For more, see http://wiki.bash-hackers.org/howto/getopts_tutorial. – SLePort Mar 15 '16 at 20:48
  • how so? My understanding was that `getopts` was just a different way to do mostly the same thing. `getopts` is still going to iterate over the options one by one, so if I want to search for one specific option before the others, I need to iterate twice. If you have a way to solve the issue, please post an example – ewok Mar 15 '16 at 20:52
  • Maybe here : http://stackoverflow.com/questions/11742996/shell-script-is-mixing-getopts-with-positional-parameters-possible – SLePort Mar 15 '16 at 20:55
  • That's just a demonstration of how to use `getopts`. It doesn't really address the problem I'm trying to solve. It's fine if you don't know. – ewok Mar 15 '16 at 20:58
  • You're right, my last link was not a pertinent choice (i was in rush...). I updated my answer. – SLePort Mar 16 '16 at 09:45
1

You can preprocess the arguments and pull out the value you're looking for:

#!/bin/bash
args=$(getopt C:a:b:c: "$@")
eval set -- $args

conf="default.cfg"
source "$conf"

# pre-process the arguments and see if we can find -C    
found=0
for opt in "$@"; do
    if [[ $found -eq 1 ]] && [[ -f "$opt" ]]; then
        source "$opt"
        break
    fi
    if [[ "$opt" == "-C" ]]; then
        found=1
    fi
done

while [ $# -gt 0 ]; do
    case "$1" in
    -a) cfg1="$2"; shift;;
    -b) cfg2="$2"; shift;;
    -c) cfg3="$2"; shift;;
    -C) shift;; #don't do anything with this
    --) shift;
    break;;
    -*) echo "invalid option";
        exit 1;;
    *) break;;
    esac
    shift
done

echo "cfg1 = $cfg1"
echo "cfg2 = $cfg2"
echo "cfg3 = $cfg3"

exit 0
miken32
  • 42,008
  • 16
  • 111
  • 154
  • It's worth mentioning that you should use `getopts` which is a shell builtin that's part of the POSIX spec, though the syntax is slightly different. `getopt` could be a much older external utility, or a newer Gnu version with support for long options. If you want to rely on the Gnu version, you should test for it. – miken32 Mar 15 '16 at 22:02
0

First source default.cfg. Than scan your options for a -C option. Handle this one when found. Finally use getopts and skip -C when you find it during getopts.

Walter A
  • 19,067
  • 2
  • 23
  • 43