Tmux Tips
Table of Contents
- Attach or create automatically
- Temporarily "maximize" a pane
- Automatically attach to a session on login
- Resize current pane without mouse
- Share "clipboard" between Emacs and Tmux
- Populate the "clipboard"
- Share windows among sessions
- Share sessions with another account
- Rescue
- Environment
- My configure
- An example of scripting tmux
Attach or create automatically
Attach to an existing tmux session if there is already one. Otherwise, creating one.
function tg { # tmux go! # Attach to a session. If it does not exist, create that session. If no # session name specified, prompt to choose one from existing ones. # A simple/naive replacement of this "bloated" function: # tmux -2 attach -t "$session_name" || tmux -2 new -s "$session_name" local usage="tg [-d] [session_name]" local detach_others="" while getopts "d" opt; do case $opt in d) detach_others="-d";; ?) echo $usage >&2;; esac done shift $((OPTIND - 1)) local session_name="$1" if [ -n "$session_name" ]; then tmux -2 attach $detach_others -t "$session_name" \ || tmux -2 new -s "$session_name" return fi # No session name specified, act according to the number of sessions local sessions=$(tmux list-sessions -F "#{session_name}") if [ -z "$sessions" ]; then # No session, create a default one tmux -2 new -s 'misc' return fi if [ $(echo "$sessions" | wc -l) -eq 1 ]; then # Only one session existing, attach to it without prompt tmux -2 attach $detach_others -t "$sessions" return fi # Multiple sessions, prompt to choose one local IFS=$'\n' # In case session names contain whitespaces. Must 'local' to # NOT pollute the global 'IFS'. $'LITERAL_STR' => ansi-c # quoting local PS3="Select a session: " select session_name in $sessions; do if [ -n "$session_name" ]; then # A valid choice tmux -2 attach $detach_others -t "$session_name" return else echo "Invalid index '$REPLY', please retry" fi done }
Temporarily "maximize" a pane
Tmux 1.8 or above have built-in support ('Prefix + z')
This is for older tmux from http://superuser.com/questions/238702/maximizing-a-pane-in-tmux
unbind + bind-key + run "if [[ $(tmux list-window) =~ MAX ]]; then \ tmux last-window;\ tmux swap-pane -s MAX.0; \ tmux kill-window -t MAX; \ else tmux new-window -d -n MAX; \ tmux swap-pane -s MAX.0; \ tmux select-window -t MAX;fi"
Automatically attach to a session on login
There are a handful of ways to achieve this, each of which affects different set of clients. Choose per your need/trade-off.
- To do it once
ssh user@host -t 'tmux attach -t foo'
- For PuTTY users,
- Put "tmux -2 attach || tmux -2 new -s foo" into PuTTY's configuration panel: "connection -> ssh -> remote command".
- In "connection -> data", add environment variable
LANG=en_US.UTF-8
.
- If you are using "konsole" (locally), edit konsole's profile
"general->command" to this:
/bin/bash -c "tmux attach || tmux new -s foo"
- Use the feature "forced command" of SSH.
This affects ssh connections only. For details, refer to my ssh tips.
Note that:
- Specify absolute path for tmux.
- Set 'LANG' to utf8 so that pane borders are shown correctly.
- The most aggressive way, attach to tmux in
.bash_profile
This measure takes effect wherever you connect from, whatever protocol you are using.
Put this following line into your
.bash_profile
[ -n "$TMUX" ] || tg
Note that:
- "tg" is the function alias defined previously
- This line needn't be the last line in your profile
- NOTE: Connecting to localhost results in recursive tmux sessions,
which eventually render the tmux session unusable.
ssh localhost -t /bin/bash
if you really need to login back to localhost since this way a non-login interactive shell is opened.
Resize current pane without mouse
resize-pane -[UDLR] N
where U for Up, D for Down etc. and N is adjustment in cells or lines.
Or, F12 M-<arrow>
.
Share "clipboard" between Emacs and Tmux
(defun lgfang-send-tmux-copied () "Send head of the kill-ring to tmux's buffer" (interactive) (let ((file (make-temp-file "/tmp/emacs-to-tmux.clip"))) (with-temp-file file (insert-for-yank (current-kill 0))) (call-process "tmux" nil nil nil "load-buffer" file) (delete-file file))) (defun lgfang-get-tmux-copied () "Get current tmux buffer" (interactive) (call-process "tmux" nil t nil "show-buffer"))
Populate the "clipboard"
function copy_to_tmux { # usage: cat file | this_function while read line; do tmux set-buffer "$line" done }
Share windows among sessions
In addition to move windows among sessions, it is also possible to make certain windows present in multiple sessions simultaneously.
tmux link-window -s session:window tmux unlink-window
Share sessions with another account
It seems to me the only benefit is that you do not share your password.
- To share all:
- Find corresponding socket by
echo ${TMUX%%,*}
in the session - Make it readable/writable to others
chmod 777 /path/to/socket
- Others attach with
tmux -S /path/to/socket attach
- Find corresponding socket by
- To share only one session: start that session with a separate socket, make
it readable/writable by others.
tmux -S /path/to/socket_to_share [new-session -s session_name]
NOTE: according to test:
- If the socket is writable by others, you (the tmux owner) cannot connect to
the tmux server unless explicitly "-S".
$ tmux list-sessions health: 2 windows (created Thu May 29 09:30:17 2014) [167x44] package: 3 windows (created Wed May 28 09:02:17 2014) [167x44] $ chmod -R 0770 /tmp/tmux-30435/ $ tmux list-sessions can't create socket $ tmux attach can't create socket $ tmux -S /tmp/tmux-30435/default list-sessions health: 2 windows (created Thu May 29 09:30:17 2014) [167x44] package: 3 windows (created Wed May 28 09:02:17 2014) [167x44]
- Once your peer connected to your tmux, you can chmod back without breaking his/her connection.
Rescue
Socket file deleted by mistake?
According to tmux manul
If the socket is accidentally removed, the SIGUSR1 signal may be sent to the tmux server process to recreate it.
Where is my processes?
Once for a while, I want to "gracefully" kill a process spotted by "ps". The following command lists ttys of every pane, therefore helps to find out in which pane a process is in
function tt { tmux list-panes -a -F '#S:#I.#P #{pane_tty}' } export -f tt
-s [-t session_name]
instead of -a
for panes in a session only
Backspace and del not working?
The root cause
+-------------------------------------------------+ | +-----------------------------------------+ | | | | | | | | | | | | | | | | | | | erase = ^H | | | | | | | | | | | | | | | | inner shell | | | +-----------------------------------------+ | | input_erase = ^H | | forwarded_erase == ^? | | tmux | +-------------------------------------------------+
It is very likely an issue caused by the mismatch of the setup of the <erase> character. Upon receiving an <erase>, tmux always forwards one "^?" (control + ?) char to the inner shell. But, the inner shell does not necessarily interpret "^?" as <erase>.
For example, a typical cause of the issue is: one set <erase> to "^h" in his/her profile. This causes problem because:
- Tmux gets a "^h" and recognize it as an <erase> because that is how it was configured in the profile.
- Tmux sends out "^?" to the inner shell.
- The inner shell gets a "^?" but does NOT interpret it as an <erase>.
Solutions
- A workaround: run
stty erase ^?
in each problematic inner shell.This is not recommended, but it does provide a remedy when you cannot afford restarting the tmux server.
- The once-for-all solution: put
stty erase ^?
into your profile.This way, you use "^?" for <erase> everywhere consistently. In fact, most "modern" software interpret "^?" as <erase> by default. Therefore, this solution is not only a once-for-all solution but also brings you least surprise.
- Put
stty erase ^?
into your profile and apply it. - Restart tmux server (NOT re-attach!)
- Configure your terminal emulator (say putty) to send "^?" when the <backspace> key is pressed.
- Put
- Another workaround: leave profile unchanged but
stty erase ^?
before starting tmux server. (You run "stty" only once and attach to tmux sessions as usual later on.)This works since tmux sees "^H" as a normal char and forwards it without conversion and then the inner shell regards the "^H" as an erase.
This workaround is useful if you cannot set erase to "^?" permanently (say you are sharing the lab with others and all others are accustomed to "^h").
Environment
Note that there are global environment (inherited when a tmux server starts) and session specific environment. And, these environments persist so long as the server/session are alive.
Therefore, you can set environment variables (via set-environment
) and
impact all new panes.
One use case of this technique is: I share an account with others. I want to
use a really fancy PS1, which others might dislike. Hence, instead of
modifying ~/.bashrc
, I run the following command to make current fancy PS1 a
session-wise environment and impacts all new shells in and only in my session.
tmux set-environment -t my_session PS1 "$PS1"
My configure
My configure file for tmux can be found here: https://github.com/lgfang/dotfiles/blob/master/.tmux.conf
This file 'source' color themes defined in files under ~/.tmux
. Therefore,
please remember to copy my color theme files as well:
https://github.com/lgfang/dotfiles/tree/master/.tmux
An example of scripting tmux
#!/bin/bash # Created: Fang lungang 2011 # Modified: Fang Lungang 10/04/2014 15:07> set -o nounset set -o errexit set -o pipefail # set -x REMOTE_HOST="135.1.2.3" REMOTE_LOGIN="lgfang" MY_PATH=".local/bin/tmux-dwim" # path to this script on the remote host REMOTE_CMD="source .bash_profile; $MY_PATH" SESSION_NAME="hi" # Attach if the session already exists if tmux has-session -t $SESSION_NAME 2>/dev/null; then exec tmux attach -t $SESSION_NAME fi # Start a session tmux new-session -d -s $SESSION_NAME if hostname | grep -q "\(lgfang\|lungang\|PA0019712\)"; then this_is_mypc=true else this_is_mypc=false fi # linux ping and windows ping use different options for the same meaning if which ping | grep -q -i windows; then PING="ping -n 1 -w 1" else PING="ping -c 1 -W 1" fi if ! $this_is_mypc; then # I am already on the "remote_host" # Start Emacs in a window with specified number tmux new-window -d -n emacsclient -t $SESSION_NAME:9 \ "emacsclient -t || (emacs --daemon && emacsclient -t)" elif $PING $REMOTE_HOST; then # I am on the local PC and remote host ping-able. # Run this script on the remote host to create a similar session tmux new-window -d -n $REMOTE_HOST -t $SESSION_NAME:9 \ "ssh -o BatchMode=yes -t $REMOTE_LOGIN@$REMOTE_HOST '$REMOTE_CMD'" tmux select-window -t $SESSION_NAME:9 # make it feels like I'm working on the remote host locally tmux set-option -g -t $SESSION_NAME status off tmux set-option -g -t $SESSION_NAME prefix F11 fi exec tmux attach -t $SESSION_NAME