LinuxQuestions.org

LinuxQuestions.org (/questions/)
-   Linux - Software (http://www.linuxquestions.org/questions/linux-software-2/)
-   -   bash pass multiple arguments with spaces (http://www.linuxquestions.org/questions/linux-software-2/bash-pass-multiple-arguments-with-spaces-717268/)

msreddy999 04-06-2009 05:50 PM

bash pass multiple arguments with spaces
 
Hi,

I have a c program that takes multiple arguments (filenames). You can pass in as many arguments as you want. You can run it like,

./myCprogram "My File1" "My File 2"

Now I want to call that from a bash script. My bash script needs to read the arguments from command line and pass them to the c program. I can read the arguments fine but I can't pass them correctly because the arguments have spaces in them.

Right now I am doing something like,

myargs+=" \""$OPTARG"\""
echo $myargs
#Echo prints "My File1" "My File 2"
myCProgram $myargs
This fails. Bash puts ` in spaces.

Then I tried
myCProgram "$myargs"
This fails too as the whole $myargs is passed as single parameter to myCprogram.

How can I fix this.

Thanks,
MSR

i92guboj 04-06-2009 06:29 PM

Try passing the list as "$@"

msreddy999 04-06-2009 06:32 PM

THanks guboj. But that will not work for me. The example I gave was a simple one. In reality, my bash script needs to read multiple arguments and call multiple programs passing different arguments to each one. So I can't just pass $@.

kenoshi 04-06-2009 06:34 PM

If you have space in your args, use a different default delimiter, for example:

args.sh
Code:

OFS=$IFS
IFS=':'
optargs="a:b:c d:e f"
./cprog.sh $optargs

cprog.sh
Code:

args=("$@")
i=${#args[*]}
while [ "$i" -gt "0" ] ; do
        echo "Argument $i is ${args[$((i-1))]}"
        ((i--))
done

Executing args.sh results in:
Code:

Argument 4 is e f
Argument 3 is c d
Argument 2 is b
Argument 1 is a

Probably better ways to do this, hope this helps.

i92guboj 04-06-2009 06:40 PM

I fail to see why that will not work.

You mean like in this case:

Code:

#!/bin/bash
# 1.sh

./2.sh "$@"


Code:

#!/bin/bash
# 2.sh

while [ ! -z "$1" ]; do echo "\"$1\""; shift; done

The fact that 2.sh is another script or a C program shouldn't matter. The list is passed as a list, with al the members intact, hence the result of running this:

Code:

./1.sh asdf ˝klj poiu rqwer piupo "foo fight"
Would be:

Code:

"asdf"
"˝klj"
"poiu"
"rqwer"
"piupo"
"foo fight"

As you see, the arguments are ok. Just change the while loop with whatever, and send each argument to whatever place it needs to be. Isn't that what you need?

msreddy999 04-06-2009 07:00 PM

Sorry if I wasn't clear. I have no issues reading and echoing all the arguments in my script. But its passing them correctly to a different program thats giving me problems.

Inside the script, I need to call two different programs

./myProgramA "My Arg 1" "My arg4" "My arg 5" #All parameters with -a
./myProgramB "My Arg 2" "Myarg3" #All arguments with -b

As you can see I can't just pass $@. I need to first parse all the arguments (which I can do correctly) and then disperse them seperately to appropriate program. . But no I need to

myScript -a "My Arg 1" -b "My Arg 2" -b "Myarg3" -a "My arg4" -a "My arg 5"

So I tried

programAArgs=" "
programBArgs=" "
while getopts "a:b:" Option
do
case $option in
a )
programAArgs=" \""$OPTARG"\""
;;
b )
programBArgs=" \""$OPTARG"\""
;;
esac
done

echo $programAArgs #"My Arg 1" "My arg4" "My arg 5"
echo $programBArgs #"My Arg 2" "Myarg3"

./myProgramA $programAArgs #This fails. Bash puts ` at blanks
./myProgramA "$programAArgs" #This fails too as all of "My Arg 1" "My arg4" "My arg 5" is passed as one single argument to myProgramA not as 3 arguments

Hope that makes it clear

i92guboj 04-06-2009 07:17 PM

I see. I now understand what the problem is. Well, then using an alternative IFS at kenoshi said above is the easiest way to go. Sorry for the misunderstanding.

msreddy999 04-06-2009 09:37 PM

Thanks. I looked at kenoshi's solution. Its not clear to me how I can use it. I am new to scripting so excuse my ignorance. I am still trying to understand his solution.

i92guboj 04-07-2009 04:55 AM

The whole trick would be based on using any other arbitrary character as a field separator, kenoshi uses the colon ":". To specify the field separator you use the IFS variable. So, in the first script you parse everything and save the parameters on strings. Each string will contain an arbitrary number of parameters separated by the ":" (or whatever). Then you can send those strings to a second script, to a function, or wherever, just remember to set IFS before that, so this list of elements separated by : will be interpreted as a list of separated entities (arguments).

This is a quick example and I didn't even test it, but I think it should work at least for the most part, and it will illustrate a couple of example functions

Code:

#!/bin/bash
# a.sh

IFS=":"

_foonction() {
        args=("$@")
        i=${#args[*]}
        while [ "$i" -gt "0" ]; do
                echo "Argument $i is ${args[$((i-1))]}"
                ((i--))
        done
}
foonction() {
        touch "$@"
}

while [ ! -z "$1" ]; do
        case "$1" in
        -a)    if [ -z "$a_params" ]; then
                                a_params="$2"
                        else
                                a_params="$a_params:$2"
                        fi
                        shift
                        ;;
        -b)    if [ -z "$b_params" ]; then
                                b_params="$2"
                        else
                                b_params="$b_params:$2"
                        fi
                        shift
                        ;;
        *)      if [ -z "$z_params" ]; then
                                z_params="$1"
                        else
                                z_params="$z_params:$1"
                        fi
                        ;;
        esac
        shift
