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
andrsync
might source your.bashrc