14
$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

2
  • 2
    Consider not paring options but passing values through the environment: eg "host=hostname user=me myscript.sh" Commented Feb 3, 2011 at 17:25
  • duplicate of stackoverflow.com/questions/192249/…, 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.) Commented Sep 5, 2015 at 4:35

6 Answers 6

17

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}
...
Sign up to request clarification or add additional context in comments.

4 Comments

Using declare instead of eval would be more secure.
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 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.
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"
12

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?

2 Comments

+1; note, however, that this won't handle compressed options, such as -vf to represent -v -f.
d= (-_o ) Thanks! Tested and works on /bin/sh (without Bash).
9

man getopt

1 Comment

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 with the latter that have to be addressed (later versions of getopt have fewer issues).
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?

Comments

1

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

Comments

1
declare ${@/-}

will parse and assign all the arguments that follow the format: script -param1=value1 -param2=value2 ...

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.