html
When working with remote servers through SSH, maintaining persistent tmux sessions is crucial for workflow continuity. However, existing solutions often fall short when you need to:
- Create a new session when none exists
- Create another session when existing ones are all attached
- Reuse the first available unattached session when possible
The common pattern ssh -t 'tmux a || tmux || /bin/bash' has limitations:
# This always tries to attach first, which isn't what we want
# when we need a fresh session
ssh -t user@server "tmux attach || tmux new"
Here's a bash function that implements intelligent session selection:
function tmux_connect() {
# List all tmux sessions
sessions=$(tmux list-sessions 2>/dev/null)
if [[ -z "$sessions" ]]; then
# No sessions exist - create new
tmux new-session
else
# Find unattached sessions
unattached=$(echo "$sessions" | grep -v "(attached)")
if [[ -n "$unattached" ]]; then
# Use the first unattached session
session_name=$(echo "$unattached" | head -n 1 | cut -d: -f1)
tmux attach -t "$session_name"
else
# All sessions are attached - create new
tmux new-session
fi
fi
}
To use this with SSH, add it to your remote server's shell configuration:
# ~/.bashrc or ~/.zshrc on remote server
alias tmx='tmux_connect' # Makes it available as a command
# Then use with SSH:
ssh -t user@server "source ~/.bashrc && tmux_connect"
For more complex scenarios, here's an enhanced version that limits maximum sessions:
function tmux_connect_adv() {
max_sessions=5
sessions=$(tmux list-sessions 2>/dev/null)
session_count=$(echo "$sessions" | wc -l)
if [[ -z "$sessions" ]]; then
tmux new-session
else
unattached=$(echo "$sessions" | grep -v "(attached)")
if [[ -n "$unattached" ]]; then
session_name=$(echo "$unattached" | head -n 1 | cut -d: -f1)
tmux attach -t "$session_name"
elif [[ $session_count -lt $max_sessions ]]; then
tmux new-session
else
echo "Maximum session limit ($max_sessions) reached"
tmux attach -t $(echo "$sessions" | head -n 1 | cut -d: -f1)
fi
fi
}
For seamless integration, create a dedicated script:
#!/bin/bash
# ~/bin/tmux-connector
# All the session logic here
function tmux_connect() {
# ... previous implementation ...
}
# Execute the function
tmux_connect
Then update your SSH config:
# ~/.ssh/config
Host myserver
HostName server.example.com
User myuser
RequestTTY yes
RemoteCommand ~/bin/tmux-connector
For those who prefer pure tmux solutions:
ssh -t user@server "tmux list-sessions | grep -v attached | head -n 1 | cut -d: -f1 | xargs -I {} tmux attach -t {} || tmux new-session"
When working with remote servers via SSH, many developers want an intelligent tmux behavior that:
- Creates a new session when none exists
- Creates another session when existing ones are all attached
- Automatically attaches to the first available unattached session
The common solution ssh -t 'tmux a || tmux || /bin/bash' has limitations:
# Problematic behavior:
1. Always tries to attach first (tmux a)
2. Only creates new session if attach fails
3. Doesn't check for unattached sessions
Here's a bash script that implements the desired logic:
#!/bin/bash
# Find unattached sessions
unattached_session=$(tmux list-sessions -F '#{session_name}:#{?session_attached,attached,unattached}' 2>/dev/null | \
grep ':unattached$' | \
head -n 1 | \
cut -d: -f1)
if [ -n "$unattached_session" ]; then
# Attach to first unattached session
exec tmux attach -t "$unattached_session"
else
# Create new session with incremental numbering
session_num=1
while tmux has-session -t "session-$session_num" 2>/dev/null; do
((session_num++))
done
exec tmux new -s "session-$session_num"
fi
To use this as your default SSH command:
# In your ~/.ssh/config
Host your-server
HostName server.example.com
User youruser
RequestTTY yes
RemoteCommand /path/to/your/tmux_wrapper.sh
For more control over session naming and behavior:
#!/bin/bash
MAX_SESSIONS=5 # Prevent creating unlimited sessions
# Custom session naming based on date/time
new_session_name() {
echo "dev-$(date +%Y%m%d-%H%M%S)"
}
# Main logic
unattached=$(tmux list-sessions -F '#{session_id}:#{?session_attached,attached,unattached}' | \
awk -F: '$2 == "unattached" {print $1; exit}')
if [ -n "$unattached" ]; then
exec tmux attach -t "$unattached"
else
current_sessions=$(tmux list-sessions 2>/dev/null | wc -l)
if [ "$current_sessions" -ge "$MAX_SESSIONS" ]; then
echo "Maximum sessions ($MAX_SESSIONS) reached"
exec bash # Fallback to regular shell
else
exec tmux new -s "$(new_session_name)"
fi
fi
The solution should account for:
- No tmux server running
- Permission issues
- Broken pipe scenarios
- Terminal size adjustments
Here's a more robust version:
#!/bin/bash
cleanup() {
# Reset terminal settings if something goes wrong
stty sane
}
trap cleanup EXIT
# Check if tmux is available
if ! command -v tmux >/dev/null 2>&1; then
exec bash
fi
# Main logic with error handling
if output=$(tmux list-sessions -F '#{session_id}:#{?session_attached,attached,unattached}' 2>&1); then
unattached_session=$(echo "$output" | \
grep ':unattached$' | \
head -n 1 | \
cut -d: -f1)
if [ -n "$unattached_session" ]; then
exec tmux attach -t "$unattached_session"
else
exec tmux new
fi
else
# If list-sessions failed (no server running)
exec tmux new
fi