tmux en pratique: intégration avec le presse-papier système

Comment créer un pont entre le tampon de copie tmux et le presse-papiers système, et stocker le texte sélectionné sur le presse-papiers système OSX ou Linux, de manière à répondre aux scénarios d'utilisation locaux et distants

Ceci est la 4ème partie de ma série d'articles tmux in practice.

Dans la partie précédente de la série «tmux in practice», nous avons parlé de choses comme le tampon de défilement, le mode de copie et avons légèrement abordé le sujet de la copie de texte dans le tampon de copie de tmux.

Tôt ou tard, vous vous rendrez compte que tout ce que vous copiez dans tmux est stocké uniquement dans le tampon de copie de tmux, mais pas partagé avec le presse-papiers du système. Le copier-coller sont des opérations tellement courantes que cette limitation suffit à elle-même à transformer tmux en une brique inutile, malgré d'autres goodies.

Dans cet article, nous explorerons comment créer un pont entre le tampon de copie tmux et le presse-papiers du système, pour stocker le texte copié dans le presse-papiers du système, d'une manière qui aborde les scénarios d'utilisation locaux et distants.

Nous discuterons des techniques suivantes:

  1. OSX uniquement, partagez du texte avec le presse-papiers en utilisant «pbcopy»
  2. OSX uniquement, en utilisant le wrapper «reattach-to-user-namespace» pour que pbcopy fonctionne correctement dans l'environnement tmux
  3. Linux uniquement, partager du texte avec la sélection X à l'aide des commandes xclipouxsel

Les techniques ci-dessus ne concernent que des scénarios locaux.

Pour prendre en charge les scénarios à distance, il existe 2 méthodes supplémentaires:

  1. Utilisez la séquence d'échappement ANSI OSC 52 pour parler au terminal de contrôle / parent afin de gérer et de stocker du texte dans le presse-papiers d'une machine locale.
  2. Configurez un écouteur de réseau local qui redirige les entrées vers pbcopyou xclipou xsel. Le canal a copié le texte sélectionné de la machine distante vers un écouteur sur la machine locale via le tunneling distant SSH. C'est assez compliqué, et je consacrerai un article dédié pour le décrire.

OSX. Commandes pbcopy et pbpaste

pbcopyet les pbpastecommandes vous permettent d'interagir et de manipuler le presse-papiers du système à partir de la ligne de commande.

pbcopy lit les données stdinet les stocke dans le presse-papiers. pbpastefait le contraire et met le texte copié stdout.

L'idée est de se connecter à diverses commandes tmux, qui parviennent à copier du texte en mode copie.

Listons-les:

$ tmux -f /dev/null list-keys -T copy-mode-vi
bind-key -T copy-mode-vi Enter send-keys -X copy-selection-and-cancelbind-key -T copy-mode-vi C-j send-keys -X copy-selection-and-cancelbind-key -T copy-mode-vi D send-keys -X copy-end-of-linebind-key -T copy-mode-vi MouseDragEnd1Pane send-keys -X copy-selection-and-cancelbind-key -T copy-mode-vi A send-keys -X append-selection-and-cancel

copy-selection-and-cancelet copy-end-of-linesont des commandes tmux spéciales que tmux comprend lorsque le volet est en mode copie. Il existe deux types de commande de copie: copy-selectionet copy-pipe.

La réécriture Let Enterraccourci clavier avec commande tuyau de copie:

bind -T copy-mode-vi Enter send-keys -X copy-pipe-and-cancel "pbcopy"

copy-pipeLa commande stocke le texte sélectionné dans le tampon tmux de la même manière que copy-selection, plus le texte sélectionné est dirigé vers la commande donnée pbcopy. Nous obtenons donc du texte stocké à deux endroits: le tampon de copie tmux et le presse-papiers système.

OSX. wrapper d'espace de noms réattaché à l'utilisateur

