How to Detect Interactive Shell Mode in Bash Scripts for SSH and Non-Interactive Scenarios


3 views

When writing bash scripts or configuring ~/.bashrc, you often need different behaviors for interactive shells versus non-interactive sessions (like SSH command execution). The key challenge is detecting the shell mode to avoid unwanted output or ANSI escape sequences in non-interactive contexts.

Bash provides two reliable ways to detect interactive mode:


# Method 1: Check if PS1 is set
if [[ $- == *i* ]]; then
    echo "Interactive shell detected"
fi

# Method 2: Test for the presence of prompt string
if [ -n "$PS1" ]; then
    echo "Interactive shell with prompt"
fi

Here's how to apply this in your ~/.bashrc to conditionally set aliases, prompts, and colors:


# Only set colorful prompt for interactive shells
if [[ $- == *i* ]]; then
    # ANSI color escape sequences
    PS1='$$\033[01;32m$$\u@\h$$\033[00m$$:$$\033[01;34m$$\w$$\033[00m$$\$ '
    alias ls='ls --color=auto'
else
    # Non-interactive settings
    PS1='\u@\h:\w\$ '
fi

For more precise SSH detection, combine interactive check with SSH environment variables:


if [[ $- == *i* ]] && [ -n "$SSH_CONNECTION" ]; then
    echo "Interactive SSH session detected"
    # Special handling for remote interactive sessions
fi

Another method uses the test -t command to check if stdin is connected to a terminal:


if [ -t 0 ]; then
    echo "Running in interactive mode (terminal input detected)"
else
    echo "Running in non-interactive mode"
fi

Here are common scenarios where this technique proves valuable:


# Example 1: Conditional logging
if [[ $- == *i* ]]; then
    echo "System info: $(uname -a)"
fi

# Example 2: Safe scripting in CI pipelines
if [ -n "$PS1" ]; then
    fancy_prompt_setup
else
    plain_output_mode
fi

Be aware of these special situations:

  • Some commands like scp may trigger .bashrc unexpectedly
  • Cron jobs typically run in non-interactive mode
  • Remote commands via ssh host 'command' are non-interactive

When working with ~/.bashrc configurations, we often need different behaviors for interactive shells versus non-interactive sessions (like SSH command execution). The primary issue arises when ANSI escape sequences meant for terminal formatting get printed during non-interactive operations, causing output corruption.

Here are three robust ways to check for interactive mode in Bash:

# Method 1: Check PS1 variable
if [[ $- == *i* ]]; then
    echo "Interactive shell detected"
fi

# Method 2: Examine terminal characteristics
if [ -t 0 ]; then
    echo "Running in terminal (stdin is a terminal)"
fi

# Method 3: Combined check (most reliable)
if [[ $- == *i* ]] && [ -t 0 ]; then
    # Your interactive-only configuration here
    alias ll='ls -alF --color=auto'
fi

Here's how to implement this in your ~/.bashrc for ANSI control:

# Only set prompt and colors for interactive shells
case $- in
    *i*)
        # Interactive commands
        PS1='$$\033[01;32m$$\u@\h$$\033[00m$$:$$\033[01;34m$$\w$$\033[00m$$\$ '
        alias grep='grep --color=auto'
        ;;
    *)
        # Non-interactive commands
        unset PROMPT_COMMAND
        ;;
esac

For SSH command execution, add an additional check:

if [[ $- == *i* ]] && [ -t 0 ] && [ -z "$SSH_COMMAND" ]; then
    echo "This is an interactive local shell session"
fi

Verify your checks work correctly with these test commands:

# Test interactive mode
bash -i -c 'echo $-'

# Test non-interactive mode
ssh localhost 'echo $-'  # For remote testing
bash -c 'echo $-'
  • Don't rely solely on $TERM variable - it can be set in non-interactive sessions
  • Avoid using tty command as it may fail in certain container environments
  • Remember that scp and rsync might source your .bashrc