Terminal sessions
Table of Contents
1 Summary
- I have been using terminator for some time for the splitting terminal capabilities. Overall very happy with it but came across a much better solution on the long term…
- First, splitting terminal windows is fine, but thinking about it, there are very few instances for which I need to see several terminals \textit{at the same time}! So tabs are nice for this reason, because they save space, you just switch between them.
- Second, I had been using screen once in a while to stay logged in or launch an interactive process on some cluster machine and come back to it later without the worry that there is an electricity shutdown in the building or that my computer decides to stop.
- With the new setup presented here, I have tabs within sessions I can log back into, which is perfect. That's thanks to byobu together with the tmux back-end. Splits are also possible by the way, but I end up not using them anymore (useless and occupy unnecessary space).
- Typical use-case: launch several (classic) terminal windows (possibly across different workspaces), launch byobu/tmux within each, create sessions and tabs and switch between them across these several terminal windows. From home, log to the work machine, launch byobu/tmux to find the exact same sessions/tabs! There are some interesting plugins too, for instance to save a session that can "survive" a reboot.
2 Byobu
- Used for management of sessions/tabs.
- One problem: when sessions exist and then quit, and log back into, this creates duplicate sessions. Solution: use tmux to log back into (not byobu). With the simple script below place in \textit{/usr/bin} this takes care of the issue:
#!/bin/bash if byobu list-sessions #sessions exist then /usr/bin/tmux attach #log into else /usr/bin/byobu #launch byobu fi
3 Shortcuts
- Prefix: CTRL+B
- F6: quit session (can log back into)
- F2: new window, F8 rename
- CTRL+SHIFT+F2: new session, CTRL+F8: rename
- ALT+arrows: navigate
- SHIFT+F1: help
4 Tmux
- Awesome appearance with the tmux-config plugin: https://github.com/samoshkin/tmux-config
- To copy/paste: normal procedure too complicated (scroll back mode, enter then space…), I just do the normal select + middle button but with shift pressed (which overrides the mouse mode).
- Another useful plugin is tmux-resurrect, which enables saving/restoring sessions even when the computer has been shutdown/rebooted.
- tmux.conf mostly from tmux-config:
# ========================== # === General settings === # ========================== set -g default-terminal "screen-256color" set -g history-limit 20000 set -g buffer-limit 20 set -sg escape-time 0 set -g display-time 1500 set -g remain-on-exit off set -g repeat-time 300 setw -g allow-rename off setw -g automatic-rename off setw -g aggressive-resize off # Change prefix key to C-a, easier to type, same to "screen" #unbind C-b #set -g prefix C-a #unbind-key -n C-b set -g prefix ^B set -g prefix2 ^B bind b send-prefix #C-b : prefix! Then use C-b C-s/r to save/restore # Set parent terminal title to reflect current window in tmux session set -g set-titles on set -g set-titles-string "#I:#W" # Start index of window/pane with 1, because we're humans, not computers set -g base-index 1 setw -g pane-base-index 1 # Enable mouse support set -g mouse on # To copy, drag to highlight text in yellow, press Enter and then release mouse # Use vim keybindings in copy mode setw -g mode-keys vi # Update default binding of `Enter` to also use copy-pipe #unbind -t vi-copy Enter #bind-key -t vi-copy Enter copy-pipe "pbcopy" # ========================== # === Key bindings === # ========================== # Unbind default key bindings, we're going to override unbind "\$" # rename-session unbind , # rename-window unbind % # split-window -h unbind '"' # split-window unbind } # swap-pane -D unbind { # swap-pane -U unbind [ # paste-buffer unbind ] unbind "'" # select-window unbind n # next-window unbind p # previous-window unbind l # last-window unbind M-n # next window with alert unbind M-p # next window with alert unbind o # focus thru panes unbind & # kill-window unbind "#" # list-buffer unbind = # choose-buffer unbind z # zoom-pane unbind M-Up # resize 5 rows up unbind M-Down # resize 5 rows down unbind M-Right # resize 5 rows right unbind M-Left # resize 5 rows left # Edit configuration and reload bind C-e new-window -n 'tmux.conf' "sh -c '\${EDITOR:-vim} ~/.tmux.conf && tmux source ~/.tmux.conf && tmux display \"Config reloaded\"'" # Reload tmux configuration bind C-r source-file ~/.tmux.conf \; display "Config reloaded" # new window and retain cwd bind c new-window -c "#{pane_current_path}" # Prompt to rename window right after it's created #set-hook -g after-new-window 'command-prompt -I "#{window_name}" "rename-window '%%'"' #set-hook -g after-new-window 'command-prompt -I "" "rename-window '%%'"' #set-hook -g after-new-session 'command-prompt -I "" "rename-session '%%'"' # Rename session and window #bind r command-prompt -I "#{window_name}" "rename-window '%%'" #bind R command-prompt -I "#{session_name}" "rename-session '%%'" # Split panes bind | split-window -h -c "#{pane_current_path}" bind _ split-window -v -c "#{pane_current_path}" # Select pane and windows bind -r C-[ previous-window bind -r C-] next-window bind -r [ select-pane -t :.- bind -r ] select-pane -t :.+ bind -r Tab last-window # cycle thru MRU tabs bind -r C-o swap-pane -D # Zoom pane bind + resize-pane -Z # Link window bind L command-prompt -p "Link window from (session:window): " "link-window -s %% -a" # Swap panes back and forth with 1st pane # When in main-(horizontal|vertical) layouts, the biggest/widest panel is always @1 bind \ if '[ #{pane_index} -eq 1 ]' \ 'swap-pane -s "!"' \ 'select-pane -t:.1 ; swap-pane -d -t 1 -s "!"' # Kill pane/window/session shortcuts bind x kill-pane bind X kill-window bind C-x confirm-before -p "kill other windows? (y/n)" "kill-window -a" bind Q confirm-before -p "kill-session #S? (y/n)" kill-session # Merge session with another one (e.g. move all windows) # If you use adhoc 1-window sessions, and you want to preserve session upon exit # but don't want to create a lot of small unnamed 1-window sessions around # move all windows from current session to main named one (dev, work, etc) bind C-u command-prompt -p "Session to merge with: " \ "run-shell 'yes | head -n #{session_windows} | xargs -I {} -n 1 tmux movew -t %%'" # Detach from session bind d detach bind D if -F '#{session_many_attached}' \ 'confirm-before -p "Detach other clients? (y/n)" "detach -a"' \ 'display "Session has only 1 client attached"' # Hide status bar on demand bind C-s if -F '#{s/off//:status}' 'set status off' 'set status on' # ================================================== # === Window monitoring for activity and silence === # ================================================== bind m setw monitor-activity \; display-message 'Monitor window activity [#{?monitor-activity,ON,OFF}]' bind M if -F '#{monitor-silence}' \ 'setw monitor-silence 0 ; display-message "Monitor window silence [OFF]"' \ 'command-prompt -p "Monitor silence: interval (s)" "setw monitor-silence %%"' # Activity bell and whistles set -g visual-activity on # TODO: Does not work as well, check on newer versions # set -g visual-silence on # BUG: bell-action other ignored · Issue #1027 · tmux/tmux · GitHub - https://github.com/tmux/tmux/issues/1027 # set -g visual-bell on # setw -g bell-action other # ================================================ # === Copy mode, scroll and clipboard === # ================================================ set -g @copy_use_osc52_fallback on # Prefer vi style key table setw -g mode-keys vi bind p paste-buffer bind C-p choose-buffer # trigger copy mode by #bind -n M-Up copy-mode # Scroll up/down by 1 line, half screen, whole screen bind -T copy-mode-vi M-Up send-keys -X scroll-up bind -T copy-mode-vi M-Down send-keys -X scroll-down bind -T copy-mode-vi M-PageUp send-keys -X halfpage-up bind -T copy-mode-vi M-PageDown send-keys -X halfpage-down bind -T copy-mode-vi PageDown send-keys -X page-down bind -T copy-mode-vi PageUp send-keys -X page-up # When scrolling with mouse wheel, reduce number of scrolled rows per tick to "2" (default is 5) bind -T copy-mode-vi WheelUpPane select-pane \; send-keys -X -N 2 scroll-up bind -T copy-mode-vi WheelDownPane select-pane \; send-keys -X -N 2 scroll-down # wrap default shell in reattach-to-user-namespace if available # there is some hack with `exec & reattach`, credits to "https://github.com/gpakosz/.tmux" # don't really understand how it works, but at least window are not renamed to "reattach-to-user-namespace" if -b "command -v reattach-to-user-namespace > /dev/null 2>&1" \ "run 'tmux set -g default-command \"exec $(tmux show -gv default-shell) 2>/dev/null & reattach-to-user-namespace -l $(tmux show -gv default-shell)\"'" yank="~/.tmux/yank.sh" # Copy selected text bind -T copy-mode-vi Enter send-keys -X copy-pipe-and-cancel "$yank" bind -T copy-mode-vi y send-keys -X copy-pipe-and-cancel "$yank" bind -T copy-mode-vi Y send-keys -X copy-line \;\ run "tmux save-buffer - | $yank" bind-key -T copy-mode-vi D send-keys -X copy-end-of-line \;\ run "tmux save-buffer - | $yank" bind -T copy-mode-vi C-j send-keys -X copy-pipe-and-cancel "$yank" bind-key -T copy-mode-vi A send-keys -X append-selection-and-cancel \;\ run "tmux save-buffer - | $yank" # Copy selection on drag end event, but do not cancel copy mode and do not clear selection # clear select on subsequence mouse click bind -T copy-mode-vi MouseDragEnd1Pane \ send-keys -X copy-pipe "$yank" bind -T copy-mode-vi MouseDown1Pane select-pane \;\ send-keys -X clear-selection # iTerm2 works with clipboard out of the box, set-clipboard already set to "external" # tmux show-options -g -s set-clipboard # set-clipboard on|external # ===================================== # === Theme === # ===================================== # Feel free to NOT use this variables at all (remove, rename) # this are named colors, just for convenience color_orange="colour166" # 208, 166 color_purple="colour134" # 135, 134 color_green="colour076" # 070 color_blue="colour39" color_yellow="colour220" color_red="colour160" color_black="colour232" color_white="white" # 015 # This is a theme CONTRACT, you are required to define variables below # Change values, but not remove/rename variables itself color_dark="$color_black" color_light="$color_white" color_session_text="$color_blue" color_status_text="colour245" color_main="$color_orange" color_secondary="$color_purple" color_level_ok="$color_green" color_level_warn="$color_yellow" color_level_stress="$color_red" color_window_off_indicator="colour088" color_window_off_status_bg="colour238" color_window_off_status_current_bg="colour254" # ===================================== # === Appearence and status bar === # ====================================== set -g mode-style "fg=default,bg=$color_main" # command line style set -g message-style "fg=$color_main,bg=$color_dark" # status line style set -g status-style "fg=$color_status_text,bg=$color_dark" # window segments in status line set -g window-status-separator "" separator_powerline_left="" separator_powerline_right="" separator_powerline_left="<" separator_powerline_right=">" # setw -g window-status-style "fg=$color_status_text,bg=$color_dark" setw -g window-status-format " #I:#W " setw -g window-status-current-style "fg=$color_light,bold,bg=$color_main" setw -g window-status-current-format "#[fg=$color_dark,bg=$color_main]$separator_powerline_right#[default] #I:#W# #[fg=$color_main,bg=$color_dark]$separator_powerline_right#[default]" # when window has monitoring notification setw -g window-status-activity-style "fg=$color_main" # outline for active pane setw -g pane-active-border-style "fg=$color_main" # general status bar settings set -g status on set -g status-interval 5 set -g status-position top set -g status-justify left set -g status-right-length 100 # define widgets we're going to use in status bar # note, that this is not the complete list, some of them are loaded from plugins wg_session="#[fg=$color_session_text] #S #[default]" wg_battery="#{battery_status_fg} #{battery_icon} #{battery_percentage}" wg_date="#[fg=$color_secondary]%h %d %H:%M#[default]" wg_user_host="#[fg=$color_secondary]#(whoami)#[default]@#H" wg_is_zoomed="#[fg=$color_dark,bg=$color_secondary]#{?window_zoomed_flag,[Z],}#[default]" # TODO: highlighted for nested local session as well wg_is_keys_off="#[fg=$color_light,bg=$color_window_off_indicator]#([ $(tmux show-option -qv key-table) = 'off' ] && echo 'OFF')#[default]" set -g status-left "$wg_session" set -g status-right "#{prefix_highlight} $wg_is_keys_off $wg_is_zoomed #{sysstat_cpu} | #{sysstat_mem} | #{sysstat_loadavg} | $wg_user_host | $wg_date $wg_battery #{online_status}" # online and offline icon for tmux-online-status set -g @online_icon "#[fg=$color_level_ok]●#[default]" set -g @offline_icon "#[fg=$color_level_stress]●#[default]" # Configure view templates for tmux-plugin-sysstat "MEM" and "CPU" widget set -g @sysstat_mem_view_tmpl 'MEM:#[fg=#{mem.color}]#{mem.pused}#[default] #{mem.used}' # Configure colors for tmux-plugin-sysstat "MEM" and "CPU" widget set -g @sysstat_cpu_color_low "$color_level_ok" set -g @sysstat_cpu_color_medium "$color_level_warn" set -g @sysstat_cpu_color_stress "$color_level_stress" set -g @sysstat_mem_color_low "$color_level_ok" set -g @sysstat_mem_color_medium "$color_level_warn" set -g @sysstat_mem_color_stress "$color_level_stress" set -g @sysstat_swap_color_low "$color_level_ok" set -g @sysstat_swap_color_medium "$color_level_warn" set -g @sysstat_swap_color_stress "$color_level_stress" # Configure tmux-battery widget colors set -g @batt_color_full_charge "#[fg=$color_level_ok]" set -g @batt_color_high_charge "#[fg=$color_level_ok]" set -g @batt_color_medium_charge "#[fg=$color_level_warn]" set -g @batt_color_low_charge "#[fg=$color_level_stress]" # Configure tmux-prefix-highlight colors set -g @prefix_highlight_output_prefix '[' set -g @prefix_highlight_output_suffix ']' set -g @prefix_highlight_fg "$color_dark" set -g @prefix_highlight_bg "$color_secondary" set -g @prefix_highlight_show_copy_mode 'on' set -g @prefix_highlight_copy_mode_attr "fg=$color_dark,bg=$color_secondary" # ===================================== # === Renew environment === # ===================================== #set -g update-environment \ # "DISPLAY\ # SSH_ASKPASS\ # SSH_AUTH_SOCK\ # SSH_AGENT_PID\ # SSH_CONNECTION\ # SSH_TTY\ # WINDOWID\ # XAUTHORITY" set -g update-environment \ "DISPLAY\ WINDOWID\ XAUTHORITY" bind '$' run "~/.tmux/renew_env.sh" # ============================ # === Plugins === # ============================ set -g @plugin 'tmux-plugins/tpm' set -g @plugin 'tmux-plugins/tmux-battery' set -g @plugin 'tmux-plugins/tmux-prefix-highlight' set -g @plugin 'tmux-plugins/tmux-online-status' set -g @plugin 'tmux-plugins/tmux-sidebar' set -g @plugin 'tmux-plugins/tmux-copycat' set -g @plugin 'tmux-plugins/tmux-open' set -g @plugin 'samoshkin/tmux-plugin-sysstat' set -g @plugin 'tmux-plugins/tmux-resurrect' set -g @resurrect-save 'S' set -g @resurrect-restore 'R' # Plugin properties set -g @sidebar-tree 't' set -g @sidebar-tree-focus 'T' set -g @sidebar-tree-command 'tree -C' set -g @open-S 'https://www.google.com/search?q=' # ============================================== # === Nesting local and remote sessions === # ============================================== # Session is considered to be remote when we ssh into host #if-shell 'test -n "$SSH_CLIENT"' \ # 'source-file ~/.tmux/tmux.remote.conf' # We want to have single prefix key "C-a", usable both for local and remote session # we don't want to "C-a" + "a" approach either # Idea is to turn off all key bindings and prefix handling on local session, # so that all keystrokes are passed to inner/remote session # see: toggle on/off all keybindings · Issue #237 · tmux/tmux - https://github.com/tmux/tmux/issues/237 # Also, change some visual styles when window keys are off bind -T root F12 \ set prefix None \;\ set key-table off \;\ set status-style "fg=$color_status_text,bg=$color_window_off_status_bg" \;\ set window-status-current-format "#[fg=$color_window_off_status_bg,bg=$color_window_off_status_current_bg]$separator_powerline_right#[default] #I:#W# #[fg=$color_window_off_status_current_bg,bg=$color_window_off_status_bg]$separator_powerline_right#[default]" \;\ set window-status-current-style "fg=$color_dark,bold,bg=$color_window_off_status_current_bg" \;\ if -F '#{pane_in_mode}' 'send-keys -X cancel' \;\ refresh-client -S \;\ bind -T off F12 \ set -u prefix \;\ set -u key-table \;\ set -u status-style \;\ set -u window-status-current-style \;\ set -u window-status-current-format \;\ refresh-client -S # Run all plugins' scripts run '~/.tmux/plugins/tpm/tpm'
- if you have some issues with X forwarding, even on a localhost, you can try the following in your .bashrc file
# -- Improved X11 forwarding through GNU Screen (or tmux). # If not in screen or tmux, update the DISPLAY cache. # If we are, update the value of DISPLAY to be that in the cache. function update-x11-forwarding { if [ -z "$STY" -a -z "$TMUX" ]; then echo $DISPLAY > ~/.display.txt else export DISPLAY=`cat ~/.display.txt` fi } # This is run before every command. preexec() { # Don't cause a preexec for PROMPT_COMMAND. # Beware! This fails i]52;;Zg==OMMAND is a string containing more than one command. [ "$BASH_COMMAND" = "$PROMPT_COMMAND" ] && return update-x11-forwarding # Debugging. #echo DISPLAY = $DISPLAY, display.txt = `cat ~/.display.txt`, STY = $STY, TMUX = $TMUX } trap 'preexec' DEBUG
5 Scroll-down terminals
- In addition to byobu+tmux I also use guake or tilix which are nice scroll-down terminals to type a quick command.
- Some people use byobu inside guake, which is nice when the latter is the main way to access the terminal, which is not my case.
6 Screenshot