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:
- Csak OSX, ossza meg a szöveget a vágólappal a „pbcopy” használatával
- 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
- Csak Linux, ossza meg a szöveget X választással
xclip
vagyxsel
parancsokkal
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:
- 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.
- Beállítás a helyi hálózaton figyelőt, csövek bemenet
pbcopy
vagyxclip
, vagyxsel
. 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 pbpaste
parancsok 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. pbpaste
az 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-line
speciá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-pipe
a 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-command
opció 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 xclip
vagy xsel
parancsokat Linux alatt a szöveg tárolására a vágólapon, ugyanúgy, mint pbcopy
az 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 xclip
vs. összehasonlítását xsel
. Nézze meg ezt a bejegyzést a xclip
felhaszná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
, xclip
vagy 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 ESC
ASCII karakterrel kezdődnek (0x1b hex, 027 decimális, \ 033 oktális). Például, amikor a terminál meglátja a \033[2A
sorrendet, 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 infocmp
paranccsal lekérdezheti az terminfo
adatbá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.sh
szkriptünkhöz, ahogyan a pbcopy
vagy 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 xclip
or 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