Tmux - čo to je a ako ho používam

27.03.2021

Tmux je skratkou terminal-multiplexer a nikto nevie, čo to znamená. Je to nástroj pre terminál, ktorý nám umožňuje v ňom mať "taby" a panely a okrem toho nás zachráni v prípade, keď niečo robíme na serveri a spadne nám internet. Mne pomáha aj veľmi rýchlo si jedným príkazom spustiť všetko, čo potrebujem k práci. Tmux sa pre mňa stal úplne základným nástrojom a bol som celkom prekvapený, keď som zistil, že kolegovia ho nepoznajú.

Nudné predstavenie

Tmux je modernejším a myšlienkovým nástupcom programu, ktorý sa volá screen. Oboje sú aplikácie, ktoré umožňujú mať taby (v ich terminológii sú to "okná") v termináloch a okrem toho je možné sa od nich odpojiť, pričom oni a programy v nich spustené bežia ďalej. Keď sa dokážete odpojiť, tak sa viete aj naspäť pripojiť.

Toto je užitočné napríklad aj pre administráciu serveru. Pripojím sa na server, spustím tmux (alebo screen), v ňom naštartujem nejaký dlhotrvajúci skript. Odpojím sa z tmux-u, odpojím sa zo serveru a idem domov. Doma sa opäť pripojím a pozriem, ako môj dlhotrvajúci skript napreduje.

Tmux má oproti screen-u aj panely, čo vlastne znamená, že viete okno rozdeliť na viacero častí a v každej mať samostatný shell.

Killer feature tmux-u je však podľa mňa to, že umožňuje programovo zasielať príkazy a simulovať písanie do hociktorej svojej session a hociktorého okna 🤯.

K čomu je to dobré?

Ja mám tmux rád, pretože jeho taby sú nezávislé na termináli a zaberajú menej miesta na výšku. Ale najmä tmux používam na rýchle naštartovanie projektov a setup pracovných nástrojov.

Keď chcem ísť programovať Wareenu, čo je jeden z mojich projektov s PHP backendom a React-om na frontende, tak potrebujem urobiť kopec vecí na to, aby som mohol pohodlne vyvíjať a všetko mal na dosah ruky. Konkrétne:

  • spustiť docker kontajnery
  • prihlásiť sa do kontajneru s PHP, aby som mohol spúšťať napr. composer
  • prihlásiť sa do kontajneru s node, aby som tam hneď spustil HMR
  • prihlásiť sa do kontajneru s PHP a pripraviť si tam príkaz na spustenie PHPUnit-u
  • prihlásiť sa do kontajneru s databázou a otvoriť pripojenie
  • prihlásiť sa do kontajneru s fixtúrovou databázou a otvoriť pripojenie
  • prihlásiť sa do kontajneru s node a spustiť websocket server
  • nastaviť sa do adresáru s projektom, aby som odtiaľ ľahko spúšťal príkazy (napr. git)
  • nastaviť sa do adresáru s projektom, aby som odtiaľ spustil editor

Keby som nemal tmux, tak by som pri týchto požiadavkách skončil s tým, že sa mi otvorí 8 samostatných terminálových okien. Trochu lepšia situácia by bola v prípade, keby som používal terminál umožňujúci programovo otvárať nové taby a spustiť v nich príkaz, ale taký nepoznám.

S pomocou tmux-u si však viem k tomuto napísať skript a potom to spúšťať jediným príkazom (alebo aj klávesovou skratkou).

Základy práce v tmux-e

Inštalácia

Tmux sa na Linuxe inštaluje klasicky cez balíčkovací manažér (napr. apt install tmux), na Mac-u cez Homebrew (brew install tmux) alebo MacPorts (port install tmux). Na Windowse ho nepotrebujeme, lebo Windows je predsa pre programátora hernou konzolou a nie pracovným nástrojom 😃. (Ak ale máte na Windowse WSL, tak inštalujte cez balíčkovací systém distribúcie.)

Spustenie a okná

Tmux spustíme v konzole príkazom tmux. (Kto by to bol povedal.)

tmux

Týmto príkazom sa spustí nová tmux session. Session môžete mať viac a každá je samostatná so svojimi oknami. Ak však chceme, tak sa vieme z jednej session pozrieť do druhej.

Pre konfigurovanie je dôležité vedieť, že prvá session na pozadí spustí aj tmux server, s ktorým komunikujú všetky session a ktorý zaniká s poslednou ukončenou. Tento server si drží konfiguráciu. Ak sa teda budeme hrať s konfiguračným súborom pre tmux, tak zmeny sa nám neprejavia, kým bude bežať aspoň jedna session. Musíme ich najskôr všetky vypnúť a naštartovať tmux odznova.

V spustenom tmux-e máme rovno otvorené prvé okno (má číslo 0). Ak chceme otvoriť ďalšie, tak sa to robí klávesovou skratkou CTRL+B C, čo znamená, že najskôr stlačíme CTRL+B, potom uvoľníme oba klávesy a stlačíme C.

