user1777907
user1777907

Reputation: 1375

Expect / Bash: Running a bash if statement in expect

I am trying to ssh into a server, and check if id_rsa.pub exists. If it does not exist, I would like to run ssh-keygen, otherwise I don't want to do anything.

I can run simply commands like "cat" or "rm" using send in Tcl/Expect, but when I try to do something like this it does not work:

send "if [ ! -f $USER_SSH_PATH/id_rsa.pub ]; then CREATE_FILE=true; fi\r"
expect "# " { }

send "if $CREATE_FILE; then; ssh-keygen -t rsa -C $USER -f $USER_SSH_PATH/id_rsa\r"
...

I get the following error message: # invalid command name "!" while executing "! -f /root/.ssh/id_rsa.pub " invoked from within

Is it possible to run such a command please?

Upvotes: 0

Views: 1610

Answers (4)

Johannes Kuhn
Johannes Kuhn

Reputation: 15163

As slebetman already pointed out, the characters [, ], $ and \ have a special meaning inside ".

  • [ and ] are used for command subsitution, so [expr {2+3}] would be replaced by 5 (the result).
  • $ is used for variable substitution.
  • \ is used for special characters (like \r for the character with the code point 13)

You can either escape those characters inside " with \: I assume that $USER_SSH_PATH is a local (tcl) variable

send "if \[ ! -f $USER_SSH_PATH/id_rsa.pub \]; then CREATE_FILE=true; fi\r"

or use { and } to surround your string.
If you use { and } no substitution will be done. This also includes that \r will be send as \r to the server, and not as single character.

There are a few other options to create such things:

  • subst can do the substitution on a string. It is also possible that only certain kinds of substitution take place (use the -nocommands switch to prevent [] substitution, -novariables for $ substitution and -nobackslashes for \)
    Example:

    send [subst -nocommands \
        {if [ ! -f $USER_SSH_PATH/id_rsa.pub ]; then CREATE_FILE=true; fi\r}]
    
  • string map to replace certain sequences.
    Example

    send [string map [list {@@r@@} \r {@@USER_SSH_PATH@@} $USER_SSH_PATH] \
       {if [ ! -f @@USER_SSH_PATH@@/id_rsa.pub ]; then CREATE_FILE=true; fi@@r@@}]
    

There are many ways to do such things.

Upvotes: 1

slebetman
slebetman

Reputation: 113866

You need to escape the square brackets because it's part of tcl syntax:

send "if \[ ! -f $USER_SSH_PATH/id_rsa.pub \]; then CREATE_FILE=true; fi\r"

In tcl, [] works the same way `` or $() works in bash. So what's happening is that before executing the send command the interpreter first does a substitution on the string and it sees two things it needs to substitute:

  1. $USER_SSH_PATH gets substituted with the value contained in the variable USER_SSH_PATH

  2. [! ....] gets substituted with the result of calling the ! function/command. Which as you discovered doesn't exist.

Note that in tcl (and by extension expect) variable and function names aren't limited to alphanumeric. It's valid to have variable and function names like $ or ! or even the NUL character (a byte with the value of 0):

proc ! {} {puts "!"}
proc "$" {} {puts "\$"}
proc \0 {} {puts "NUL character"}

!   ;# this prints !
\$  ;# this prints $
\0  ;# this prints "NUL character"

See http://www.tcl.tk/man/tcl/TclCmd/Tcl.htm for more information on tcl syntax.

Upvotes: 4

user1777907
user1777907

Reputation: 1375

One solution I just thought about is to run "ls /root/.ssh/ | grep id_rsa.pub" in the send and look at the output with expect. I am curious how I could have run bash if commands though.

Either I'll get "id_rsa.pub" in return, or empty line. But if I have "id_rsa.pub*" it will break.

Anyone has an idea to improve this please? Thanks!

Upvotes: 0

iamauser
iamauser

Reputation: 11469

What's in your $USER_SSH_PATH ? Is it the same in your local and remote machine, meaning you have the right permission... The following may help you debug.

echo ${USER_SSH_PATH}
ssh <hostname> "
if [ ! -f ${USER_SSH_PATH}/id_rsa.pub ]; then
   echo ${USER_SSH_PATH};
   echo \"File Not Found, Creating....\";
   ssh-key-gen -t rsa -C ${USER} -f ${USER_SSH_PATH}/id_rsa;
else
   echo \"File Found\";
   exit 1;
fi
"

Upvotes: 0

Related Questions