11

Is it possible to pass command line arguments to shell script as name value pairs, something like

myscript action=build module=core

and then in my script, get the variable like $action and process it?

I know that $1....and so on can be used to get variables, but then won't be name value like pairs. Even if they are, then the developer using the script will have to take care of declaring variables in the same order. I do not want that.

3
  • 1
    I think your answer is here: stackoverflow.com/questions/2642707/… Commented Mar 31, 2011 at 12:07
  • Yes, as opposed to the bash builtin getopts, getopt works with tcsh as well. Commented Mar 31, 2011 at 12:11
  • I guess something like that is available only for bash.Sorry I didnt specify, Im using tcsh. Commented Mar 31, 2011 at 12:24

5 Answers 5

19

This worked for me:

for ARGUMENT in "$@"
do
   KEY=$(echo $ARGUMENT | cut -f1 -d=)

   KEY_LENGTH=${#KEY}
   VALUE="${ARGUMENT:$KEY_LENGTH+1}"

   export "$KEY"="$VALUE"
done

# from this line, you could use your variables as you need

cd $FOLDER
mkdir $REPOSITORY_NAME

Usage

bash my_scripts.sh  FOLDER="/tmp/foo" REPOSITORY_NAME="stackexchange"

FOLDER and REPOSITORY_NAME are ready to use in the script.

It does not matter what order the arguments are in.

Changelog

Sign up to request clarification or add additional context in comments.

2 Comments

Thanks. Is there a windows equivalent for batch scripts?
Theoretically, you can replicate this script in windows using its own commands. Here some links with complex windows bats if you need : - github.com/krichter722/squirrel-sql/blob/master/archive/… - github.com/apache/tomcat/tree/master/bin
5

In the Bourne shell, there is a seldom-used option '-k' which automatically places any values specified as name=value on the command line into the environment. Of course, the Bourne/Korn/POSIX shell family (including bash) also do that for name=value items before the command name:

name1=value1 name2=value2 command name3=value3 -x name4=value4 abc

Under normal POSIX-shell behaviour, the command is invoked with name1 and name2 in the environment, and with four arguments. Under the Bourne (and Korn and bash, but not POSIX) shell -k option, it is invoked with name1, name2, name3, and name4 in the environment and just two arguments. The bash manual page (as in man bash) doesn't mention the equivalent of -k but it works like the Bourne and Korn shells do. I don't think I've ever used it (the -k option) seriously.

There is no way to tell from within the script (command) that the environment variables were specified solely for this command; they are simply environment variables in the environment of that script.

This is the closest approach I know of to what you are asking for. I do not think anything equivalent exists for the C shell family. I don't know of any other argument parser that sets variables from name=value pairs on the command line.


With some fairly major caveats (it is relatively easy to do for simple values, but hard to deal with values containing shell meta-characters), you can do:

case $1 in
(*=*) eval $1;;
esac

This is not the C shell family. The eval effectively does the shell assignment.

arg=name1=value1
echo $name1
eval $arg
echo $name1

3 Comments

I've tried to find information for passing environmental variables to commands like name=value command. However I cannot find any explanation in man bash. Is there any man page documenting this?
I just found it in man bash: The environment for any simple command or function may be augmented temporarily by prefixing it with parameter assignments, as described above in PARAMETERS. These assignment statements affect only the environment seen by that command.
Several sections of the Bash manual discuss the setting the environment before the command name. One is Simple Command Expansion. Another is the section on Environment and that provides pointers to -k via The Set Built-in.
2
env action=build module=core myscript

You said you're using tcsh. For Bourne-based shells, you can drop the "env", though it's harmless to leave it there. Note that this applies to the shell from which you run the command, not to the shell used to implement myscript.

If you specifically want the name=value pairs to follow the command name, you'll need to do some work inside myscript.

2 Comments

on bash RHEL this is working for me even without env prefix; is env prefix important?
@spioter env is an external command, typically /usr/bin/env, that updates the environment and invokes the specified command. In bash, action=build module=core myscript does essentially the same thing; it invokes myscript with those two entries added to the environment. (In both cases, the environment updates do not persist past the execution of myscript.) The env command still has its uses, but it's not needed in this case.
1

It's quite an old question, but still valid I have not found the cookie cut solution. I combined the above answers. For my needs I created this solution; this works even with white space in the argument's value.

Save this as argparse.sh

#!/bin/bash

: ${1?
  'Usage:
  $0 --<key1>="<val1a> <val1b>" [ --<key2>="<val2a> <val2b>" | --<key3>="<val3>" ]'
}

declare -A args
while [[ "$#" > "0" ]]; do
  case "$1" in 
    (*=*)
        _key="${1%%=*}" &&  _key="${_key/--/}" && _val="${1#*=}"
        args[${_key}]="${_val}"
        (>&2 echo -e "key:val => ${_key}:${_val}")
        ;;
  esac
  shift
done
(>&2 echo -e "Total args: ${#args[@]}; Options: ${args[@]}")

## This additional can check for specific key
[[ -n "${args['path']+1}" ]] && (>&2 echo -e "key: 'path' exists") || (>&2 echo -e "key: 'path' does NOT exists");

@Example: Note, arguments to the script can have optional prefix --

./argparse.sh --x="blah"
./argparse.sh --x="blah" --yy="qwert bye"
./argparse.sh x="blah" yy="qwert bye"

Some interesting use cases for this script:

./argparse.sh --path="$(ls -1)"
./argparse.sh --path="$(ls -d -1 "$PWD"/**)"

Above script created as gist, Refer: argparse.sh

Comments

0

Extending on Jonathan's answer, this worked nicely for me:

#!/bin/bash
if [ "$#" -eq "0" ]; then
  echo "Error! Usage: Remind me how this works again ..."
  exit 1
fi

while [[ "$#" > "0" ]]
do
  case $1 in
    (*=*) eval $1;;
  esac
shift
done

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.