tmux a gyakorlatban: integráció a rendszer vágólapjával

Hogyan lehet hidat építeni a tmux másolási puffer és a rendszer vágólapja között, és hogyan lehet a kiválasztott szöveget tárolni OSX vagy Linux rendszer vágólapjára, úgy, hogy az megfeleljen a helyi és a távoli felhasználási forgatókönyveknek is

Ez a tmuxom 4. része a gyakorlati cikksorozatban.

A „tmux in practice” sorozat előző részében olyan dolgokról beszéltünk, mint a görgetési puffer, a másolási mód, és kissé kitértünk a szöveg másolásának témájára a tmux másolási pufferjében.

Előbb vagy utóbb rájön, hogy bármi, amit a tmux-ban másol, csak a tmux másolási pufferében kerül tárolásra, de nem osztható meg a rendszer vágólapjával. A másolás és beillesztés olyan gyakori műveletek, hogy ez a korlátozás önmagában elegendő ahhoz, hogy a tmux-ot haszontalan téglává tegye, egyéb finomságok ellenére.

Ebben a bejegyzésben azt vizsgáljuk, hogyan lehet hidat építeni a tmux másolási puffer és a rendszer vágólapja között, hogyan lehet a másolt szöveget a rendszer vágólapjára tárolni, oly módon, hogy mind a helyi, mind a távoli felhasználási forgatókönyveket kezelje.

Megbeszéljük a következő technikákat:

  1. Csak OSX, ossza meg a szöveget a vágólappal a „pbcopy” használatával
  2. Csak OSX, az „újracsatolás a felhasználói névtérhez” csomagoló segítségével a pbcopy megfelelő működéshez a tmux környezetben
  3. Csak Linux, ossza meg a szöveget X választással xclipvagy xselparancsokkal

A fenti technikák csak a helyi forgatókönyvekre vonatkoznak.

A távoli forgatókönyvek támogatásához két további módszer létezik:

  1. Az ANSI OSC 52 menekülési szekvenciával beszélhet a vezérlővel / szülőterminállal a szöveg kezeléséhez és tárolásához a helyi gép vágólapján.
  2. Beállítás a helyi hálózaton figyelőt, csövek bemenet pbcopyvagy xclip, vagy xsel. Pipe másolta a kijelölt szöveget a távoli gépről a helyi gép hallgatójához az SSH távoli alagút segítségével. Ez inkább érintett, és ennek szentelésére dedikált bejegyzést fogok szentelni.

OSX. pbcopy és pbpaste parancsokat

pbcopyés a pbpasteparancsok lehetővé teszik a rendszer vágólapjának interakcióját és kezelését a parancssorból.

pbcopy adatokat olvas le stdinés tárol a vágólapon. pbpasteaz ellenkezőjét teszi, és ráteszi a másolt szöveget stdout.

Az ötlet az, hogy különféle tmux parancsokba kapcsoljon, amelyek másolás módban képesek szöveget másolni.

Soroljuk fel őket:

$ 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-cancelés copy-end-of-linespeciális tmux parancsok, amelyeket a tmux megért, amikor a panel másolás módban van. A másolás parancsnak két íze van: copy-selectionés copy-pipe.

EnterÍrjuk át a billentyűkötést copy-pipe paranccsal:

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

copy-pipea parancs a kijelölt szöveget a tmux pufferben tárolja copy-selection, és plusz a kijelölt szöveget az adott parancshoz kapcsolja pbcopy. Tehát két helyen tárolunk szöveget: a tmux másolási pufferben és a rendszer vágólapján.

OSX. csatolja újra a felhasználó-névtér burkolót

Eddig jó. Azonban egyes változatai OSX, pbcopyés pbpaste nem működik megfelelően, ha fut a tmux.

Olvassa el Chris Johnsen további részleteit, hogy miért történik:

A tmux a daemon (3) könyvtár függvényt használja a szerver folyamat indításakor. A Mac OS X 10.5-ben az Apple megváltoztatta a (3) démont, hogy a kapott folyamatot az eredeti bootstrap névtérről a root bootstrap névtérre helyezze át. Ez azt jelenti, hogy a tmux szerver és gyermekei automatikusan és ellenőrizhetetlenül elveszítik hozzáférésüket az eredeti bootstrap névtérükhöz (vagyis ahhoz, amely hozzáfér a karton szolgáltatáshoz).

Gyakori megoldás az újracsatolás a felhasználó-névtérbe csomagolással. Ez lehetővé teszi számunkra, hogy elindítsunk egy folyamatot, és azt a folyamatot csatoljuk a felhasználónkénti bootstrap névtérhez, ami arra készteti a programot, amire számítunk. Meg kell változtatnia a billentyűkötést megfelelően:

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

Ráadásul meg kell mondania a tmux-nak, hogy futtassa a shelljét (bash, zsh,…) egy burkolóban, az default-commandopció beállításával:

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

Megjegyzés : Egyes OSX verziók e hack nélkül is jól működnek (OSX 10.11.5 El Capitan), míg az OSX Sierra felhasználói szerint erre a feltörésre még mindig szükség van.

Linux. Kölcsönhatásba léphet az X kiválasztásával az xclip és az xsel segítségével

Használhatunk xclipvagy xselparancsokat Linux alatt a szöveg tárolására a vágólapon, ugyanúgy, mint pbcopyaz OSX-en. A Linux rendszeren az X szerver fenntartja a vágólap többféle választását: elsődleges, másodlagos és vágólap. Csak az elsődleges és a vágólapra vonatkozunk. A másodlagos az elsődleges alternatívája volt.

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

