Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a way to avoid positional arguments in bash?

I have to write a function in bash. The function will take about 7 arguments. I know that I can call a function like this:

To call a function with parameters:

function_name $arg1 $arg2 

And I can refer my parameters like this inside the function:

function_name () {    echo "Parameter #1 is $1" } 

My question is, is there a better way refer to the parameters inside the function? Can I avoid the $1, $2, $3, .... thing and simply use the $arg1, $arg2, ...?

Is there a proper method for this or do I need to re-assign these parameters to some other variables inside the function? E.g.:

function_name () {    $ARG1=$1    echo "Parameter #1 is $ARG1" } 

Any example would be much appreciated.

like image 968
Bhushan Avatar asked Aug 26 '12 07:08

Bhushan


People also ask

What is a positional argument in bash?

A positional parameter is a parameter denoted by one or more digits, other than the single digit 0 . Positional parameters are assigned from the shell's arguments when it is invoked, and may be reassigned using the set builtin command.

Do all bash scripts require arguments?

In many cases, bash scripts require argument values to provide input options to the script. You can handle command-line arguments in a bash script in two ways. One is by using argument variables, and another is by using the getopts function.

Does bash ignore whitespace?

Bash uses whitespace to determine where words begin and end. The first word is the command name and additional words become arguments to that command.

Is bash 1 True or false?

There are no Booleans in Bash. However, we can define the shell variable having value as 0 (“ False “) or 1 (“ True “) as per our needs. However, Bash also supports Boolean expression conditions.


1 Answers

The common way of doing that is assigning the arguments to local variables in the function, i.e.:

copy() {     local from=${1}     local to=${2}      # ... } 

Another solution may be getopt-style option parsing.

copy() {     local arg from to     while getopts 'f:t:' arg     do         case ${arg} in             f) from=${OPTARG};;             t) to=${OPTARG};;             *) return 1 # illegal option         esac     done }  copy -f /tmp/a -t /tmp/b 

Sadly, bash can't handle long options which would be more readable, i.e.:

copy --from /tmp/a --to /tmp/b 

For that, you either need to use the external getopt program (which I think has long option support only on GNU systems) or implement the long option parser by hand, i.e.:

copy() {     local from to      while [[ ${1} ]]; do         case "${1}" in             --from)                 from=${2}                 shift                 ;;             --to)                 to=${2}                 shift                 ;;             *)                 echo "Unknown parameter: ${1}" >&2                 return 1         esac          if ! shift; then             echo 'Missing parameter argument.' >&2             return 1         fi     done }  copy --from /tmp/a --to /tmp/b 

Also see: using getopts in bash shell script to get long and short command line options


You can also be lazy, and just pass the 'variables' as arguments to the function, i.e.:

copy() {     local "${@}"      # ... }  copy from=/tmp/a to=/tmp/b 

and you'll have ${from} and ${to} in the function as local variables.

Just note that the same issue as below applies — if a particular variable is not passed, it will be inherited from parent environment. You may want to add a 'safety line' like:

copy() {     local from to    # reset first     local "${@}"      # ... } 

to ensure that ${from} and ${to} will be unset when not passed.


And if something very bad is of your interest, you could also assign the arguments as global variables when invoking the function, i.e.:

from=/tmp/a to=/tmp/b copy 

Then you could just use ${from} and ${to} within the copy() function. Just note that you should then always pass all parameters. Otherwise, a random variable may leak into the function.

from= to=/tmp/b copy   # safe to=/tmp/b copy         # unsafe: ${from} may be declared elsewhere 

If you have bash 4.1 (I think), you can also try using associative arrays. It will allow you to pass named arguments but it will be ugly. Something like:

args=( [from]=/tmp/a [to]=/tmp/b ) copy args 

And then in copy(), you'd need to grab the array.

like image 103
Michał Górny Avatar answered Sep 19 '22 15:09

Michał Górny