Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Pass multi-word arguments to a bash function

Inside a bash script function, I need to work with the command-line arguments of the script, and also with another list of arguments. So I'm trying to pass two argument lists to a function, the problem is that multi-word arguments get split.

function params()
{
    for PARAM in $1; do
        echo "$PARAM"
    done

    echo .

    for ITEM in $2; do
        echo "$ITEM"
    done
}

PARAMS="$@"
ITEMS="x y 'z t'"
params "$PARAMS" "$ITEMS"

calling the script gives me

myscript.sh a b 'c d'
a
b
c
d
.
x
y
'z
t'

Since there are two lists they must be passed as a whole to the function, the question is, how to iterate the elements while respecting multi-word items enclosed in single quotes 'c d' and 'z t'?

The workaround that I have (see below) makes use of BASH_ARGV so I need to pass just a single list to the function. However I would like to get a better understanding of what's going on and what's needed to make the above work.

function params()
{
    for PARAM in "${BASH_ARGV[@]}"; do
        echo "$PARAM"
    done

    echo .

    for ITEM in "$@"; do
        echo "$ITEM"
    done
}

params x y 'z t'

calling the script gives me

myscript.sh a b 'c d'
c d
b
a
.
x
y
z t

... Which is how I need it (except that first list is reversed, but that would be tolerable I guess)

like image 393
haelix Avatar asked Apr 08 '12 11:04

haelix


People also ask

How do I pass multiple arguments in bash?

You can pass more than one argument to your bash script. In general, here is the syntax of passing multiple arguments to any bash script: script.sh arg1 arg2 arg3 … The second argument will be referenced by the $2 variable, the third argument is referenced by $3 , .. etc.

How do you pass an argument to a function in bash?

We can use the getopts program/ command to parse the arguments passed to the script in the command line/ terminal by using loops and switch-case statements. Using getopts, we can assign the positional arguments/ parameters from the command line to the bash variables directly.

What does %% mean in bash?

So as far as I can tell, %% doesn't have any special meaning in a bash function name. It would be just like using XX instead. This is despite the definition of a name in the manpage: name A word consisting only of alphanumeric characters and under- scores, and beginning with an alphabetic character or an under- score.

How do you pass arguments to a shell script?

Syntax for defining functions: To invoke a function, simply use the function name as a command. To pass parameters to the function, add space-separated arguments like other commands. The passed parameters can be accessed inside the function using the standard positional variables i.e. $0, $1, $2, $3, etc.


2 Answers

function params()
{
   arg=("$@")

   for ((i=1;i<=$1;i++)) ;do
       echo "${arg[i]}"
   done

   echo .

   for ((;i<=$#-$1+2;i++)) ;do
       echo "${arg[i]}"
   done
}

items=(w x 'z t')
params $# "$@" "${items[@]}"

Assuming you call your script with args a b 'c d', the output is:

a
b
c d
.
x
y
z t
like image 196
Peter.O Avatar answered Oct 09 '22 07:10

Peter.O


Peter.O's answer above works fine, and this is an addendum to it, with an example.

I needed a function or script that would take multi-word arguments, that would then be used to search the list of running processes and kill those that matched one of the arguments. The following script does that. The function kill_processes_that_match_arguments only needs one for loop, because my need was only to iterate over the set of all arguments to the function. Tested to work.

#!/bin/bash

function kill_processes_that_match_arguments()
{
    arg=("$@")
    unset PIDS_TO_BE_KILLED

    for ((i=0;i<$#;i++))
    do
        unset MORE_PIDS_TO_KILL
        MORE_PIDS_TO_KILL=$( ps gaux | grep "${arg[i]}" | grep -v 'grep' | awk '{ print $2 }' )
        if [[ $MORE_PIDS_TO_KILL ]]; then
            PIDS_TO_BE_KILLED="$MORE_PIDS_TO_KILL $PIDS_TO_BE_KILLED"
        fi
    done

    if [[ $PIDS_TO_BE_KILLED ]]; then
        kill -SIGKILL $PIDS_TO_BE_KILLED
        echo 'Killed processes:' $PIDS_TO_BE_KILLED
    else
        echo 'No processes were killed.'
    fi
}

KILL_THESE_PROCESSES=('a.single.word.argument' 'a multi-word argument' '/some/other/argument' 'yet another')
kill_processes_that_match_arguments "${KILL_THESE_PROCESSES[@]}"
like image 33
Teemu Leisti Avatar answered Oct 09 '22 05:10

Teemu Leisti