Vagy a következő használatakor xsel:

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

Ha kíváncsi, olvassa el a xclipvs. összehasonlítását xsel. Nézze meg ezt a bejegyzést a xclipfelhasználásról és a példákról is. És ne felejtse el telepíteni az egyik ilyen segédprogramot, mert lehet, hogy nem tartoznak a terjesztéshez.

Az ANSI OSC 52 menekülési sorrendjének használatával a terminál szöveget tárol a vágólapra

Eddig csak a helyi forgatókönyveket ismertettük. Amikor SSH-t használ a távoli géphez, és ott indítja a tmux munkameneteket, akkor nem használhatja pbcopy, xclipvagy xsel, mivel a szöveget a távoli gép vágólapjára, nem pedig a helyi tárolja. Szüksége van valamilyen módon a másolt szövegnek a helyi gép vágólapjára történő továbbítására.

Az ANSI menekülési szekvencia a terminálnak küldött bájtsorozat, amelyet szabályos nyomtatható karakterekkel átszövekeznek, és amelyeket a terminál különféle aspektusainak vezérlésére használnak: például a szöveg színei, a kurzor helye, szövegeffektusok, a képernyő törlése. A terminál képes olyan bájtsorozatot észlelni, amely bizonyos műveleteket vált ki, és nem nyomtatja ki ezeket a karaktereket a kimenetre.

Az ANSI menekülési szekvencia észlelhető, amikor ESCASCII karakterrel kezdődnek (0x1b hex, 027 decimális, \ 033 oktális). Például, amikor a terminál meglátja a \033[2Asorrendet, felfelé mozgatja a kurzor 2. pozícióját.

Nagyon sok ilyen ismert szekvencia létezik. Némelyik azonos a különböző termináltípusokban, míg mások változhatnak, és nagyon specifikusak lehetnek a terminálemulátorodra. A infocmpparanccsal lekérdezheti az terminfoadatbázist a különböző típusú terminálok által támogatott menekülési szekvenciákról.

Oké, nagyszerű, de hogyan segíthet nekünk a vágólapra vonatkozóan? Kiderült, hogy van egy speciális kategóriája a menekülési szekvenciáknak: „Operációs rendszer vezérlők” (OSC) és „OSC 52” menekülési sorrend, amelyek lehetővé teszik az alkalmazások számára, hogy kölcsönhatásba lépjenek a vágólappal.

Ha iTerm-et használ, akkor próbálja meg végrehajtani a következő parancsot, majd a „ ⌘V” billentyűt a rendszer vágólapjának tartalmának megtekintéséhez. Győződjön meg róla, hogy bekapcsolta az OSC 52 menekülési sorrend kezelését: „Beállítások -> Általános -> A terminálon lévő alkalmazások hozzáférhetnek a vágólaphoz”.

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

A következtetés az, hogy szöveget tárolhatunk a rendszer vágólapján, ha egy speciálisan kialakított ANSI menekülési szekvenciát küldünk a terminálunkhoz.

Írjuk meg a shell parancsfájlt 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"

Tehát olvassuk a szöveget, ahonnan másolunk stdin, majd ellenőrizzük, hogy a hossza meghaladja-e a maximális 74994 bájtot. Ha igaz, levágjuk, végül konvertáljuk az adatokat base64-be, és beburkoljuk az OSC 52 menekülési sorrendjébe:\033]53;c;${data_in_base64}\a

Akkor kösd össze a tmux billentyűzárunkkal. Ez meglehetősen egyszerű: csak pipálja a kiválasztott szöveget a yank.shszkriptünkhöz, ahogyan a pbcopyvagy a pipához is xclip.

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

However, there is one piece left to complete the puzzle. Where should we send the escape sequence? Apparently, just sending it to stdout won’t work. The target should be our parent terminal emulator, but we don’t know the right tty. So, we’re going to send it to tmux’s active pane tty, and tell tmux to further resend it to the parent terminal emulator:

# 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"

We use tmux list-panes command to query for the active pane and it’s tty. We also put our OSC 52 sequence in an additional wrapper escape sequence (Device Control String, ESC P), so tmux unwraps this envelope and passes OSC 52 to parent terminal.

In newer versions of tmux, you can tell tmux to handle interactions with the clipboard for you. Seeset-clipboard tmux option. on — tmux will create an inner buffer and attempt to set the terminal clipboard using OSC 52. external — do not create a buffer, but still attempt to set the terminal clipboard.

Just make sure it’s either external or on:

set -g set-clipboard on

So, if tmux is already capable of this feature, why we need to bother ourselves with manual wiring OSC 52 stuff? That’s because set-clipboard does not work when you have a remote tmux session nested in a local one. And it only works in those terminals which supports OSC 52 escape sequence handling.

The trick for nested remote sessions is to bypass the remote session and send our OSC 52 escape sequence directly to the local session, so it hits our local terminal emulator (iTerm).

Use $SSH_TTY for this purpose:

# 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"

That’s it. Now we have a completely working solution, be it a local session, remote or both, nested in each other. Credits to this great post, where I first read about this approach.

The major drawback of using OSC escape sequences,is that despite being declared in spec, only a few terminals support this in practice: iTerm and xterm do, whereas OSX Terminal, Terminator, and Gnome terminal does not. So, an otherwise great solution (especially in remote scenarios, when you cannot just pipe to xclip or pbcopy) lacks wider terminal support.

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