tmux的脚本化(Scripting){#scripting-tmux}¶
tmux 中的命令行快捷方式和选项比较多。
我整理以一些表格,每个人使用tmux都有点不同,你也可以整理一个你自己使用的表格。这里我尽量包含大家经常使用的,更多的清单可以看 附录清单(cheatsheets)
缩写(Aliases) {#aliases}¶
tmux 命令有很多缩写(alias)。使用缩写,使用 $ tmux attach
命令即可以执行 $ tmux attach-session
命令。缩写主要是方便记忆,不用将整个命令打出来。
Command | Alias |
---|---|
attach-session | attach |
break-pane | breakp |
capture-pane | capturep |
display-panes | displayp |
find-window | findw |
join-pane | joinp |
kill-pane | killp |
kill-window | killw |
last-pane | lastp |
last-window | last |
link-window | linkw |
list-panes | lsp |
list-windows | lsw |
move-pane | movep |
move-window | movew |
new-session | new |
new-window | neww |
next-layout | nextl |
next-window | next |
pipe-pane | pipep |
previous-layout | prevl |
previous-window | prev |
rename-window | renamew |
resize-pane | resizep |
respawn-pane | respawnp |
respawn-window | respawnw |
rotate-window | rotatew |
select-layout | selectl |
select-pane | selectp |
set-option | set |
set-window-option | setw |
show-options | show |
show-window-options | showw |
split-window | splitw |
swap-pane | swapp |
swap-window | swapw |
unlink-window | unlinkw |
If you know the full name of the command, if you were to chop the hyphen (-) from the command and add the first letter of the last word, you’d get the shortcut, e.g., swap-window is swapw, split-window is splitw.
Pattern matching {#fnmatch}¶
In addition to aliases, tmux commands and arguments may all be accessed via fnmatch(3)
patterns.
For instance, you need not type $ tmux attach-session
every time. First, there’s the alias of $ tmux attach
, but additionally, more concise commands can be used if they partially match the name of the command or the target. tmux’s pattern matching allows $ tmux attac
, $ tmux att
, $ tmux at
and $ tmux a
to reach $ tmux attach
.
Every tmux command has shorthands; let’s try this for $ tmux new-session
:
$ tmux new-session
# ...
$ tmux new-s
and so on, until:
$ tmux new-
ambiguous command: new-, could be: new-session, new-window
The limitation, as seen above, is command matches can collide. Multiple commands begin with new-
. So, if you wanted to use matches, $ tmux new-s
for a new session or $ tmux new-w
for a new window would be the most efficient way. But, the alias of $ tmux new
for new session and $ tmux neww
for new windows is even more concise than matching, since the special alias exists.
Patterns can also match targets with window and session names. For instance, a session named mysession
can be matched via mys
:
$ tmux attach -t mys
Matching targets will fail if a pattern matches more than one item. If 2 sessions exist, named mysession
and mysession2
, the above command would fail. To target either session, the complete target name must be specified.
Targets {#targets}¶
If a command allows target specification, it’s usually done through -t
.
Think of targets as tmux’s way of specifying a unique key in a relational database.
Entity | Prefix | Example |
---|---|---|
server | n/a | n/a, uses socket-name and socket-path |
client | n/a | n/a, uses /dev/tty{p,s}[000-9999] |
session | $ | $13 |
window | @ | @2313 |
pane | % | %5432 |
What I use to help me remember:
So, sessions are represented by dollar signs ($) because they hold your projects (ostensibly where you make money or help someone else do it).
Windows are represented by the at sign (@). So, windows are like referencing / messaging a user on a social networking website.
Panes are the fun one, represented by the percent sign (%), like the default prompt for csh and tcsh. Hey, makes sense, since panes are pseudoterminals!
When scripting tmux, the symbols help denote the type of object, but also serve as a way to target something deeply, such as the pane, directly, without needing to know or specify its window or session.
Here are some examples of targets, assuming one session named mysession
and a
client at /dev/ttys004
:
attach-session [-t target-session]
¶
$ tmux attach-session -t mysession
detach-client [-s target-session] [-t target-client]
¶
$ tmux detach-client -s mysession -t /dev/ttys004
# If within client, -t is assumed to be current client
$ tmux detach-client -s mysession
has-session [-t target-session]
¶
$ tmux has-session -t mysession
# Pattern matching session name
$ tmux has-session -t mys
$ tmux kill-session [-t target-session]
¶
$ tmux kill-session -t mysession
$ tmux list-clients [-t target-session]
¶
$ tmux list-clients -t mysession
$ tmux lock-client [-t target-client]
¶
$ tmux lock-clients -t /dev/ttys004
$ tmux lock-session [-t target-session]
¶
$ tmux lock-session -t mysession
$ tmux new-session [-t target-session]
¶
$ tmux new-session -t newsession
# Create new-session in the background
$ tmux new-session -t newsession -d
$ tmux refresh-client [-t target-client]
¶
$ tmux refresh-client -t /dev/ttys004
$ tmux rename-session [-t target-session]
session-name¶
$ tmux rename-session -t mysession renamedsession
# If within attached session, -t is assumed
$ tmux rename-session renamedsession
$ tmux show-messages [-t target-client]
¶
$ tmux show-messages -t /dev/ttys004
$ tmux suspend-client [-t target-client]
¶
$ tmux suspend-client -t /dev/ttys004
# If already in client
$ tmux suspend-client
# Bring client back to the foreground
$ fg
$ tmux switch-client [-c target-client] [-t target-session]
¶
$ tmux suspend-client -c /dev/ttys004 -t othersession
# Within current client, -c is assumed
$ tmux suspend-client -t othersession
Formats {#formats}¶
tmux provides a minimal template language and set of variables to access information about your tmux environment.
Formats are specified via the -F
flag.
You know how template engines, such as mustache, handlebars ERB in ruby, jinja2 in python, twig in PHP, and JSP in Java, allow template variables? Formats are a similar concept.
The FORMATS
(variables) provided by tmux have expanded greatly since version 1.8. Some of the most commonly used formats as of tmux 2.3 are listed below. See the appendix section on formats for a complete list.
Let’s try to output it:
$ tmux list-windows -F "#{window_id} #{window_name}"
> @0 zsh
Here’s a cool trick to list all panes with the x and y coordinates of the cursor position:
$ tmux list-panes -F "#{pane_id} #{pane_current_command} \
#{pane_current_path} #{cursor_x},#{cursor_y}"
> %0 vim /Users/me/work/tao-of-tmux/manuscript 0,34
%1 tmux /Users/me/work/tao-of-tmux/manuscript 0,17
%2 man /Users/me/work/tao-of-tmux/manuscript 0,0
Variables are specific to the objects being listed. For instance:
Server-wide variables: host
, host_short
(no domain name), socket_path
, start_time
and pid
.
Session-wide variables: session_attached
, session_activity
, session_created
, session_height
, session_id
, session_name
, session_width
, session_windows
and all server-wide variables.
Window variables: window_activity
, window_active
, window_height
, window_id
, window_index
, window_layout
, window_name
, window_panes
, window_width
and all session and server variables.
Pane variables: cursor_x
, cursor_y
, pane_active
, pane_current_command
, pane_current_path
, pane_height
, pane_id
, pane_index
, pane_width
, pane_pid
and all window, session and server variables.
This book focuses on separating the concept of server, sessions, windows, and panes. With the knowledge of targets and formats, this
separation takes shape in tmux’s internal attributes. If you list-panes
all
variables up the ladder, including window, session and server variables are
available for the panes being listed. Try this:
$ tmux list-panes -F "pane: #{pane_id}, window: #{window_id}, \
session: #{session_id}, server: #{socket_path}"
> pane: %35, window: @13, session: $6, server: /private/tmp/tmux-501/default
pane: %38, window: @13, session: $6, server: /private/tmp/tmux-501/default
pane: %36, window: @13, session: $6, server: /private/tmp/tmux-501/default
Listing windows isn’t designed to display variables for pane-specific properties. Since a window is a collection of panes, it can have 1 or more panes open at any time.
$ tmux list-windows -F "window: #{window_id}, panes: #{window_panes} \
pane_id: #{pane_id}"
> window: @15, panes: 1 pane_id: %40
window: @13, panes: 3 pane_id: %36
window: @25, panes: 1 pane_id: %50
This will show the window ID, prefixed by an @
symbol, and the number of panes
inside the window.
Surprisingly, pane_id
shows up via list-windows
, as of tmux 2.3. While this
output occurs in this version of tmux, it’s undefined behavior. It’s advised to
keep use of -F
scoped to the objects being listing when scripting to avoid
breakage. For instance, if you want the active pane, use #{pane_active}
via
$ tmux list-panes -F "#{pane_active}"
.
By default, list-panes
will only show panes in a window, unless you specify
-a
to output all on a server or -s [-t session-name]
for all panes in a
session:
$ tmux list-panes -s -t mysession
> 1.0: [176x29] [history 87/2000, 21033 bytes] %0
1.1: [87x6] [history 1814/2000, 408479 bytes] %1 (active)
1.2: [88x6] [history 1916/2000, 464932 bytes] %2
2.0: [176x24] [history 9/2000, 2262 bytes] %13
2.1: [55x11] [history 55/2000, 7395 bytes] %14
And the -t
flag lists all panes in a window:
$ tmux list-panes -t @0
> 0: [176x29] [history 87/2000, 21033 bytes] %0
1: [176x36] [history 1790/2000, 407807 bytes] %1 (active)
2: [88x6] [history 1916/2000, 464932 bytes] %2
The same concept applies to list-windows
. By default, The -a
flag will list
all windows on a server, -t
lists windows within a session, and omitting -t
will only list windows within the current session inside tmux.
$ tmux list-windows
> 1: zsh* (3 panes) [176x36] [layout f9a4,176x36,0,0[176x29,0,0,0,176x6,0,30{87x6,0,30,1,88x6,88,30,2}]] @0 (active)
2: zsh- (5 panes) [176x36] [layout 55ef,176x36,0,0[176x24,0,0,13,176x11,0,25{55x11,0,25,14,58x11,56,25[58x7,56,25,16,58x3,56,33,17],61x11,115,25,15}]] @6
Controlling tmux {#send-keys}¶
tmux allows sending keys, including Ctrl via C-
or ^
, alt (Meta) via M-
,
and special key names. Here’s a list of special keys straight from the manual:
Up
, Down
, Left
, Right
, BSpace
, BTab
, DC
(Delete), End
, Enter
,
Escape
, F1
to F12
, Home
, IC
(Insert), NPage
/PageDown
/PgDn
,
PPage
/PageUp
/PgUp
, Space
, and Tab
.
If special keys are not matched, the defined behavior is to send it as a string to the pane, character by character.
For this example, we will use send-keys
through tmux prompt, because omitting
target (-t
) will direct the command to the current pane, but the keys sent will
sometimes print before the prompt.
Open tmux command prompt via Prefix
+ :
and type this after the :
:
send-keys echo 'hi'
Hit enter. This inserted hi into the current active pane. You can also use targets to specify which pane to send it to.
Let’s now try to send keys to another pane in our current window. Create a second pane via splitting the window if one doesn’t exist. You can also do this exercise outside of tmux or inside a scripting file and running it.
Grab a pane ID from the output of list-panes
:
$ tmux list-panes
> 0: [180x57] [history 87/2000, 21033 bytes] %0
1: [89x14] [history 1884/2000, 509864 bytes] %1 (active)
2: [90x14] [history 1853/2000, 465297 bytes] %2
%2
looks good. Replace %2
with the pane you want to target. This sends cal
to the input:
$ tmux send-keys -t %2 'cal'
Nice, let’s cancel that out by sending a SIGINT
:
$ tmux send-keys -t %2 'C-c'
This cancelled the command and brought up a fresh input. This time, let’s send
an Enter keypress to run cal(1)
.
$ tmux send-keys -t %2 'cal' 'Enter'
This outputs in the adjacent pane.
Top-left: Listing panes, Bottom-left: Sending keys to right pane, Right: Output of cal(1).
Capturing pane content {#capture-pane}¶
$ tmux capture-pane
will copy a panes’ contents.
By default, the contents will be saved to tmux’s internal clipboard, the paste
buffer. You can run capture-pane
within any pane, then navigate to an
editor, paste the contents (don’t forget to :set paste
and go into insert mode
with i
in vim), and save it to a file. To paste, use Prefix
+
]
inside the pane you’re pasting into.
You can also add the -p
flag to print it to stdout.
From there, you could use redirection
to place the output into a file. Let’s do >>
so we don’t accidentally
truncate a file:
$ tmux capture-pane -p >> ./test
As an alternative to redirection, you can also use save-buffer
. The -a
flag
will get you the same behavior as appended output direction.
$ tmux save-buffer -a ./test
To check what’s inside:
$ cat ./test
Like with send-keys
, targets can be specified with -t
. Let’s
copy a pane into tmux’s clipboard (“paste buffer”) and paste it into a text
editor in a third pane:
Top-left: Listing panes, Bottom-left: Capturing pane output of top-left pane, Right: Pasting buffer into vim.
Remember, you can also copy, paste, and send-keys to other windows and sessions also. Targets are server-wide.
Summary¶
tmux has a well-devised and intuitive command system, enabling the user to
access bread and butter functionality quickly. At the same time, tmux provides
a powerful way of retrieving information on its objects between list-panes
,
list-windows
and list-sessions
and formats. This makes tmux not only
accessible and configurable, but also scriptable.
The ability to retrieve explicitly and reliably, from a session down to a pane. All it takes is a pane’s ID to capture its contents or even send it keys. Used by the skilled programmer, scripting tmux can facilitate orchestrating terminals in ways previously deemed unrealistic; anything from niche shell scripts to monitor and react to behavior on systems to high-level, intelligent and structured control via object oriented libraries, like libtmux.
In the next chapter, we delve into optimizations that showcase the latest generation of unix tools that build upon old, time-tested concepts, like man pages and piping, while maintaining portability across differences in platforms and graceful degradation to ensure development tooling works on machines missing optional tools. Also, the chapter will introduce session managers, a powerful, high-level tool leveraging tmux’s scripting capabilities to consistently load workspace via a declarative configuration.