done


echo "a_params=$a_params"
echo "=================="
_foonction $a_params
echo
echo "b_params=$b_params"
echo "=================="
_foonction $b_params
echo
echo "z_params=$z_params"
echo "=================="
echo " touch'ing some files:"
foonction $z_params
ls -l $z_params
echo

See how in one function I use the kenoshi's snippet, to illustrate how would I use that list on another bash script (or function, it's the same), and in another function I illustrate how an external command or tool can also take approach of the IFS thing. In this case it's "touch", but it could also be your external program or another script. I hope this makes sense. Remember, it's untested so don't go mad if it doesn't work correctly (though it should).

msreddy999 04-07-2009 05:24 PM

Awesome. Thanks kenoshi and i92guboj. I think I finally got it. Thanks for detailed explanation.

norobro 04-07-2009 05:38 PM

I'm jumping in a little late, but here goes...

If you change the IFS to “ (a double quotation mark), your script will work with a couple of modifications:

Code:

programAArgs=""
programBArgs=""
while getopts "a:b:" Option
do
case $option in
a )
programAArgs+="\""$OPTARG""      # remove trailing double quote – two quotes together will give you a blank field
;;
b )
programBArgs+="\""$OPTARG""      # same as above
;;
esac
done

echo $programAArgs    # "My Arg 1"My arg4"My arg 5  - Pretty ugly but you don't need to output it
echo $programBArgs    # "My Arg 2"Myarg3

IFS=”\””                                      # set the IFS to a double quote
./myProgramA$programAArgs  # note: no space between program name and arguments so there is only one string for bash to parse

The content of the string executed by bash is:
Code:

./myProgramA”My Arg 1"My arg4"My arg 5
and bash will separate it at the double quotes


All times are GMT -5. The time now is 09:26 PM.