Calling one Bash script from another Script passing it arguments with quotes and spaces

BashShellDouble QuotesSalt Stack

Bash Problem Overview


###I made two test bash scripts on Linux to make the problem clear.

####TestScript1 looks like:####

    echo "TestScript1 Arguments:"
    echo "$1"
    echo "$2"
    echo "$#"
    ./testscript2 $1 $2

####TestScript2 looks like:####

    echo "TestScript2 Arguments received from TestScript1:"
    echo "$1"
    echo "$2"
    echo "$#"

####When i execute testscript1 in the following way:####

    ./testscript1 "Firstname Lastname" [email protected]  

####The desired Output should be:

    TestScript1 Arguments:  
    Firstname Lastname  
    testmail@domain.com  
    2
    TestScript2 Arguments received from TestScript1:  
    Firstname Lastname  
    testmail@domain.com  
    2  

####But the actual output is:####

    TestScript1 Arguments:  
    Firstname Lastname  
    testmail@domain.com  
    2
    TestScript2 Arguments received from TestScript1:  
    Firstname
    Lastname      
    3  

How do i solve this problem? I want to get the desired output instead of the actual output.

Bash Solutions


Solution 1 - Bash

Quote your args in Testscript 1:

echo "TestScript1 Arguments:"
echo "$1"
echo "$2"
echo "$#"
./testscript2 "$1" "$2"

Solution 2 - Bash

You need to use : "$@" (WITH the quotes) or "${@}" (same, but also telling the shell where the variable name starts and ends).

(and do NOT use : $@, or "$*", or $*).

ex:

#testscript1:
echo "TestScript1 Arguments:"
for an_arg in "$@" ; do
   echo "${an_arg}"
done
echo "nb of args: $#"
./testscript2 "$@"   #invokes testscript2 with the same arguments we received

I'm not sure I understood your other requirement ( you want to invoke './testscript2' in single quotes?) so here are 2 wild guesses (changing the last line above) :

'./testscript2' "$@"  #only makes sense if "/path/to/testscript2" containes spaces?

./testscript2 '"some thing" "another"' "$var" "$var2"  #3 args to testscript2

Please give me the exact thing you are trying to do

edit: after his comment saying he attempts tesscript1 "$1" "$2" "$3" "$4" "$5" "$6" to run : salt 'remote host' cmd.run './testscript2 $1 $2 $3 $4 $5 $6'

You have many levels of intermediate: testscript1 on host 1, needs to run "salt", and give it a string launching "testscrit2" with arguments in quotes...

You could maybe "simplify" by having:

#testscript1

#we receive args, we generate a custom script simulating 'testscript2 "$@"'
theargs="'$1'"
shift
for i in "$@" ; do
   theargs="${theargs} '$i'"
done

salt 'remote host' cmd.run "./testscript2 ${theargs}"

if THAt doesn't work, then instead of running "testscript2 ${theargs}", replace THE LAST LINE above by

echo "./testscript2 ${theargs}" >/tmp/runtestscript2.$$  #generate custom script locally ($$ is current pid in bash/sh/...)
scp /tmp/runtestscript2.$$ user@remotehost:/tmp/runtestscript2.$$ #copy it to remotehost
salt 'remotehost' cmd.run "./runtestscript2.$$" #the args are inside the custom script!
ssh user@remotehost "rm /tmp/runtestscript2.$$" #delete the remote one
rm /tmp/runtestscript2.$$ #and the local one

Solution 3 - Bash

You could do the following to parse all arguments:

Note the use of /bin/bash

testscript1:

#!/bin/bash

echo "TestScript1 Arguments:"
echo "$1"
echo "$2"
echo "$#"

# Build PARAMS to be a list of arguments
# Each wrapped in quotes and
# any existing quotes escapes with \
for var in "$@"
do
	PARAMS="$PARAMS \"${var//\"/\\\"}\""
done

# Call the second script with eval, also passing an extra parameter.
eval ./testscript2 "ExtraParam" $PARAMS

testscript2:

#!/bin/bash
echo "TestScript2 Arguments received from TestScript1:"
echo "$1"
echo "$2"
echo "$3"
echo "$#"

Then when you execute the following:

./testscript1 "Firstname Lastname" [email protected]  

The output will be:

TestScript1 Arguments:
Firstname Lastname
testmail@domain.com
2
TestScript2 Arguments received from TestScript1:
ExtraParam
Firstname Lastname
testmail@domain.com
3

By building the PARAMS in this way using \"${var//\"/\\\"}\"" it allows for the script to behave correctly even if called with quotes in the arguments such as follows:

./testscript1 "Firstname's Last\"name" testmail@domain.com  

Output will be:

TestScript1 Arguments:
Firstname's Last"name
[email protected]
2
TestScript2 Arguments received from TestScript1:
ExtraParam
Firstname's Last"name
[email protected]
3

All quotes and spaces are passed from one script to the other correctly.

If you don't want all the arguments passed, miss out the for loop and just use eval with the relevant arguments.

For example:

eval ./testscript2 "\"${1//\"/\\\"}\"" "Test1" "Test2" 

${1... means the 1st argument, use ${2... for the 2nd etc.

This will result in:

TestScript1 Arguments:
Firstname's Last"name
[email protected]
2
TestScript2 Arguments received from TestScript1:
Firstname's Last"name
Test1
Test2
3

If you're sure you'll never get quotes in the argument, then don't use "\"${1//\"/\\\"}\"" just use "$1".

Solution 4 - Bash

I found following program works for me

test1.sh 
a=xxx
test2.sh $a

in test2.sh you use $1 to refer variable a in test1.sh

echo $1

The output would be xxx

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionnmadhokView Question on Stackoverflow
Solution 1 - BashMarkku K.View Answer on Stackoverflow
Solution 2 - BashOlivier DulacView Answer on Stackoverflow
Solution 3 - BashJohn SimmondsView Answer on Stackoverflow
Solution 4 - BashLinsong GuoView Answer on Stackoverflow