Všetky klávesové skratky v tmux-e začínajú prefixom, ktorý je defaultne nastavený na CTRL+B. Je však možné ho zmeniť buď pre aktuálnu session alebo rovno v konfiguračnom súbore na stálo. Ja osobne som ti prefix zmenil na CTRL+A, pretože tak to bolo v screen-e a zvykol som si a zároveň mi to je príjemnejšie.

Ďalej budem namiesto CTRL-B alebo CTRL-A uvádzať {prefix} a vy tam budete stláčať to, čo ste si nakonfigurovali.

Medzi oknami sa prepíname skratkou {prefix} číslo-okna. Ak je okien viac, ako desať, tak ich zoznam si zobrazíme skratkou {prefix} W

Zoznam základných skratiek, vyskúšajte si:

Skratka Čo robí
{prefix} C vytvorenie ďalšieho okna
{prefix} číslo prepnutie do okna s danýmn číslom
{prefix} W zobrazenie zoznamu okien (pohyb šípkami, potvrdenie Enter)
{prefix} , premenovanie aktuálneho okna
{prefix} " vertikálne rozdelenie aktuálneho panelu na dve časti
{prefix} % horizontálne rozdelenie aktuálneho panelu na dve časti
{prefix} ! vytvorenie nového okna z aktuálneho panelu
{prefix} šípka pohyb medzi panelmi
{prefix} : zadanie konfiguračného príkazu
{prefix} d odpojenie sa od session

Príkazy

Tmux má hrozne veľa príkazov. Viete ich zadávať ako parametre príkazu tmux alebo priamo v bežiacom tmux-e do príkazového riadku zobrazenom po stlačení {prefix} :. A tie isté príkazy zapisujete aj do konfiguračného súboru (defaultne ~/.tmux.conf).

Ak je možné príkaz jednoznačne identifikovať aj z časti jeho názvu, tak ho nemusíme písat celý. Napríklad nemusíme písať list-sessions a úplne postačí list-s.

Zoznam niektorých užitočných príkazov:

Príkaz Čo robí
set {premenná} {hodnota} nastaví konfiguračnú premennú, napr. set prefix C-a zmení prefix na CTRL+A
set -g {premenná} {hodnota} nastaví premennú globálne pre session a okná (takto to píšte v konfiguračnom súbore)
new-session vytvorí novú session
list-sessions vypíše zoznam všetkých bežiacich session a ich názvov
new-window vytvorí nové okno
send-keys -t {názov-session}:{názov-okna} odošle "klávesy" do okna v danej session
attach-session -t {názov-session} pripojí sa do session s daným názvom
rename-session {názov} premenuje session
source "cesta" načíta konfiguráciu z iného súboru
run-shell "príkaz" spustí konzolový príkaz

Príkazy môžu mať vlastné prepínače (klasicky unixovské písmenkové flagy začínajúce pomlčkou). Pre detaily je dobré si pozrieť dokumentáciu (príkaz man tmux v konzole).

Moja konfigurácia

Tmux sa konfiguruje v súbore ~/.tmux.conf.

U mňa tmux vyzerá takto:

Môj tmux

Spodný riadok je "tabbar". Úplne naľavo je názov session, potom nasleduje zoznam okien, uptime, systémový load, dátum a čas a názov počítača.

Dosiahol som to touto konfiguráciou:

# prefix bude CTRL+A
set -g prefix C-a

# Chvíľku čakáme na escape sekvencie.
# Vysvetlenie viď https://unix.stackexchange.com/questions/608142/whats-the-effect-of-escape-time-in-tmux
set -g escape-time 10

# Chceme pekné farby
set -g default-terminal "tmux-256color"
set -ga terminal-overrides ",*256col*:Tc"

# O vizuál tmux-u sa mi stará powerline
run-shell "powerline-daemon -q"
source "/usr/share/powerline/bindings/tmux/powerline.conf"

Ak sa vám nepáči defaultný vzhľad tmux-u, tak existuje veľa možností na to, aby ste si to nastavili podľa svojho gusta (pozrite man tmux). Ak to chcete mať rovnaké ako ja, tak si potrebujete nainštalovať powerline a k tomu ešte fonty (na Ubuntu apt install powerline fonts-powerline).

Zázračný skript pre spustenie projektu

Spomínal som, že mám vytvorené skripty pre jednoduché spúšťanie projektov. Jeden taký mám pre projekt Wareenu a po spustení skriptu sa mi naštartujú docker kontajnery, rovno ma do niektorých prihlási a všetko je pripravené tak, aby som len otvoril neovim a mohol programovať.

Takto to vyzerá po spustení skriptu:

Takto to vyzerá po spustení skriptu

Do konzoly iba napíšem start-wareena.sh, pár sekúnd počkám (lebo docker kontajnery chvíľku štartujú) a môžem pracovať.

