title: Using positional parameters in place of if statements
body: <p>I'm trying to better understand how to write bash scripts using positional parameters as arguments to the script. Specifically I want to understand how to make a script as user friendly as possible, capable of handling multiple arguments from any position. Much in that <code>ls -lst</code> is the same as <code>ls -stl</code>. I decided to attempt a password generator. I'm not very familiar with writing scripts that take arguments. After a while I just decided to do the whole thing using <code>if</code> statements. </p>
  
  <p><strong>Note: to give credit where credit is due, the <code>rand_string</code> function in the script below came directly from <a href="https://stackoverflow.com/a/23837814/5904552">this answer</a>.</strong></p>
  
  <pre><code>#!/bin/bash
  
  ## grep -v '##' will remove all the comments if you're so inclined :)
  usage(){
  echo "usage: $0 [n][-c]||[-h]"  
  }
  
  ## Character set used to create random strings 
  chars=( {a..z} {A..Z} {0..9} \! \" \# \$  \% \&amp; \( \) \* \+ \, \- \. \/ \: \; \&lt; \= \&gt; \? \@ \[ \] \^ \_ \` \{ \| \} \~ \\ )
  
  ## Create random string from character set
  rand_string() { 
      local c=$1 ret=
      while((c--)); do
          ret+=${chars[$((RANDOM%${#chars[@]}))]}; done
      printf '%s\n' "$ret"
  }
  
  ## get a random number between 14-50
  length() {
      python -S -c "import random; print random.randrange(14,50)"
  }
  
  ## regular expression to test for valid numbers
  re='^[0-9]+$'
  
  ## if no options specified 
  ## create a random string of charcters
  ## of a random length between 14-50 characters
  ## display password on screen and exit
  if [ ! "$1" ]; then
      set - "$(length)"
      password="$(rand_string "$1")"
      echo "$password"
      exit
  fi
  
  ## if option 1 or option 2 is -h or -help
  ## display usage and exit
  if [[ "$1" == -h || "$1" == -help || "$2" == -h || "$2" == -help ]]; then
      usage
      exit
  fi
  
  ## if more than 2 options
  ## exit with error code 1
  if [[ $# -gt 2 ]]; then
      echo "Invalid number of options specified"
      usage
      exit 1
  fi
  ## if exactly 2 options
  if [[ $# -eq 2 ]]; then
      ## test if option 1 is a number
      ## if option 1 is NOT a number 
      if [[ ! "$1" =~ $re ]]; then
          ## test if option 1 is -c or -copy
          if [[ "$1" == -c || "$1" == -copy ]]; then
              ## if option 1 is -c or -copy
              ## test if option 2 is a number
              ## if 2 is a number and 1 is -c or -copy 
              ## execute the command
              if [[ "$2" =~ $re ]]; then
                  set - "$(length)"
                  rand_string "$1" | pbcopy
                  echo "Password copied to clipboard"
                  exit
              ## if option 1 is -c or -copy
              ## but option 2 is NOT a number
              ## exit script with error code 1
              elif [[ ! "$2" =~ $re ]]; then
                  echo "Unrecognized option \"$2\""
                  usage
                  exit 1
              fi
          else
              echo "Unrecognized option \"$1\""
              exit 1
          fi
      ## if option 1 is a number
      elif [[ "$1" =~ $re ]]; then
          ## and option 2 is -c or -copy
          ## execute the command
          if [[ "$2" == -c || "$2" == -copy ]]; then
              rand_string "$1" | pbcopy
              echo "Password copied to clipboard"
              exit
          ## if option 1 is a number 
          ## but option 2 is not -c or -copy
          ## exit script with error code 1
          else
              echo "Unrecognized option \"$2\""
              usage
              exit 1
          fi
      fi
  ## if exactly one option specified
  elif [[ $# -eq 1 ]]; then
      ## if option is NOT a number
      ## check if option is -c or -copy
      if [[ ! "$1" =~ $re ]]; then
          ## if option is -c or -copy
          ## execute the command 
          if [[ "$1" == -c || "$1" == -copy ]]; then
              set - "$(length)"
              rand_string "$1" | pbcopy
              echo "Password copied to clipboard"
              exit
          ## if option is neither a number nor -c or -copy
          ## exit script with error code 1
          else
              echo "Unrecognized option \"$1\""
              usage
              exit 1
          fi
      ## if option is a number
      ## execute the command
      elif [[ "$1" =~ $re ]]; then
          rand_string "$1"
      fi
  fi
  </code></pre>
  
  <p>This script takes two optional arguments:</p>
  
  <ul>
  <li><p>any whole number e.g.. <code>10</code>, <code>29</code>, <code>54</code> to determine the length of string</p></li>
  <li><p><code>-c</code> or <code>-copy</code> to copy the string to the clipboard</p></li>
  <li><p><code>-h</code> or <code>-help</code> to display the <code>usage</code> function</p></li>
  </ul>
  
  <p>Any of the above arguments can be called from the <code>$1</code> or <code>$2</code> position. Any of these arguments may also be omitted.</p>
  
  <p>Assuming the script is called <code>password</code> any of the following are <strong>valid uses</strong>:</p>
  
  <p><code>password</code> </p>
  
  <p><code>password 25</code> </p>
  
  <p><code>password -c</code></p>
  
  <p><code>password 16 -copy</code></p>
  
  <p><code>password -c 42</code></p>
  
  <p><code>password -help</code> </p>
  
  <p>But the script itself is a giant mess of <code>if</code> statements. It's long, messy and hard to read. </p>
  
  <p>So my question is: How could this be written to take the same arguments shown in <strong>valid uses</strong> without using so many <code>if</code> statements? </p>
  
  <p>I'm not asking anyone to rewrite my script. I'm just looking for some guidance to gain better understanding of how to do this correctly. </p>