Introduction to tmux Scripting
tmux provides powerful scripting capabilities that allow you to:
Automate session and window creation
Execute conditional logic
Run shell commands in the background
Display formatted information
Load and organize configuration files
All tmux commands can be scripted, whether from configuration files, shell scripts, or key bindings.
The if-shell Command
The if-shell command executes tmux commands conditionally based on shell command results.
Basic Syntax
if-shell [-bF] [-t target-pane] shell-command tmux-command [else-command]
Simple Conditional
# If shell command succeeds, run tmux command
if-shell "test -f ~/.tmux.local.conf" "source ~/.tmux.local.conf"
# With else clause
if-shell "[ -d /opt/local ]" \
"set-option -g default-command /opt/local/bin/bash" \
"set-option -g default-command /bin/bash"
Background Execution
# Run shell command in background with -b
if-shell -b "sleep 5 && echo done" "display 'Command completed'"
# Without -b, subsequent commands wait for shell command to finish
if-shell "curl -s https://api.example.com" "display 'API available'"
Use -F to treat the shell command as a format string instead of executing it:
# Check if format evaluates to non-empty and non-zero
if-shell -F '#{window_zoomed_flag}' \
'set-option status-style bg=red' \
'set-option status-style bg=green'
# Check session count
if-shell -F '#{>:#{session_windows},5}' \
'display "Too many windows!"'
# String comparison
if-shell -F '#{==:#{host},myserver}' \
'set-option -g status-left "[SERVER] "'
Real-World Examples
# Load different configs based on hostname
if-shell '[ "$(hostname)" = "workstation" ]' \
'source ~/.tmux.work.conf' \
'source ~/.tmux.home.conf'
# Load OS-specific config
if-shell 'uname | grep -q Darwin' \
'source ~/.tmux.macos.conf'
if-shell 'uname | grep -q Linux' \
'source ~/.tmux.linux.conf'
# Enable true color if supported
if-shell '[[ $COLORTERM = truecolor ]]' \
'set-option -ga terminal-overrides ",*256col*:Tc"'
# Check tmux version
if-shell '[ "$(tmux -V | cut -d" " -f2)" -ge "3.2" ]' \
'set-option -g extended-keys on'
# Check for clipboard command
if-shell 'command -v xclip' \
'set-option -g @clipboard xclip' \
'set-option -g @clipboard pbcopy'
# Show different status based on session
if-shell -F '#{==:#{session_name},work}' \
'set-option -g status-style bg=blue' \
'set-option -g status-style bg=green'
# Alert if too many panes
if-shell -F '#{>:#{window_panes},4}' \
'set-option -w window-status-style bg=red'
Nested if-shell
You can nest if-shell commands:
if-shell 'tmux run "true"' \
'if-shell "test -f ~/.tmux.conf" "set -s @done yes"'
Be careful with nested if-shell commands as they can become difficult to read and debug. Consider using format-based conditionals with -F instead.
The run-shell Command
The run-shell command executes shell commands in the background without creating a window.
Basic Syntax
run-shell [-bCE] [-c start-directory] [-d delay] [-t target-pane] shell-command
Simple Execution
# Run command in background
run-shell "sleep 5 && tmux display 'Done'"
# Run immediately (don't wait for prompt)
run-shell -b "curl -s https://api.example.com/notify"
Output Display
By default, command output is shown in view mode:
# Output displayed in current pane
run-shell "ls -la"
# Output displayed in specific pane
run-shell -t %2 "df -h"
# Redirect stderr to stdout
run-shell -E "command-that-may-error 2>&1"
Running tmux Commands
Use -C to run tmux commands instead of shell commands:
# Run tmux command
run-shell -C "display-message 'Hello from run-shell'"
# Chain tmux commands
run-shell -C "split-window ; send-keys 'htop' Enter"
Delayed Execution
# Wait before executing
run-shell -d 5 "tmux display 'Delayed message'"
# Useful for startup sequences
run-shell -d 1 "tmux select-window -t :0"
run-shell -d 2 "tmux split-window -h"
run-shell -d 3 "tmux send-keys 'vim' Enter"
Working Directory
# Run in specific directory
run-shell -c /var/log "tail -n 20 syslog | tmux load-buffer -"
# Use current pane directory
run-shell -c '#{pane_current_path}' "git status"
Practical Examples
# Update status bar periodically
bind-key U run-shell -b \
'while true; do \
tmux set-option -g status-right "$(date)"; \
sleep 60; \
done'
# Fetch and display data
bind-key W run-shell -b \
'weather=$(curl -s "wttr.in?format=%c+%t"); \
tmux set-option -g @weather "$weather"'
# Log errors to file
set-hook -g command-error \
'run-shell "echo \'Command failed \' >> /tmp/tmux-errors.log"'
# Display and log
set-hook -g command-error \
'run-shell -E "echo \' Error: #{error}\' | tee -a ~/tmux.log"'
# Save session layout
bind-key S run-shell \
'tmux list-windows -F "#{window_index}:#{window_name}" > ~/.tmux-snapshot'
# Capture all panes
bind-key C run-shell \
'for pane in $(tmux list-panes -F "#{pane_id}"); do \
tmux capture-pane -p -t $pane > ~/pane-${pane}.txt; \
done'
The display-message Command
The display-message command shows messages and evaluates formats.
Basic Display
# Show message in status line
tmux display-message "Hello, tmux!"
tmux display "Simpler syntax"
# Show for specific duration (milliseconds)
tmux display -d 5000 "This message stays for 5 seconds"
# Show until keypress
tmux display -d 0 "Press any key to continue"
# Print to stdout instead
tmux display -p "Output to terminal"
Display formatted information:
# Show current session info
tmux display "Session: #{session_name}, Windows: #{session_windows}"
# Show pane info
tmux display "Pane #{pane_index}: #{pane_current_command}"
# Multiple variables
tmux display "#{session_name}:#{window_index}.#{pane_index}"
# Print format to stdout
tmux display -p '#{pane_current_path}'
Script Integration
Get Values
Conditional Display
Formatted Output
# Get current path
path = $( tmux display -p '#{pane_current_path}' )
cd " $path "
# Get session name
session = $( tmux display -p '#{session_name}' )
echo "Current session: $session "
# Get window list
windows = $( tmux display -p '#{session_windows}' )
# Show only if condition met
tmux display -p \
'#{?window_zoomed_flag,ZOOMED,normal}'
# Complex conditional
tmux display -p \
'#{?#{>:#{window_panes},1},multi-pane,single-pane}'
# List all sessions with details
tmux display -p \
'#{T:session_name}: #{session_windows} windows, #{session_attached} clients'
# Window list
for i in $( seq 0 9 ); do
tmux display -p -t : $i \
'Window #{window_index}: #{window_name}' 2> /dev/null
done
Verbose Mode and Debugging
# Show format parsing details
tmux display -v "Session: #{session_name}"
# List all format variables
tmux display -a
# Literal output (no format expansion)
tmux display -l "Literal: #{not_a_variable}"
# Forward stdin to empty pane
tmux display-message -I -t %3
# Now type or pipe input
echo "hello" | tmux display-message -I -t %3
The source-file Command
The source-file command loads and executes tmux commands from files.
Basic Usage
# Load configuration file
tmux source-file ~/.tmux.conf
tmux source ~/.tmux.conf # Short form
# Load multiple files
tmux source-file ~/.tmux.conf ~/.tmux.local.conf
# Silent loading (no error if file doesn't exist)
tmux source-file -q ~/.tmux.optional.conf
# Expand format in file path
tmux source-file -F "~/.tmux/#{host}.conf"
# Load config based on session
tmux source-file -F "~/.tmux/#{session_name}.conf"
Parse Without Executing
# Check syntax without running commands
tmux source-file -n ~/.tmux.conf
# Show parsed commands
tmux source-file -nv ~/.tmux.conf
Organized Configuration
Split configuration into multiple files:
# ~/.tmux.conf
source-file ~/.tmux/options.conf
source-file ~/.tmux/bindings.conf
source-file ~/.tmux/status.conf
source-file -q ~/.tmux/local.conf
# General options
set-option -g default-terminal "screen-256color"
set-option -g history-limit 10000
set-option -g mouse on
set-option -g base-index 1
set-option -w pane-base-index 1
# Key bindings
unbind C-b
set-option -g prefix C-a
bind-key C-a send-prefix
bind | split-window -h
bind - split-window -v
bind r source-file ~/.tmux.conf \; display "Reloaded!"
# Status line configuration
set-option -g status-position bottom
set-option -g status-style bg=black,fg=white
set-option -g status-left "[#S] "
set-option -g status-right "%H:%M %d-%b-%y"
Conditional Loading
# Load host-specific config
if-shell '[ -f ~/.tmux.$(hostname).conf ]' \
'source-file ~/.tmux.$(hostname).conf'
# Load based on tmux version
if-shell '[ "$(tmux -V | cut -d" " -f2)" -ge "3.0" ]' \
'source-file ~/.tmux/tmux3.conf' \
'source-file ~/.tmux/tmux2.conf'
Advanced Scripting Patterns
Command Sequences
Chain multiple commands:
# Semicolon separator
tmux new-window ; split-window -h ; split-window -v
# Using \; in shell
tmux new-window \; split-window -h \; split-window -v
# With bind-key
bind-key S split-window -h \; split-window -v
# Error handling (stop on error)
tmux new-window ; invalid-command ; display "This won't run"
# Get information for scripting
pane_path = $( tmux display -p '#{pane_current_path}' )
pane_cmd = $( tmux display -p '#{pane_current_command}' )
session = $( tmux display -p '#{session_name}' )
# Conditional based on format
if tmux display -p '#{window_zoomed_flag}' | grep -q 1 ; then
echo "Window is zoomed"
fi
# Iterate over panes
for pane in $( tmux list-panes -F '#{pane_id}' ); do
cmd = $( tmux display -p -t $pane '#{pane_current_command}' )
echo "Pane $pane running: $cmd "
done
Session Management Scripts
#!/bin/bash
# create-dev-session.sh
SESSION = "dev"
# Check if session exists
tmux has-session -t $SESSION 2> /dev/null
if [ $? != 0 ]; then
# Create new session
tmux new-session -d -s $SESSION -n editor
# Setup editor window
tmux send-keys -t $SESSION :editor 'cd ~/projects' C-m
tmux send-keys -t $SESSION :editor 'vim' C-m
# Create console window
tmux new-window -t $SESSION -n console
tmux split-window -t $SESSION :console -h
# Create server window
tmux new-window -t $SESSION -n server
tmux send-keys -t $SESSION :server 'npm start' C-m
# Select first window
tmux select-window -t $SESSION :editor
fi
# Attach to session
tmux attach-session -t $SESSION
# Save layout
tmux_save_layout () {
local file = " $HOME /.tmux-layout-$( date +%s)"
tmux list-windows -F \
"#{window_index}:#{window_name}:#{window_layout}" > " $file "
tmux list-panes -F \
"#{window_index}.#{pane_index}:#{pane_current_path}:#{pane_current_command}" \
>> " $file "
echo "Layout saved to $file "
}
# Restore layout
tmux_restore_layout () {
local file = " $1 "
# Read window layouts
while IFS = : read -r idx name layout ; do
tmux new-window -n " $name "
tmux select-layout -t : $idx " $layout "
done < <( grep -v '\.' " $file ")
}
Error Handling
# Capture command errors
if ! tmux select-window -t :99 2> /dev/null ; then
echo "Window 99 doesn't exist"
tmux new-window -t :99
fi
# Use hooks for error logging
set-hook -g command-error \
'run-shell "echo #{error} >> ~/.tmux-errors.log"'
# Check exit status
tmux if-shell "test $? -eq 0" \
"display 'Success'" \
"display 'Failed'"
Environment Variables in Scripts
tmux provides useful environment variables:
# Available in panes
echo $TMUX # Socket and session info
echo $TMUX_PANE # Current pane ID
echo $TERM # Terminal type
# Use in scripts
if [ -n " $TMUX " ]; then
echo "Running inside tmux"
pane_id = " $TMUX_PANE "
fi
Setting Environment Variables
# Set for session
tmux set-environment -t mysession MYVAR value
# Global environment
tmux set-environment -g GLOBALVAR value
# Hidden variable (not passed to new processes)
tmux set-environment -h SECRETVAR value
# Use in formats
tmux display -p '#{MYVAR}'
Complete Scripting Example
A comprehensive project setup script:
#!/bin/bash
# project-session.sh - Create a development session
PROJECT_NAME = " ${1 :- myproject } "
PROJECT_DIR = " ${2 :- $HOME / projects / $PROJECT_NAME } "
SESSION = " $PROJECT_NAME "
# Check if session already exists
if tmux has-session -t " $SESSION " 2> /dev/null ; then
echo "Session $SESSION already exists. Attaching..."
tmux attach-session -t " $SESSION "
exit 0
fi
# Create session
tmux new-session -d -s " $SESSION " -n editor -c " $PROJECT_DIR "
# Setup editor window
tmux send-keys -t " $SESSION :editor" 'vim .' C-m
# Create git window
tmux new-window -t " $SESSION " -n git -c " $PROJECT_DIR "
tmux split-window -t " $SESSION :git" -h -c " $PROJECT_DIR "
tmux send-keys -t " $SESSION :git.0" 'git status' C-m
tmux send-keys -t " $SESSION :git.1" 'git log --oneline -10' C-m
# Create server window
tmux new-window -t " $SESSION " -n server -c " $PROJECT_DIR "
tmux split-window -t " $SESSION :server" -v -c " $PROJECT_DIR "
tmux send-keys -t " $SESSION :server.0" 'npm run dev' C-m
tmux send-keys -t " $SESSION :server.1" 'npm run test -- --watch' C-m
# Create monitoring window
tmux new-window -t " $SESSION " -n monitor
tmux send-keys -t " $SESSION :monitor" 'htop' C-m
# Return to editor
tmux select-window -t " $SESSION :editor"
# Set session-specific options
tmux set-option -t " $SESSION " status-left "[ $PROJECT_NAME ] "
tmux set-option -t " $SESSION " @project_dir " $PROJECT_DIR "
# Display info
tmux display -t " $SESSION " "Project session $PROJECT_NAME created"
# Attach
tmux attach-session -t " $SESSION "
Use it:
./project-session.sh myapp ~/dev/myapp