16

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.

Richard
  • 14,642
  • 18
  • 56
  • 77
  • 3
    Have you tried removing the single quotes around 'command argument'? It appears that the root of the problem is it's trying to interpret the entire quoted string as the name of a command, rather than splitting it into command and arguments... – twalberg Sep 20 '12 at 18:58
  • @twalberg that's working. I also tried double quotes, wo/ problem as well. Just out of curiosity, can u explain a bit what's special about single quotes? – Richard Sep 20 '12 at 19:11
  • I'd have to dig out the `expect` docs to see what it says - don't know off the top of my head. I suggested that based on the syntax of the particular error message you were getting... – twalberg Sep 20 '12 at 19:13

3 Answers3

9

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.

Johannes Kuhn
  • 14,778
  • 4
  • 49
  • 73
5

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}
   }
"
genesys87
  • 51
  • 1
  • 3
0

I guess you don't require eval there. Remove it and check.

Check this expect examples link for detailed examples on "how to use expect?" .

Hope this helps.

mtk
  • 13,221
  • 16
  • 72
  • 112