Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems. It's 100% free, no registration required.

Sign up
Here's how it works:
  1. Anybody can ask a question
  2. Anybody can answer
  3. The best answers are voted up and rise to the top

My command line/bash fu is extremely weak, but I'm trying to hone my abilities. Hopefully someone can help.

I wrote a super simple expect script so that I can SSH in to a headless box we're using at work, while also sending in the password (for reasons that I am unable to expand upon, we can't use public keys to automate logging in). The script:

#!/usr/bin/expect -f
set timeout 30
spawn ssh -X ***@ $argv
match_max 100000
expect "*?assword:*"
send -- "***\r"

The box has a developer environment and Eclipse installed; most of the time that I'm connecting to it, I will end up tunneling straight in to Eclipse to work on some code that we keep on this box, so I usually end up using script eclipse.

How can I pass the & flag in to expect so that it sees the ampersand as an argument for the ssh session I'm spawning ? When I use script eclipse & it just backgrounds script on my machine instead of doing something on the remote machine, but I'd like to be able to send the & in to script so that when Eclipse launches on the remote box I can still use the box's shell.

This is probably an incredibly silly question, but any help would be greatly appreciated.

Update: I figured out escaping the ampersand (which was definitely a "no duh" moment for me... meh). But this leads to a new problem. It seems that when I pass any arguments to my script, the expect command never allows me to interact; once the passed command executes the SSH session terminates. I'm guessing that this isn't a limitation of expect but a part of the behavior of ssh. Thanks for your answers though.

Update 2: Just for the sake of posterity, I got the behavior I wanted by modifying the script to look like this:

#!/usr/bin/expect -f
set timeout 30
spawn ssh -X ***@
match_max 100000
expect "*?assword:*"
send -- "***\r"
send $argv
send -- "\r"
share|improve this question
The equivalent of regular, repeated open-heart surgery is modus-operendi for your team having installed eclipse in your robot's brain and regularly running it remotely using a scripted password -- yet you can't add ssh keys? Just when I thought I'd seen everything. – Caleb Jun 30 '11 at 7:31
OR you could just use the ssh -f flag to fork it into a background process. :) – user8774 Jul 2 '11 at 4:04
@Caleb it looks like it's okay for me to say it now; it's because the machine will eventually belong to NASA but I work from my own personal laptop. @simplicityFirst I wanted to background Eclipse, not the ssh connection. – Doug Stephen Jul 2 '11 at 16:39
@Doug: Oh that makes me feel much better :-P Next you're going to tell me that the robot is actually the rover Opportunity and the reason you can't add your public key is that there isn't enough space left on it's internal memory! – Caleb Jul 2 '11 at 16:51
@Caleb ba-dum-psh. It's actually Robonaut 2. – Doug Stephen Jul 2 '11 at 22:29
up vote 3 down vote accepted

Geekosaur is right in pointing out that you can pass the ampersand by quoting it, (you could also escape it with like script eclipse \& ) but there is something else we could maybe suggest a better way of doing here. It seems you have written an expect script to ssh into another machine with your password and execute a command.

You should know that from both a practical standpoint and a security standpoint this is not a very good solution. Practically, the whole situation is brittle and several potential situations could cause it to break, and debugging is confusing down the road. From a security standpoint it's crazy because your system password on another machine is saved in a text file somewhere!

There is a better way! Key based authentication was built to solve this issue. You create key pairs --one part goes on the server on one on the client-- that are allowed to authenticate without a password. This allows you to run ssh from scripts without using expect AND without saving your password anywhere. As an extra step you could even encrypt the local key with a pass-phrase and use an agent or keyring manager to load the key into a shell before you run any scripts that will need to use it.

Edit: I missed that you said you can't use public key authentication. That's crazy talk. I'm leaving my answer up for the benefit of any readers who might have also missed the requirement and don't realize that your script is a hack :)

