Search code examples
bashcommand-linegetoptgetopts

Automatic parsing of getopts options into dynamic variables of same name in bash


I have a bash script I wrote with say, 3 command line options, bib, bob and boo... and I want to read in the user options into a bash variable of the same name, which I do as follows:

PARSED_OPTIONS=$(getopt -n $0  --long "bib:,bob:,boo:"  -- "$@")

eval set -- "$PARSED_OPTIONS";

while true; do
  case "$1" in

  --bib)
  bib=$2
  shift 2;;

  --bob)
  bob=$2
  shift 2;;

  --boo)
  boo=$2
  shift 2 ;;

  --)
  shift
  break;;

  esac
done

This all works fine, so far, so good...

But now I want to extend this to a list of many many options, and so rather than writing out a long case statement, it would be really nice to be able to somehow loop over a list of options and automatically pass the options to the variable, something along these lines

opts="bib:,bob:,boo:," 

PARSED_OPTIONS=$(getopt -n $0  --long $opts -- "$@")

for arg in `echo $opts | tr , " "` ; do 
  eval set -- "$PARSED_OPTIONS";
  while true; do

    case "$1" in
      --${arg})
      declare $arg=$2
      shift 2
      ;;
      --)
      shift
      break;;
    esac
  done
done

I'm using the declaration statement to get the argument into a dynamic variable of the same name (see Dynamic variable names in Bash second solution), and this solution to do the loop over comma separated lists Loop through a comma-separated shell variable but I'm getting an infinite loop here. I think because the 2 unused options are allows as they are in the PARSED_OPTIONS list, but then they are not sliced off in the loop as only "arg" is looked for... I can't see an obvious way around this, but I'm sure there is one.


Solution

  • I realized that I had the shift command still inside the case statement, so that is why it wasn't exiting. I also needed to strip the colon : from the argument, so here is my automated argument retrieval for a bash script that works:

    # specify an arbitrary list of arguments:
    
    opts=bib:,bob:,boo:
    
    PARSED_OPTIONS=$(getopt -n $0  --long "${opts}"  -- "$@")
    
    for arg in ${opts//,/ } ; do 
      var=${arg//:} # remove the colon
      eval set -- "$PARSED_OPTIONS";
      while true ; do
        case "$1" in
          --${var})
          declare ${var}=$2
          ;;
        --)
          break
          ;;
        esac
        shift 2
      done
    done
    

    So if you try test_script --boo 3 --bib hello --bob lkkfrfrfr

    echo $bib $bob $boo
    

    should give

    hello lkkfrfrfr 3