Pass multi-word arguments to a bash function
Asked Answered
L

2

17

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)

Lullaby answered 8/4, 2012 at 11:21 Comment(0)
T
11
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
Tenenbaum answered 8/4, 2012 at 12:25 Comment(2)
I see... so you are passing items individually to the function. Seems to me that the only place where bash splits a string into items while respecting quotes is when interpreting a command line.Lullaby
The nature of passing the array elements as "${items[@]}" (in double quotes) has the effect of sending each element as "${items[0]}" "${items[1]}" "${items[2]} ... etc ... if you passed it as ${items[@]} you would get word splitting with elements which contained whitespace.Tenenbaum
A
3

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[@]}"
Azeria answered 1/11, 2012 at 10:42 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.