-1

I want to create a config file that has parameters that can be specified using - or -- (without an equals sign), as well as the ability to output the file using - or --. Could somebody point me to a generic script to do this? Say for example, I wanted to run MainScript and have it set alpha to 2 and beta to 3 in config.cfg by saying

MainScript --alpha 2 --beta 3 --output config.cfg

or

MainScript -a 2 -b 3 -o config.cfg

So then if I open config.cfg it has

alpha = 2
beta = 3

Alpha and beta here are arbitrarily named, there will be more parameters, these are just an example. The values used can be any sort of number, no validation required.

I've checked this thread (https://stackoverflow.com/questions/192249/how-do-i-parse-command-line-arguments-in-bash), but can't seem to make anything work, I'm also not on version4 of Bash. Thanks!

  • It would be easier to just say printf 'alpha = 2\nbeta = 3\n' >config.cfg. Could you explain why you need a script for this? – Kusalananda Nov 4 '18 at 18:04
  • I am creating a script that I want any user to be able to call from a different directory and specify their own parameters. What I gave was just a crude example. – user317784 Nov 4 '18 at 18:18
  • Could you maybe give a more sophisticated example? Should the script be able to write arbitrarily named parameters, or only alpha and beta? Are the values of the parameters always positive integers, or could it be strings as well (with embedded spaces, tabs and/or newlines)? Does the script need to do any validation of the given parameters and their values so that only valid data is written to the configuration file? – Kusalananda Nov 4 '18 at 18:23
  • Fixed, apologies, I'm new to this. – user317784 Nov 4 '18 at 18:27
  • The thread you linked has a great answer about arg parsing in bash, and the first code block should work in pretty much any array-capable shell. Maybe post a minimal example showing what you were doing, and why it doesn't work the way you want? – Wüstengecko Nov 4 '18 at 18:46
0
#!/bin/sh

outfile=/dev/stdout

while getopts 'o:' opt; do
    case $opt in
        o)  outfile=$OPTARG ;;
        *)  echo 'Error in command line parsing' >&2
            exit 1
    esac
done

shift "$(( OPTIND - 1 ))"

while [ "$#" -gt 0 ]; do
    printf '%s = %s\n' "${1#--}" "$2"
    shift 2
done >$outfile

This script will take an -o option that will set the output file to the given option argument. If -o is not used, the output will go to standard output.

The non-option bit of the script's command line must be given after --. The double dash is a standard way of signalling that there are no more options to the utility and that the rest of the operands should not be processed as command line options. We use this fact here so that getopts does not get confused by the option-like strings present at the end of the command line (these are not options to this script).

The while loop at the end of the script will iterate over the operands in pairs. It will strip off the double dash from the first operand and output the modified value together with the second operand with an equal sign in-between. If there is no double dash at the start of the first operand (as in -alpha), the value is left unmodified. The output is redirected to the given output file name.

Example run:

$ ./script.sh -o out -- --alpha 2 --beta 4 --abba "Dancing Queen" --xxx --yyy
$ cat out
alpha = 2
beta = 4
abba = Dancing Queen
xxx = --yyy

A shorter variant without command line parsing:

#!/bin/sh

while [ "$#" -gt 0 ]; do
    printf '%s = %s\n' "${1#--}" "$2"
    shift 2
done

This would be used together with a redirection on the command line to write the result to a file:

./script.sh --alpha 2 --beta 4 --abba "Dancing Queen" --xxx --yyy >outfile
  • Note that your first example only accepts -o as the very first option on the command line. This may actually be beneficial however, as it's clear which options get processed by whom. The downside to your version is that it only accepts long option names and not short ones (i.e. -a = --alpha); the ideal solution probably would be a mix of our two answers. – Wüstengecko Nov 4 '18 at 20:48
  • @Wüstengecko Since the script needs to take arbitrary parameter names, it can not know that a corresponds to alpha and not to apple. Adding a list of approved options and their mappings from short to long names is doable, but then they would no longer be arbitrary. Note that the --alpha etc. "options" are not options to the script, they are data (parameter names to be writte to the configuration file). Ideally they are specified without dashes as they have nothing to do with options to the script. – Kusalananda Nov 4 '18 at 21:23
  • From how I understood the question, --alpha 2 --beta 3 and -a 2 -b 3 should both result in the config file alpha = 2\nbeta = 3. Also "there will be more parameters" implied to me that these other parameters would actually be known beforehand - but I might have misinterpreted something here. – Wüstengecko Nov 4 '18 at 21:38
0

Try this script as a base, expanding / modifiying it for your own parameters:

#!/bin/bash

config=()
outfile=default-outfile.cfg

while [[ $# -gt 0 ]]
do
  case "$1" in
    -a|--alpha)
      config+=(alpha "$2")
      shift 2
      ;;
    -b|--beta)
      config+=(beta "$2")
      shift 2
      ;;
    -o)
      outfile="$2"
      shift 2
      ;;
    *)
      echo >&2 "Error parsing arguments: unknown option $1"
      exit 1
  esac
done

printf '%s = %s\n' "${config[@]}" > "$outfile"

The while loops over all command line arguments, and from them creates an array like

config=(alpha 2 beta 3)

i.e. the keys and values are interleaved - a key followed by a value, followed by the next pair.

It is then trivial to print them out into a config file with the printf call.

printf '%s = %s\n' consumes a set of two arguments (let's call them $1 and $2), and outputs $1 = $2 (followed by a newline). If there are more than two arguments present, it will do this in a loop until all arguments were consumed. Its output is redirected by the shell into the file named by $outfile

The final printf command in this example as invoked by the shell would therefore be

printf '%s = %s\n' "alpha" "2" "beta" "3"

and result in the output

alpha = 2
beta = 3

More examples:

$ ./script.sh --alpha 2 -b 5
$ cat default-outfile.cfg
alpha = 2
beta = 5
$ ./script.sh --alpha omega --yankee doodledandy
Error parsing arguments: unknown option --yankee
$ cat default-outfile.cfg 
alpha = 2
beta = 5
$ # (unchanged, as there was an error)
$ ./script.sh -a omega -o otherfile.cfg
$ cat otherfile.cfg
alpha = omega
$

Please do note that due to platform-specific restrictions, it may be impossible to call printf with many arguments, however it is unlikely that you'll run into this kind of error unless your users use very (very very!) long filenames or many options, and it is unlikely that the call to your script succeeded in that case in the first place.

  • Thanks so much! One more question: Let's say I want to set alpha to default to 5 if no alpha option is there. How would I do that? Thanks again! – user317784 Nov 4 '18 at 21:39
  • 1
    I'd do that with an associative array instead. declare -A config, set the defaults, then do the arg parsing (config[alpha]="$2"). This would require some non-trivial change to the outputting at the end though, which I'm not really familiar with in bash. – Wüstengecko Nov 4 '18 at 22:04

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy