12
$myscript.sh -host blah -user blah -pass blah

I want to pass arguments into it.

I'm used to doing $1, $2, $3....but I want to start naming them

Martin
  • 37,119
  • 15
  • 73
  • 82
TIMEX
  • 259,804
  • 351
  • 777
  • 1,080
  • 2
    Consider not paring options but passing values through the environment: eg "host=hostname user=me myscript.sh" – William Pursell Feb 03 '11 at 17:25
  • duplicate of http://stackoverflow.com/questions/192249/how-do-i-parse-command-line-arguments-in-bash, which has a very good answer comparing pure-bash `switch`, `getopts` (POSIX shell builtin), and `getopt` (not recommended unless it's the util-linux version and you use its non-POSIX features to avoid problems with empty args, and so on.) – Peter Cordes Sep 05 '15 at 04:35

5 Answers5

16

There are lots of ways to parse arguments in sh. Getopt is good. Here's a simple script that parses things by hand:

#!/bin/sh
# WARNING: see discussion and caveats below
# this is extremely fragile and insecure

while echo $1 | grep -q ^-; do
    # Evaluating a user entered string!
    # Red flags!!!  Don't do this
    eval $( echo $1 | sed 's/^-//' )=$2
    shift
    shift
done

echo host = $host
echo user = $user
echo pass = $pass
echo args = $@

A sample run looks like:

$ ./a.sh -host foo -user me -pass secret some args
host = foo
user = me
pass = secret
args = some args

Note that this is not even remotely robust and massively open to security holes since the script eval's a string constructed by the user. It is merely meant to serve as an example for one possible way to do things. A simpler method is to require the user to pass the data in the environment. In a bourne shell (ie, anything that is not in the csh family):

$ host=blah user=blah pass=blah myscript.sh

works nicely, and the variables $host, $user, $pass will be available in the script.

#!/bin/sh
echo host = ${host:?host empty or unset}
echo user = ${user?user not set}
...
William Pursell
  • 204,365
  • 48
  • 270
  • 300
  • 3
    Using `declare` instead of `eval` would be more secure. – Dennis Williamson Feb 03 '11 at 09:40
  • In addition to @DennisWilliamson's suggestion, I suggest removing `| tr -d '\012'`, as it is not necessary - command substitution automatically trims all trailing newlines. – mklement0 Mar 13 '14 at 23:05
  • @mklement0 I believe I was actually consciously thinking about internal newlines, which is odd considering that attempting to deal with that is an attempt to add robustness to an idea which is absurdly fragile. There are so many ways this is fragile, it's just too funny. – William Pursell Aug 14 '14 at 13:10
  • Here's an example of using `declare` without using `sed` (or `eval`): `declare "${1#-}"=$2` . Note that neither this nor the technique in this answer will handle two or more leading hyphens nor embedded hyphens. Also, hyphens aren't allowed in Bash variable names. A separate step could be added to convert hyphens to underscores which _are_ allowed: `var=${1#-}; declare "${var//-/_}"="$2"` – Dennis Williamson Apr 26 '19 at 22:12
11

Here is a simple way to handle both long and short options:

while [[ $1 == -* ]]; do
    case "$1" in
      -h|--help|-\?) show_help; exit 0;;
      -v|--verbose) verbose=1; shift;;
      -f) if [[ $# > 1 && $2 != -* ]]; then
            output_file=$2; shift 2
          else 
            echo "-f requires an argument" 1>&2
            exit 1
          fi ;;
      --) shift; break;;
      -*) echo "invalid option: $1" 1>&2; show_help; exit 1;;
    esac
done

From How can I handle command-line arguments (options) to my script easily?

Top-Master
  • 7,611
  • 5
  • 39
  • 71
Dennis Williamson
  • 346,391
  • 90
  • 374
  • 439
10

man getopt

Zac Thompson
  • 12,401
  • 45
  • 57
James Sumners
  • 14,485
  • 10
  • 59
  • 77
  • 1
    Note that `getopts` is a Bash builtin while `getopt` is an external utility. The former only works with short options (e.g. `-e`) while the latter will also work with long ones (e.g. `-foo`). Note that there are some [issues](http://mywiki.wooledge.org/BashFAQ/035) with the latter that have to be addressed (later versions of `getopt` have fewer issues). – Dennis Williamson Mar 14 '14 at 00:50
1

I adopt above William Pursell example (with Dennis Williamson advice) for parameters in this format: script -param1=value1 -param2=value2 ...

Here is code with one-line arguments parser (save it in file 'script'):

#!/bin/bash

while echo $1 | grep ^- > /dev/null; do declare $( echo $1 | sed 's/-//g' | sed 's/=.*//g' | tr -d '\012')=$( echo $1 | sed 's/.*=//g' | tr -d '\012'); shift; done

echo host = $host
echo user = $user
echo pass = $pass

You call it like that:

script -host=aaa -user=bbb -pass=ccc

and result is

host = aaa
user = bbb
pass = ccc

Do someone know shorter code to parse arguments than this above?

Kamil Kiełczewski
  • 85,173
  • 29
  • 368
  • 345
0

Here's my approach:

host=""
user=""
pass=""
while [[ "$#" -gt 0 ]]; do
    case $1 in
        -h|--host) host="$2"; shift ;;
        -u|--user) user="$2"; shift ;;
        -p|--pass) pass="$2"; shift ;;
        *) echo "Unknown parameter passed: $1" ;;
    esac
    shift
done

Ege Hurturk
  • 693
  • 1
  • 10
  • 12