Jusqu'ici tout va bien. Cependant, sur certaines versions d'OSX, pbcopyet pbpaste ne fonctionne pas correctement lorsqu'il est exécuté sous tmux.

Lisez plus de détails de Chris Johnsen sur pourquoi cela se produit:

tmux utilise la fonction de bibliothèque daemon (3) lors du démarrage de son processus serveur. Sous Mac OS X 10.5, Apple a modifié le démon (3) pour déplacer le processus résultant de son espace de noms d'amorçage d'origine vers l'espace de noms d'amorçage racine. Cela signifie que le serveur tmux et ses enfants perdront automatiquement et de manière incontrôlable l'accès à ce qui aurait été leur espace de noms d'amorçage d'origine (c'est-à-dire celui qui a accès au service de presse-papiers).

Une solution courante consiste à utiliser un wrapper d'espace de noms reattach-to-user. Cela nous permet de lancer un processus et d'attacher ce processus à l'espace de noms de bootstrap par utilisateur, ce qui fait que le programme se comporte comme prévu. Vous devez modifier correctement la combinaison de touches:

bind -T copy-mode-vi Enter send-keys -X copy-pipe-and-cancel “reattach-to-user-namespace pbcopy”

De plus, vous devrez dire à tmux d'exécuter votre shell (bash, zsh,…) dans un wrapper, en définissant l' default-commandoption:

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)\"'"

Remarque : certaines versions d'OSX fonctionnent bien même sans ce hack (OSX 10.11.5 El Capitan), alors que les utilisateurs d'OSX Sierra signalent que ce hack est toujours nécessaire.

Linux. Interagir avec la sélection X via xclip et xsel

Nous pouvons utiliser les commandes xclipou xselsous Linux pour stocker du texte dans le presse-papiers, comme pbcopysur OSX. Sous Linux, il existe plusieurs types de sélections de presse-papiers gérées par le serveur X: primaire, secondaire et presse-papiers. Nous ne concernons que le primaire et le presse-papiers. Le secondaire était conçu comme une alternative au primaire.

bind -T copy-mode-vi Enter send-keys -X copy-pipe-and-cancel "xclip -i -f -selection primary | xclip -i -selection clipboard"

Ou lors de l'utilisation xsel:

bind -T copy-mode-vi Enter send-keys -X copy-pipe-and-cancel "xsel -i --clipboard"

Lisez ici à propos de la comparaison de xclipvs xsel, si vous êtes curieux. Consultez également cet article sur l' xcliputilisation et les exemples. Et n'oubliez pas d'installer l'un de ces utilitaires, car ils pourraient ne pas faire partie de votre distribution.

Utilisation de la séquence d'échappement ANSI OSC 52 pour que le terminal stocke du texte dans le presse-papiers

Jusqu'à présent, nous n'avons couvert que des scénarios locaux. Lorsque vous SSH à la machine à distance, et commencer à tmux sessions là, vous ne pouvez pas utiliser pbcopy, xclipou xselparce que le texte sera stocké dans le presse - papiers de la machine à distance, pas dans votre local. Vous avez besoin d'un moyen de transporter le texte copié dans le presse-papiers de votre machine locale.

La séquence d'échappement ANSI est une séquence d'octets envoyés au terminal qui sont entrelacés avec des caractères imprimables normaux, et sont utilisés pour contrôler divers aspects du terminal: tels que les couleurs du texte, la position du curseur, les effets de texte, l'écran d'effacement. Le terminal est capable de détecter une telle séquence de contrôle d'octets qui l'amène à déclencher des actions spécifiques et à ne pas imprimer ces caractères sur la sortie.

