Many developers encounter this scenario: you need to SSH into a remote server, execute specific commands, but want to maintain the interactive session afterward. Standard SSH command execution terminates the connection immediately after command completion.
The Expect script provided attempts to solve this by:
#!/usr/bin/expect -f
set password [lrange $argv 0 0]
spawn ssh root@marlboro "cd /tmp; ls -altr | tail"
expect "?assword:*"
send -- "$password\r"
send -- "\r"
interact
However, this approach has two fundamental issues:
- The command executes remotely then exits
- The final
interact
has nothing to interact with
Method 1: Using SSH with Interactive Shell
This approach chains commands while keeping the shell alive:
ssh -t user@host "cd /target/directory && exec \$SHELL -l"
The -t
flag forces pseudo-terminal allocation, and exec \$SHELL -l
spawns a new login shell.
Method 2: Command Execution Followed by Shell
Execute your command then drop into interactive mode:
ssh -t user@host "cd /tmp && ls -altr | tail; bash --login"
Method 3: Advanced Expect Script
Here's a corrected Expect implementation:
#!/usr/bin/expect -f
set password [lindex $argv 0]
spawn ssh -t user@host "cd /tmp && ls -altr | tail; exec bash -i"
expect {
"password:" { send "$password\r"; exp_continue }
eof { interact }
}
- tmux/screen: Execute commands within persistent terminal multiplexers
- SSH Config: Use
RemoteCommand
in~/.ssh/config
- Nested SSH: Chain SSH sessions with command execution followed by shell
For automated deployments where you need to verify results interactively:
ssh -t deploy@prod-server \
"cd /app && git pull && bundle install && \
sudo systemctl restart app.service; \
exec bash --login"
Many developers face this common scenario: You need to SSH into a remote server, execute specific commands, but maintain the interactive session afterward. Standard SSH command execution terminates the connection immediately after completing the command.
The Expect script approach shown in the question fails because:
spawn ssh root@marlboro "cd /tmp; ls -altr | tail"
The quoted command string makes SSH execute the commands in non-interactive mode, then exit. Even with interact
, the connection terminates because the remote shell completes execution.
Here are three proven approaches:
1. Using SSH with Interactive Shell
ssh -t user@host "cd /target/directory && exec \$SHELL -l"
The -t
flag forces pseudo-terminal allocation, and exec \$SHELL -l
launches a new login shell after command execution.
2. Modified Expect Script
#!/usr/bin/expect -f
set password [lindex $argv 0]
spawn ssh -t user@host "cd /target/dir && bash --login"
expect "password:"
send "$password\r"
interact
3. SSH Config with RemoteCommand
Add to ~/.ssh/config
:
Host myserver
HostName server.example.com
User myuser
RemoteCommand cd /target && bash -l
RequestTTY force
For complex automation:
Environment Preservation
ssh -t host "cd /path && export MY_VAR=value && exec bash --noprofile --norc"
Command Chaining
ssh -t host "command1 && command2; exec bash -i"
For a deployment workflow:
ssh -t deploy@production \
"cd /var/www/app \
&& git pull origin main \
&& npm install \
&& pm2 restart app.js \
&& exec bash -l"
- Always use
-t
for terminal allocation - Ensure remote
.bashrc
doesn't contain exit commands - Use
exec
to replace current shell process - Test with simple commands before complex scripts