How to Execute Commands via SSH and Maintain Persistent Session Connection


2 views

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:

  1. The command executes remotely then exits
  2. 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