La séquence d'échappement ANSI peut être détectée lorsqu'elle commence par ESCle caractère ASCII (0x1b hex, 027 décimal, \ 033 en octal). Par exemple, lorsque le terminal voit la \033[2Aséquence, il déplacera la position du curseur de 2 lignes vers le haut.

Il y a vraiment beaucoup de ces séquences connues. Certains d'entre eux sont identiques pour différents types de terminaux, tandis que d'autres peuvent varier et être très spécifiques à votre émulateur de terminal. Utilisez la infocmpcommande pour interroger la terminfobase de données sur les séquences d'échappement prises en charge par différents types de terminaux.

Très bien, mais comment cela peut-il nous aider concernant le presse-papiers? Il s'avère qu'il existe une catégorie spéciale de séquences d'échappement: «Operating System Controls» (OSC) et la séquence d'échappement «OSC 52», qui permet aux applications d'interagir avec le presse-papiers.

Si vous utilisez iTerm, essayez d'exécuter la commande suivante, puis « ⌘V» pour voir le contenu du presse-papiers du système. Assurez-vous d'activer la gestion des séquences d'échappement OSC 52: «Préférences -> Général -> Les applications du terminal peuvent accéder au presse-papiers».

printf "\033]52;c;$(printf "%s" "blabla" | base64)\a"

La conclusion est que nous pouvons stocker du texte dans le presse-papiers du système en envoyant une séquence d'échappement ANSI spécialement conçue à notre terminal.

Écrivons le script shell yank.sh:

#!/bin/bash
set -eu
# get data either form stdin or from filebuf=$(cat "[email protected]")
# Get buffer lengthbuflen=$( printf %s "$buf" | wc -c )
maxlen=74994
# warn if exceeds maxlenif [ "$buflen" -gt "$maxlen" ]; then printf "input is %d bytes too long" "$(( buflen - maxlen ))" >&2fi
# build up OSC 52 ANSI escape sequenceesc="\033]52;c;$( printf %s "$buf" | head -c $maxlen | base64 | tr -d '\r\n' )\a"

Donc, nous lisons le texte à copier stdin, puis vérifions si sa longueur dépasse la longueur maximale de 74994 octets. Si c'est vrai, nous le recadrons, puis convertissons les données en base64 et enveloppons dans la séquence d'échappement OSC 52:\033]53;c;${data_in_base64}\a

Alors connectons-le avec nos raccourcis clavier tmux. C'est assez simple: dirigez simplement le texte sélectionné vers notre yank.shscript, tout comme nous le dirigeons vers pbcopyou xclip.

yank="~/.tmux/yank.sh"
bind -T copy-mode-vi Enter send-keys -X copy-pipe-and-cancel "$yank"

Cependant, il reste une pièce pour terminer le puzzle. Où devons-nous envoyer la séquence d'échappement? Apparemment, le simple fait de l'envoyer stdoutne fonctionnera pas. La cible devrait être notre émulateur de terminal parent, mais nous ne connaissons pas la bonne tty. Donc, nous allons l'envoyer au volet actif de tmux ttyet dire à tmux de le renvoyer davantage à l'émulateur de terminal parent:

# build up OSC 52 ANSI escape sequenceesc="\033]52;c;$( printf %s "$buf" | head -c $maxlen | base64 | tr -d '\r\n' )\a"esc="\033Ptmux;\033$esc\033\\"
pane_active_tty=$(tmux list-panes -F "#{pane_active} #{pane_tty}" | awk '$1=="1" { print $2 }')
printf "$esc" > "$pane_active_tty"

Nous utilisons la tmux list-panescommande pour interroger le volet actif et c'est tty. Nous avons également mis notre séquence OSC 52 dans une séquence d'échappement supplémentaire (Device Control String, ESC P), donc tmux déballe cette enveloppe et passe OSC 52 au terminal parent.

Dans les versions plus récentes de tmux, vous pouvez dire à tmux de gérer les interactions avec le presse-papiers à votre place. Voir l' set-clipboardoption tmux. on - tmux créera un tampon interne et tentera de définir le presse-papiers du terminal en utilisant OSC 52. external - ne créez pas de tampon, mais tentez toujours de définir le presse-papiers du terminal.