share|improve this answer
I know about key based authentication, but you'll notice in my question I said that there are reasons that I'm not at liberty to discuss that prevents me from using keys. I'm pretty familiar with setting up keys for SSH logins and I wish I could just use that. – Doug Stephen Jun 29 '11 at 19:38
Didn't notice you were escaping the space as well. While I don't think this would make the command not work, it's not necessary. Reflected this in a reply to your comment below too. – Doug Stephen Jun 29 '11 at 20:02
@Doug: It would break in the same way that geekosaur's solution broke. It's not that one or the other format isn't working inside expect, it's that not quoting/escaping the space meant the data got sent as two separate arguments instead of one and expect had different data to work with. I only escaped the space to have the same affect as geekosaur's answer which I assumed would work. It's only when you said it failed that I thought of mentioning the alternative. – Caleb Jun 29 '11 at 20:05
gotcha. I understand what you're saying now. – Doug Stephen Jun 29 '11 at 20:06

If you didn't have to type the password, you could do

ssh -t 'eclipse & exec bash'

The -t flag tells ssh to allocate a terminal even though you're giving it a command name. The remote shell has to be non-interactive because it's executing a command, but the last thing it does is exec bash, i.e. replace itself by a new shell instance, which is interactive. (Replace bash by zsh or whatever your favorite shell is.)

If you need to type a password, you do need to type it in a terminal of some kind (which is why you need expect if you're going to automate that part). ssh deliberately refuses to read passwords from a command line argument or file.

One thing that may be useful is to create a master SSH connection once and for all, the piggyback onto it. As long as the master is running, you can establish slave connections that don't require any authentication. Put the following lines in your ~/.ssh/config:

Host nickname

Create the master connection with ssh -MX nickname eclipse (inside expect, if you still want to pass the password automatically). Then, as long as Eclipse is running, you can do ssh nickname without re-authenticating.

share|improve this answer
+1 for suggesting ControlMaster as a way to get around not having key based authentication as an option for scripting. Client side fix for unfixable server-side problem. – Caleb Jun 29 '11 at 20:51
Neat approach, I may end up using both; only because after I launch Eclipse and do some work, I tend to close it but remain connected to the box. The headless box is actually a robot brain that we develop directly on instead of cross compiling so it's a pony that does far too many tricks for a narrow scope solution; that's why I tried to make such a generic script. But it's a good solution for long periods of time in the dev phase. Also, thanks for adding the expect tag. – Doug Stephen Jun 29 '11 at 22:19
@Doug You can replace eclipse by sleep 999999999 to have an effectively infinitely long master connection. – Gilles Jun 29 '11 at 22:22

Use quoting: script 'eclipse &'. (Note that you do not quote $argv in the expect script; Tcl follows different rules from sh.)

share|improve this answer
This causes the bash shell to spit out a syntax error on the remote end because expect seems to turn the single quotes in to curly braces. I know that I could isolate each element in the argument vector but I'd prefer passing the entire vector in order to make the script generic. It appears to work if I escape the ampersand. But the new problem now seems to be that if I pass arguments, the script never makes it to interact; as soon as passed commands execute the SSH session ends. Updating my question to reflect this. – Doug Stephen Jun 29 '11 at 19:37
@Doug: Did you try the escape method in my answer instead of quoting? If that doesn't work I'd suggest a wrapper script on the remote side to do the backgrounding or change your arguments to ssh so you don't have to background at all. – Caleb Jun 29 '11 at 19:43
Mmm, yes, Tcl might well do that. Now I have to try to remember how to make it behave itself; only thing occurring to me off the top of my head is [puts $argv] in place of $argv, but boy is that ugly. (Hm, actually "$argv" might work there come to think of it. Don't remember Tcl quoting rules and the book is long lost in a move.) – geekosaur Jun 29 '11 at 19:46
@Caleb yeah, I did... It actually dawned on me before you popped up and said it. So it works as expected now, except for the caveat I put in my edit. – Doug Stephen Jun 29 '11 at 19:47
@Caleb: Tclsh is expanding the variable fully quoted, Tcl style, because of the space; changing how you escape it going in won't help because Tcl never sees the difference between the two forms. (The shell strips both of them before passing it in.) – geekosaur Jun 29 '11 at 19:47

Your Answer


By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.