In a bash script I have an expect segment where it sends commands to a machine via ssh. I have the bash -c
there as I need to set environment variables in this command.
But I'm getting an error:
extra characters after close-quote
while executing
"spawn -noecho ssh -t anon@machine 'bash -c "cd /tmp;"'
"
When I run that command manually ssh -t anon@machine 'bash -c "cd /tmp;"'
it works fine. What is wrong here?
ssh_param="-t ${scp_user}${_machine} 'bash -c \"cd /tmp;\"'"
/usr/bin/expect <<-EOF1
set timeout 360
spawn -noecho ssh ${ssh_param}
expect {
"password:" {
send "${_pass}\r"
}
eof { exit }
}
expect {
eof { exit }
}
EOF1
Also if I set ssh_param as ssh_param="-t ${scp_user}${_machine} \"cd /tmp;\""
this works, so why does bash -c ...
cause it to fail?
After bash does its parameter substitution on the heredoc, expect
is run with
set timeout 360
spawn -noecho ssh -t XXXYYY 'bash -c "cd /tmp;"'
expect {
"password:" {
send "ZZZ\r"
}
eof { exit }
}
expect {
eof { exit }
}
expect
scripts are written in the tcl
language, which basically works by splitting up a line into words and treating the first word as a command to run with the rest of the words as arguments after doing substitutions. Kind of like shell that way, but with saner rules. The 12 rules for parsing tcl are called the Dodekalogue.
Single quotes are not used to group multiple words into one - only double quotes, []
(Which indicates a command and arguments to evaluate), and {}
(Which don't substitute or evaluate anything in them). So the line
spawn -noecho ssh -t XXXYYY 'bash -c "cd /tmp;"'
is split up into spawn
, -noecho
, ssh
, -t
, XXXYYY
, 'bash
, -c
and "cd /tmp;"'
. The single quote at the end after the closing double quote of the last word is an error. Normally in tcl you'd just wrap it in curly braces instead (Or double quotes but then you have to escape the nested ones):
spawn -noecho ssh -t XXXYYY {bash -c "cd /tmp;"}
which means the shell variable you set needs to look something like
ssh_param="-t ${scp_user}${_machine} {bash -c \"cd /tmp;\"}"
Writing the entire thing in tcl/expect instead of shell that builds expect scripts to execute will make life easier, as you don't have to switch between different quoting styles, worry about shell parameter expansion causing invalid tcl to be generated like this example, etc.