Skript samotný vyzerá takto, kľudne sa inšpirujte a upravte pre svoje potreby. Nezabudnite mu dať práva na spúšťanie.

#!/bin/bash

# názov tmux-ovej session
SESSION_NAME=wareena

# ak už session s týmto názvom existuje, tak sa do nej len pripojíme
if tmux has-session -t=$SESSION_NAME 2>/dev/null; then
    tmux attach -t $SESSION_NAME
    exit;
fi

# adresár, kde sú zdrojáky projektu
TARGET_DIR="/home/perun/projects/wareena/src/web"
# adresár, kde je umiestnený docker-compose.yml k projektu
DOCKER_DIR="/home/perun/projects/wareena"

# id aktuálneho používateľa (aby som sa do docker neprihlásil ako root)
USER_ID=`id -u $USER`

# aktuálny počet riadkov a stĺpcov v termináli (docker občas hnevá a nevie to správne detekovať)
LINES=`tput lines`
COLUMNS=`tput cols`

# Príkaz na prihlásenie sa do web kontajneru.
# V tomto projekte mám ten istý kontajner pre PHP aj pre node
WEB_LOGIN_CMD="cd $DOCKER_DIR; docker exec --user $USER_ID -it -e COLUMNS=$COLUMNS -e LINES=$LINES wareena_php_1 bash"

# Príkaz pre prihlásenie do databázového kontajneru
DB_LOGIN_CMD="cd $DOCKER_DIR; docker exec -it -e COLUMNS=$COLUMNS -e LINES=$LINES wareena_db_1 bash"

# Príkaz pre prihlásenie do kontajneru s fixtúrovou databázou
FIXTURE_DB_LOGIN_CMD="cd $DOCKER_DIR; docker exec -it -e COLUMNS=$COLUMNS -e LINES=$LINES wareena_fixture_db_1 bash"

# Naštartovanie kontajnerov
cd $DOCKER_DIR
docker-compose up -d

# Občas nejaký kontajner nenaštartuje, vtedy nechcem pokračovať
read -p "Continue with a new tmux session [Y/n]?" yn
case $yn in
    [Nn]* ) exit;;
    * ) echo "OK";;
esac

# Vytvorím novú tmux session:
# - nechcem sa do nej hneď pripnúť (-d parameter), inak by sa zvyšné príkazy spustili az po jej ukončení :)
# - so zvoleným názov (-s parameter)
# - vytvorím aj nový tab/okno a rovno ho pomenujem (-n parameter)
tmux new-session -d -s $SESSION_NAME -n webserver

# Povytváram zvyšné taby
tmux new-window -n node
tmux new-window -n src
tmux new-window -n misc
tmux new-window -n test
tmux new-window -n db
tmux new-window -n fixture_db
tmux new-window -n ws

# V tabe webserver sa prihlásim do web kontajneru.
# Parameter -t určuje cieľovú session a okno
tmux send-keys -t $SESSION_NAME:webserver "$WEB_LOGIN_CMD" Enter

# V tabe pre node sa prihlásim do kontajneru a spustím HMR
tmux send-keys -t $SESSION_NAME:node "$WEB_LOGIN_CMD" Enter
tmux send-keys -t $SESSION_NAME:node "yarn dev" Enter

# V tomto tabe spúšťam neovim, len sa prepnem do adresáru so zdrojákmi
tmux send-keys -t $SESSION_NAME:src "cd $TARGET_DIR" Enter

# Tu spúšťam kadejaké príkazy (napr. git), len sa prepnem do adresáru so zdrojákmi
tmux send-keys -t $SESSION_NAME:misc "cd $TARGET_DIR" Enter

# Pripravím si tab pre spúšťanie testov pre PHPUnit
tmux send-keys -t $SESSION_NAME:test "$WEB_LOGIN_CMD" Enter
tmux send-keys -t $SESSION_NAME:test "export SYMFONY_DEPRECATIONS_HELPER=disabled=1" Enter
tmux send-keys -t $SESSION_NAME:test "bin/phpunit"

# Prihlásim sa do databázy
tmux send-keys -t $SESSION_NAME:db "$DB_LOGIN_CMD" Enter
tmux send-keys -t $SESSION_NAME:db "sleep 5; mysql -u root wareena" Enter

# Prihlásim sa do fixtúrovej databázy
tmux send-keys -t $SESSION_NAME:fixture_db "$FIXTURE_DB_LOGIN_CMD" Enter
tmux send-keys -t $SESSION_NAME:fixture_db "sleep 5; mysql -u root wareena" Enter

# V poslednom tabe spustím websocket server
tmux send-keys -t $SESSION_NAME:ws "$WEB_LOGIN_CMD" Enter
tmux send-keys -t $SESSION_NAME:ws "node bin/ws-server.js" Enter

# Pripojím sa do vytvorenej session a do tabu, kde chcem spustiť neovim
tmux attach -t $SESSION_NAME:src