web-dev-qa-db-fra.com

Bash scripts avec tmux pour lancer une fenêtre à 4 volets

Quelqu'un peut-il aider à expliquer ce qui se passe avec tmux, bash et exec? J'essaie de configurer une session tmux avec une fenêtre à 4 volets. Idéalement, je veux exécuter une commande dans 3 des volets: par ex. un Ruby serveur mince et quelques Ruby démons. C'est ce que j'ai jusqu'à présent:

~/.bin/tmux-foo:

#!/bin/sh

tmux new-session -d -s foo 'exec pfoo "bundle exec thin start"'
tmux rename-window 'Foo'
tmux select-window -t foo:0
tmux split-window -h 'exec pfoo "bundle exec compass watch"'
tmux split-window -v -t 0 'exec pfoo "rake ts:start"'
tmux split-window -v -t 1 'exec pfoo'
tmux -2 attach-session -t foo

~/.bin/pfoo:

#!/bin/bash
cd ~/projects/foo
rvm use ree

# here I want to execute command1 2 3 or 4...

exec $Shell

Tout fonctionne ... mais quand je ctlr-c dans le premier volet qui exécute le serveur léger, il arrête le serveur léger et revient au shell. Cependant, la commande n'est pas dans l'histoire; c'est-à-dire que si j'appuie sur la touche haut, je n'obtiens pas le bundle exec thin start commande ... J'obtiens une autre commande de mon historique bash. Je me demande s'il y a un moyen d'arranger ces scripts pour que j'obtienne les commandes dans l'historique bash.

Aussi ... J'ai essayé de nombreuses combinaisons de exec, exec $Shell -s ..., et exec $Shell -s ... -I et je ne sais pas trop ce qui se passe ...

Quelqu'un peut-il aider à expliquer ici l'idée générale de ce qui se passe avec tmux et bash et exec?

62
Aaron Gibralter

Comme d'autres l'ont mentionné, vos commandes sont exécutées par le script Shell avant lancement de votre $Shell; il n'y a pas de manière générale l'instance de $Shell peut savoir ce que son parent a exécuté avant de le démarrer.

Pour obtenir la "commande initiale" dans l'historique du shell, vous devez envoyer les frappes de commande directement à l'instance de $Shell lui-même (après son démarrage, bien sûr). Dans d'autres contextes, je pourrais suggérer d'utiliser un petit programme Expect pour générer une instance de $Shell, donnez-lui les touches, puis utilisez interact pour lier le tty au attendez - engendré $Shell.

Mais dans le contexte de tmux, nous pouvons simplement utiliser send-keys:

#!/bin/sh

tmux new-session -d -s foo 'exec pfoo'
tmux send-keys 'bundle exec thin start' 'C-m'
tmux rename-window 'Foo'
tmux select-window -t foo:0
tmux split-window -h 'exec pfoo'
tmux send-keys 'bundle exec compass watch' 'C-m'
tmux split-window -v -t 0 'exec pfoo'
tmux send-keys 'rake ts:start' 'C-m'
tmux split-window -v -t 1 'exec pfoo'
tmux -2 attach-session -t foo
59
Chris Johnsen

tmuxinator vous permet de spécifier cela avec un fichier Nice yaml. Pour votre cas, vous pourriez avoir:

# ~/.tmuxinator/foo.yml
# you can make as many tabs as you wish...

project_name: foo
project_root: ~/projects/foo
rvm: ree
tabs:
  - main:
      layout: tiled
      panes:
        - bundle exec thin start
        - bundle exec compass watch
        - #empty, will just run plain bash
        - rake ts:start

Vous pouvez bien sûr avoir des fenêtres supplémentaires, etc.

14
Hamish Downer

Vous exécutez la commande, puis entrez dans le shell interactif; la commande exécutée à partir du script, n'étant pas dans un shell interactif, n'est pas enregistrée dans l'historique. Vous voulez vraiment un moyen de bourrer (c'est un terme technique :) il était une fois TIOCSTI pour "terminal ioctl (): stuff input") entrée pour le Shell dans la fenêtre.

Avec tmux, il semble que vous utilisiez des tampons pour cela. Quelque chose comme (non testé)

#! /bin/bash
cd ~/projects/foo
rvm use ree

if [[ $# != 0 ]]; then
  tmux set-buffer "$(printf '%s\n' "$*")" \; paste-buffer -d
fi

exec ${Shell:-/bin/sh}
3
geekosaur