When executing commands via SSH, the remote server uses the user's default shell to interpret the command string. This becomes problematic when:
- Your script assumes Bash syntax but users have csh/tcsh as default
- You need consistent behavior across different user environments
- Multi-line commands get mangled by unexpected shell parsing
The most reliable approach is to execute your desired shell directly and pass commands to it:
ssh user@host /bin/bash -s <<'EOF'
# Your multi-line Bash code here
if [ -d "/path" ]; then
echo "Directory exists"
fi
EOF
Key advantages:
-s
makes Bash read commands from stdin- Heredoc (
<<'EOF'
) prevents local shell interpolation - Single quotes around EOF prevent variable expansion
For simpler commands, you can use:
ssh user@host /bin/bash -c "your command here"
Or force a shell via environment variable:
ssh user@host "SHELL=/bin/bash your_command"
For scripts requiring multiple shells or sudo:
ssh user@host /bin/bash -s <<'EOF'
sudo /bin/bash -c '
# Privileged commands here
apt-get update
'
# Regular user commands
echo "Done"
EOF
When implementing this:
- Always specify full paths to shells (/bin/bash not just bash)
- Audit remote systems for available shells
- Consider adding shell checks in your scripts
When executing remote commands via SSH, many developers assume their commands will be interpreted by the shell they specify. However, the reality is more complex:
ssh user@example.com /bin/bash -c 'echo $SHELL'
This seemingly straightforward command faces parsing challenges because the remote user's default shell first processes the entire command string before passing it to your specified shell. This becomes particularly problematic with:
- Multi-line commands
- Complex quoting requirements
- Special character handling
Method 1: Using SSH ForcedCommand
Add this to the remote server's ~/.ssh/authorized_keys
:
command="/bin/bash -c '${SSH_ORIGINAL_COMMAND}'" ssh-rsa AAAAB3... user@client
This forces all SSH commands from this key to use bash, regardless of the user's default shell.
Method 2: Here Document Syntax
Works reliably across different local shells:
ssh user@host /bin/bash <<'EOF'
# Your multi-line commands here
if [[ -f /etc/profile ]]; then
source /etc/profile
fi
env | grep PATH
EOF
Method 3: Base64 Encoded Commands
For maximum compatibility with special characters:
cmd="echo 'complex $string with \"quotes\"'" \
&& ssh user@host "/bin/bash -c \"\$(echo '$(echo "$cmd" | base64)' | base64 --decode)\""
CI/CD Pipeline Example
For Jenkins pipelines where you need consistent shell behavior:
ssh deploy@production /bin/bash <<'DEPLOY_SCRIPT'
set -euo pipefail
git -C /app pull origin main
docker-compose -f /app/docker-compose.prod.yml up -d --build
DEPLOY_SCRIPT
Multi-Node Cluster Management
When running commands across servers with different default shells:
parallel-ssh -h hosts.txt -i "/bin/bash -c '\
uname -a && \
free -h && \
df -h | grep -v tmpfs'"