Skip to main content

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'"

Format Expansion

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'

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"

Format Expansion

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 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}')

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}"

Forwarding Input

# 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

Format Expansion

# 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"

Using Formats in Scripts

# 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

1
Session Creation Script
2
#!/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
3
Layout Save/Restore
4
# 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