How to Execute Pre-Session Commands Before SSH Interactive Shell Login


35 views

When working with SSH connections, we often face a binary choice: either get a standard interactive shell in the default environment, or execute a single remote command non-interactively. Many developers need something in between - an interactive session that executes specific initialization commands first.

The typical workarounds have limitations:

ssh user@host "cd /project; bash --login"  # Loses some shell features
ssh -t user@host "source env.sh; bash"    # May break session control

Neither properly preserves the full interactive shell experience while allowing pre-session commands.

1. Forced Command with Environment Preservation

This approach maintains full shell functionality:

ssh -t user@host '
  cd /your/target/directory
  export CUSTOM_VAR=value
  exec bash --login
'

2. SSH Config with LocalCommand

Add to ~/.ssh/config:

Host myserver
  HostName server.example.com
  User developer
  LocalCommand ssh -t %h "cd /projects/%(n); exec bash --login"

3. Dynamic .bashrc Modification

Create a wrapper script that temporarily modifies .bashrc:

function ssh-pre() {
  local cmd="$(printf '%q ' "$@")"
  ssh -t $1 "echo 'eval $cmd' >> ~/.bashrc.tmp && 
            trap 'rm ~/.bashrc.tmp' EXIT && 
            bash --rcfile ~/.bashrc.tmp -i"
}

For complex environments:

ssh -t dbadmin@prod-db '
  . /etc/database-profile
  cd /var/lib/pgsql
  exec bash --norc --noprofile -i'
  • Always use -t for pseudo-terminal allocation
  • exec bash replaces the current shell process
  • Consider SSH ControlMaster for multiple commands

Many developers face this common scenario: you need to SSH into a server but want certain commands to execute automatically before the interactive shell starts. Standard solutions like .bashrc modifications aren't ideal when you need session-specific initialization.

The obvious approach using ssh "commands; bash" fails because:

  • Environment variables set in the command sequence don't persist
  • The shell doesn't load your normal profile/rc files
  • Terminal settings and job control behave differently

1. SSH RemoteCommand with Login Shell

Modern OpenSSH (7.6+) supports the RemoteCommand option:

ssh -t user@host \
  -o RemoteCommand="cd /project/dir && export ENV_VAR=value; exec \$SHELL -l"

Key points:

  • -t forces pseudo-terminal allocation
  • exec \$SHELL -l starts a proper login shell after commands
  • Environment variables persist because we're in the same process

2. SSH Config with Local Command Sequences

For reusable setups, add to ~/.ssh/config:

Host myserver
  HostName server.example.com
  User devuser
  RequestTTY yes
  RemoteCommand cd /projects/current && export DEBUG=1 && exec bash -l

3. Environment Forwarding Alternative

When you just need environment variables:

ssh -o SendEnv=MY_VAR user@host
# Then in remote ~/.ssh/environment:
MY_VAR=value

Requires PermitUserEnvironment yes in server's sshd_config.

For development environments where you switch between projects:

function ssh-project() {
  local project_dir="/projects/$1"
  ssh -t user@host \
    -o RemoteCommand="cd $project_dir && source envsetup.sh && exec \$SHELL -l"
}

# Usage:
ssh-project my_current_project

If things don't work as expected:

  • Check echo $0 - should show -bash (login shell)
  • Verify ssh -V shows at least OpenSSH 7.6
  • Test with simple commands first (echo test)