Assurez-vous simplement que c'est soit externalou on:

set -g set-clipboard on

Donc, si tmux est déjà capable de cette fonctionnalité, pourquoi nous devons nous embêter avec le câblage manuel des trucs OSC 52? C'est parce que set-clipboardcela ne fonctionne pas lorsque vous avez une session tmux distante imbriquée dans une session locale. Et cela ne fonctionne que dans les terminaux qui prennent en charge la gestion des séquences d'échappement OSC 52.

L'astuce pour les sessions distantes imbriquées est de contourner la session distante et d'envoyer notre séquence d'échappement OSC 52 directement à la session locale, afin qu'elle atteigne notre émulateur de terminal local (iTerm).

Utilisez $SSH_TTYà cette fin:

# resolve target terminal to send escape sequence# if we are on remote machine, send directly to SSH_TTY to transport escape sequence# to terminal on local machine, so data lands in clipboard on our local machinepane_active_tty=$(tmux list-panes -F "#{pane_active} #{pane_tty}" | awk '$1=="1" { print $2 }')target_tty="${SSH_TTY:-$pane_active_tty}"
printf "$esc" > "$target_tty"

C'est ça. Nous avons maintenant une solution complètement fonctionnelle, que ce soit une session locale, distante ou les deux, imbriquées l'une dans l'autre. Crédits à cet excellent article, où j'ai lu pour la première fois sur cette approche.

L'inconvénient majeur de l'utilisation des séquences d'échappement OSC est que, bien que déclarés dans les spécifications, seuls quelques terminaux le supportent en pratique: iTerm et xterm le font, contrairement aux terminaux OSX, Terminator et Gnome. Ainsi, une solution autre grande ( en particulier dans les scénarios à distance, quand vous ne pouvez pas pipeà xclipou pbcopy) manque support de borne plus large.

You might want to checkout complete version of yank.sh script.

There is yet another solution to support remote scenarios, which is rather crazy, and I’ll describe it in another dedicated post. The idea is to setup a local network listener which pipes input to pbcopy or xclipor xsel; and pipes copied selected text from a remote machine to a listener on the local machine through SSH remote tunneling. Stay tuned.

Resources and links

ANSI escape code — Wikipedia — //en.wikipedia.org/wiki/ANSI_escape_code#Escape_sequences

What are OSC terminal control sequences / escape codes? | ivucica blog — //blog.vucica.net/2017/07/what-are-osc-terminal-control-sequences-escape-codes.html

Copying to clipboard from tmux and Vim using OSC 52 — The Terminal Programmer — //sunaku.github.io/tmux-yank-osc52.html

Copy Shell Prompt Output To Linux / UNIX X Clipboard Directly — nixCraft — //www.cyberciti.biz/faq/xclip-linux-insert-files-command-output-intoclipboard/

software recommendation — ‘xclip’ vs. ‘xsel’ — Ask Ubuntu — //askubuntu.com/questions/705620/xclip-vs-xsel

Everything you need to know about Tmux copy paste · rushiagr — //www.rushiagr.com/blog/2016/06/16/everything-you-need-to-know-about-tmux-copy-pasting/

macos — Synchronize pasteboard between remote tmux session and local Mac OS pasteboard — Super User — //superuser.com/questions/407888/synchronize-pasteboard-between-remote-tmux-session-and-local-mac-os-pasteboard/408374#408374

linux — Getting Items on the Local Clipboard from a Remote SSH Session — Stack Overflow — //stackoverflow.com/questions/1152362/getting-items-on-the-local-clipboard-from-a-remote-ssh-session

Use tmux set-clipboard in gnome-terminal (XTerm’s disallowedWindowOps) — Ask Ubuntu — //askubuntu.com/questions/621522/use-tmux-set-clipboard-in-gnome-terminal-xterms-disallowedwindowops/621646