Calling one Bash script from another Script passing it arguments with quotes and spaces
BashShellDouble QuotesSalt StackBash 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