tmux hooks let you run commands automatically when certain events happen within your tmux sessions, like creating a new window or attaching to a session.
Here’s a tmux session in action, showing a new window being created and a hook automatically running a command:
user@host:~ $ tmux new-session -d -s my_session
# (tmux session created in background)
user@host:~ $ tmux attach-session -t my_session
# (tmux attaches to the session)
# In the tmux window, you see:
# (new window created)
# Running: ls -l /
# total 4.0K
# drwxr-xr-x 1 root root 4.0K Jan 1 1970 bin
# drwxr-xr-x 1 root root 4.0K Jan 1 1970 boot
# ... (output of ls -l /)
The power of tmux hooks lies in their ability to automate repetitive tasks, making your workflow smoother and more efficient. Imagine always having a specific set of directories open in new windows, or automatically starting a development server when you attach to your main project session. Hooks make this a reality.
Let’s break down how it works internally. Tmux has a set of predefined events that can trigger actions. These events are declared in your tmux configuration file (usually ~/.tmux.conf) using the set-hook command.
The basic syntax is:
set-hook [-ag] [-F format] [-n ...] event-name command [arguments ...]
-a: Appends the command to existing hooks for that event.-g: Sets the hook globally. Without this, it applies only to the current session.-F format: Allows you to use tmux’s format strings to dynamically insert information about the event into your command.event-name: The specific tmux event you want to hook into (e.g.,window-create,session-attach).command [arguments ...]: The command to execute when the event occurs.
Here are some common events and how you might use them:
-
window-create: This event fires whenever a new window is created in a session. A common use case is to automatically run a command in that new window.# ~/.tmux.conf set-hook -g window-create 'if-shell "echo \"Window created: #{window_name}\""' set-hook -g window-create 'run-shell "ls -l /"'This hook will print a message and then execute
ls -l /in every newly created window. The#{window_name}format string inserts the name of the newly created window into theechocommand. -
session-attach: This hook runs when a client attaches to a session. You could use this to display a welcome message or start a specific process.# ~/.tmux.conf set-hook -g session-attach 'display-message "Welcome back to session: #{session_name}!"' -
session-created: Fires when a new session is created.# ~/.tmux.conf set-hook -g session-created 'display-message "New session \"#{session_name}\" created."' -
client-attached: Runs when a client attaches to any session.# ~/.tmux.conf set-hook -g client-attached 'display-message "Client attached to session #{session_name} on #{client_host}."' -
client-detached: Runs when a client detaches from a session.# ~/.tmux.conf set-hook -g client-detached 'display-message "Client detached from session #{session_name}."' -
window-close: Fires when a window is closed.# ~/.tmux.conf set-hook -g window-close 'display-message "Window \"#{window_name}\" in session \"#{session_name}\" closed."'
The real magic happens when you combine hooks with tmux’s format strings. These strings allow you to inject dynamic information into your hook commands. Some useful ones include:
#{session_name}: The name of the current session.#{window_index}: The index of the current window.#{window_name}: The name of the current window.#{pane_id}: The ID of the current pane.#{pane_current_path}: The current working directory of the pane.#{client_host}: The hostname of the client.
For example, to automatically cd to the current pane’s directory when a new window is created:
# ~/.tmux.conf
set-hook -g window-create 'run-shell "cd #{pane_current_path}"'
This hook uses run-shell to execute a shell command. The #{pane_current_path} format string is crucial here, ensuring that the cd command operates on the correct directory of the pane that triggered the event (which is typically the one where the new window is created). If you want this to apply to every pane within the new window, you might need a more complex script or loop, but for a single pane, this is direct.
One subtlety that often trips people up is the difference between run-shell and display-message. display-message is for showing information to the user directly within tmux’s status line or as a temporary message. run-shell, on the other hand, executes a command in the background or foreground, allowing you to perform actions like starting processes, manipulating files, or changing directories. If you try to cd using display-message, it won’t have any effect on your shell’s working directory.
Another powerful, yet often overlooked, aspect is chaining hooks or using them in conjunction with other tmux commands. For instance, you can create a hook that triggers another tmux command, which in turn might create a new window with a specific layout and then run a command within it. This allows for complex session setup automation.
Consider a hook that sets up a specific development environment when a session named dev is attached:
# ~/.tmux.conf
set-hook -g session-attach 'if-shell "$TMUX_SESSION_NAME" = "dev" "new-window -n \"Backend\" \"cd ~/projects/api && npm start\""; if-shell "$TMUX_SESSION_NAME" = "dev" "new-window -n \"Frontend\" \"cd ~/projects/frontend && npm start\""'
This hook checks if the attached session name is dev. If it is, it creates two new windows: "Backend" with the npm start command for the API, and "Frontend" with the npm start for the frontend. This is a very common pattern for setting up project environments quickly.
The next thing you’ll likely want to explore is how to conditionally execute hooks based on more complex criteria than just the event type, perhaps involving environment variables or the state of running processes.