Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

use expect to spawn command with arguments containing spaces

I want to use expect to run a simple command cat /tmp/id_rsa.pub over ssh.

In a shell, I can run this wo problem, (with manually put in the password)

ssh root@localhost 'cat /tmp/id_rsa.pub'

I want to automate this with expect. My expect script is,

#!/usr/bin/expect
eval spawn ssh root@localhost 'cat /tmp/id_rsa.pub'
expect "password:"
send "123456"
expect eof

It throws error bash: cat /tmp/id_rsa.pub: no such file or directory. it looks very strange to me. What could be the possible cause?

Edit: after some testing, I find this is common, not only in the case of cat. If the argument to spawned command is with space (even if it's in the quotes), it will have problem. For example, replacing cat /tmp/id_rsa.pub with other commands with spaces, like

eval spawn ssh root@localhost 'which java'

it complains with bash: which java: command not found. But if replacing that with pwd, like

eval spawn ssh root@localhost 'pwd'

it work fine.

like image 466
Richard Avatar asked Sep 20 '12 18:09

Richard


People also ask

What is spawn in Expect?

The spawn command is used to start a script or a program like the shell, FTP, Telnet, SSH, SCP, and so on. The remaining lines are the Expect script that interacts with our shell script. The last line if the end of file which means the end of the interaction.

How do you read command line arguments in Expect script?

Normally you would read the command line arguments as shown below. $ cat print_cmdline_args. exp #!/usr/bin/expect puts 'argv0 : [lindex $argv 0]'; puts 'argv1 : [lindex $argv 1]';

What is Exp_continue?

The command exp_continue allows expect itself to continue executing rather than returning as it normally would. By default exp_continue resets the timeout timer. The -continue_timer flag prevents timer from being restarted. (See expect for more information.)


2 Answers

Single quotes (') have no special meaning to Expect, unlike sh and other compatible shells.
This means that your statment

spawn ssh root@localhost 'cat /tmp/id_rsa.pub'

is parsed into the following words:

  • spawn
  • ssh
  • root@localhost
  • 'cat - not until the other single quote.
  • /tmp/id_rsa.pub'

The usage in sh is to group this to a single argument. In Tcl you could either use double quotes (") or curly brackets ({}). Inside double quotes, Tcl variables will be substituted, while the content inside {} is passed without any substitution1.

tl;dr The Expect/Tcl equivalent of sh's ' are {}.

1 A \ before a newline will still be substitued.

like image 113
Johannes Kuhn Avatar answered Sep 19 '22 12:09

Johannes Kuhn


As Johannes said, using ' doesn't work.
I had a similar problem, but I wanted to execute more commands and then still get a login shell. I managed to make it work with ":

expect -c "
   set timeout 5;
   spawn ssh -XY $user@$host -t \"cat /etc/motd; bash -l\"
   expect {
      -re \"^Warning.*\" {exp_continue}
      -re \"^.*sword: \" {send \"${PASSWORDS[$user]}\r\"; interact}
   }
"
like image 38
genesys87 Avatar answered Sep 19 '22 12:09

genesys87