DEV Community

Isaac
Isaac

Posted on

Build a reusable Terminator layout with pre-loaded commands per pane

If you regularly work across multiple servers, environments, or services, you probably know this dance: open a terminal, split it three ways, type the same SSH command in one pane, the same tail -f in another, and a ping in the third. Every time. For every host.

This post walks through building a single shell script that opens
Terminator with a custom layout — three panes, each with its own title and a list of commands pre-loaded into shell history, ready to fire with a single up arrow.

#!/bin/bash
#
# multi-pane.sh — Abre Terminator con un layout de 3 paneles personalizables,
# cada uno con su propio título y comandos precargados en el historial
# (listos para ejecutar con flecha arriba).
#
# Uso: ./multi-pane.sh <argumento>
#
# El argumento se sustituye en los títulos y comandos de cada panel.
# Útil para flujos donde alternás entre múltiples hosts/entornos.
#

set -euo pipefail

ARG=${1:?Uso: $(basename "$0") <argumento>}

CONFIG="$HOME/.config/terminator/config"
RC_LEFT=/tmp/multi-pane-rc-left
RC_TOP_RIGHT=/tmp/multi-pane-rc-top-right
RC_BOTTOM_RIGHT=/tmp/multi-pane-rc-bottom-right

# ---------------------------------------------------------------------------
# gen_rc: genera un rcfile temporal que será cargado por bash con --rcfile.
# Hace dos cosas:
#   1) Sourcea ~/.bashrc para mantener el entorno habitual del usuario.
#   2) Inyecta cada comando recibido como argumento en el historial mediante
#      `history -s`, sin ejecutarlo. El último argumento queda más cerca del
#      prompt (1 sola flecha arriba para acceder).
# ---------------------------------------------------------------------------
gen_rc() {
    local rcfile=$1
    shift
    {
        echo '[ -f "$HOME/.bashrc" ] && source "$HOME/.bashrc"'
        for cmd in "$@"; do
            echo "history -s \"$cmd\""
        done
    } > "$rcfile"
}

# Comandos por panel — personalizá según tu flujo.
# El último argumento es el primero al que accedés con flecha arriba.
gen_rc "$RC_LEFT" \
    "echo 'segundo comando'" \
    "echo 'primer comando (flecha arriba lo trae)'"

gen_rc "$RC_TOP_RIGHT" \
    "ls -la" \
    "pwd"

gen_rc "$RC_BOTTOM_RIGHT" \
    "date"

# ---------------------------------------------------------------------------
# Inyección del layout en el config de Terminator.
#
# Se inserta directamente en ~/.config/terminator/config en lugar de usar
# un archivo separado con `-g`. ¿Por qué? Porque `-g` reemplaza la config
# completa, perdiendo fuente, colores y atajos personales del usuario.
# Inyectando en el config real, todo se hereda automáticamente.
# ---------------------------------------------------------------------------

mkdir -p "$(dirname "$CONFIG")"
touch "$CONFIG"

# Borrar layout previo con el mismo nombre (idempotencia: el script se puede
# ejecutar múltiples veces sin acumular layouts duplicados).
python3 - "$CONFIG" <<'PYEOF'
import sys, re
path = sys.argv[1]
with open(path) as f:
    content = f.read()
pattern = re.compile(
    r'(^  \[\[multi_pane\]\].*?)(?=^  \[\[|^\[|\Z)',
    re.MULTILINE | re.DOTALL
)
content = pattern.sub('', content)
with open(path, 'w') as f:
    f.write(content)
PYEOF

# Asegurar que exista la sección [layouts]
grep -q '^\[layouts\]' "$CONFIG" || echo -e "\n[layouts]" >> "$CONFIG"

# Insertar el layout nuevo justo después de [layouts].
# El layout define una división horizontal (HPaned) al 50%, y el lado
# derecho se divide verticalmente (VPaned) en dos paneles.
#
#   ┌─────────────┬─────────────┐
#   │             │  TOP-RIGHT  │
#   │    LEFT     ├─────────────┤
#   │             │BOTTOM-RIGHT │
#   └─────────────┴─────────────┘
python3 - "$CONFIG" "$ARG" "$RC_LEFT" "$RC_TOP_RIGHT" "$RC_BOTTOM_RIGHT" <<'PYEOF'
import sys
path, arg, rc_l, rc_tr, rc_br = sys.argv[1:]
layout = f"""  [[multi_pane]]
    [[[window0]]]
      type = Window
      parent = ""
      title = Multi-pane {arg}
    [[[child1]]]
      type = HPaned
      parent = window0
      ratio = 0.5
    [[[left]]]
      type = Terminal
      parent = child1
      order = 0
      profile = default
      title = LEFT - {arg}
      command = bash --rcfile {rc_l}
    [[[child2]]]
      type = VPaned
      parent = child1
      order = 1
      ratio = 0.5
    [[[top_right]]]
      type = Terminal
      parent = child2
      order = 0
      profile = default
      title = TOP RIGHT - {arg}
      command = bash --rcfile {rc_tr}
    [[[bottom_right]]]
      type = Terminal
      parent = child2
      order = 1
      profile = default
      title = BOTTOM RIGHT - {arg}
      command = bash --rcfile {rc_br}
"""
with open(path) as f:
    content = f.read()
content = content.replace('[layouts]\n', '[layouts]\n' + layout, 1)
with open(path, 'w') as f:
    f.write(content)
PYEOF

# Lanzar Terminator desconectado de la shell padre:
#   --no-dbus    : evita que una instancia existente capture la invocación
#   --maximise   : abre la ventana maximizada
#   nohup + &    : sobrevive al cierre de la shell padre
#   disown       : libera la shell para que pueda cerrarse sin advertencias
nohup terminator --no-dbus --maximise -l multi_pane </dev/null >/dev/null 2>&1 &
disown
Enter fullscreen mode Exit fullscreen mode

You invoke it as ./multi-pane.sh prod and the argument propagates into
every pane title and command. The same script works for staging, dev, or
any environment label you want.

Why Terminator and not tmux?

tmux is more powerful, scriptable, and works over SSH. But tmux has a
learning curve, lives inside one terminal window, and isn't something most
people want to launch from a desktop shortcut.

Terminator is a GUI terminal emulator that supports persistent layouts via a
config file. It's perfect when you want a desktop launcher that opens a
specific multi-pane setup with one command — and you want the result to feel
like a normal application window, not an embedded multiplexer.

Top comments (1)

Collapse
 
isaac_90 profile image
Isaac

ty