de Alexey Samoshkin

tmux în practică: integrare cu clipboard-ul sistemului

Cum să construiți o punte între tamponul de copiere tmux și clipboard-ul sistemului și să stocați textul selectat în clipboard-ul sistemului OSX sau Linux, într-un mod care să abordeze atât scenariile de utilizare locale, cât și cele de la distanță

Aceasta este a patra parte a mea tmux în practică serie de articole.

tmux in practica integrare cu clipboardul sistemului
Puteți copia text din sesiune locală sau de la distanță, sau chiar imbricată la distanță în clipboard-ul sistemului

În partea anterioară a „tmux în practică” serii am vorbit despre lucruri cum ar fi bufferul scrollback, modul copiere și am atins ușor subiectul copierii textului în tamponul de copiere tmux.

Mai devreme sau mai târziu veți realiza că orice copiați în tmux este stocat numai în memoria tampon de copiere tmux, dar nu este partajat cu clipboard-ul sistemului. Copierea și lipirea sunt operațiuni atât de comune, încât această limitare este suficientă pentru a transforma tmux într-o cărămidă inutilă, în ciuda altor bunătăți.

În acest post vom explora cum să construim o punte între tamponul de copiere tmux și clipboard-ul sistemului, pentru a stoca textul copiat în clipboard-ul sistemului, într-un mod care să abordeze atât scenariile de utilizare locale, cât și cele de la distanță.

Vom discuta despre următoarele tehnici:

  1. Numai OSX, partajați text cu clipboard folosind „pbcopy”
  2. Numai OSX, folosind împachetarea „reattach-to-user-namespace” pentru a face pbcopy să funcționeze corect în mediul tmux
  3. Numai Linux, partajați text cu selecția X folosind xclip sau xsel comenzi

Tehnicile de mai sus abordează doar scenarii locale.
Pentru a susține scenarii la distanță, există 2 metode suplimentare:

  1. Folosește ANSI OSC 52 evadare secvenţă pentru a vorbi cu controlerul / terminalul părinte pentru a gestiona și stoca textul pe un clipboard al unei mașini locale.
  2. Configurați un ascultător de rețea locală pe care canalizează intrarea pbcopy sau xclipsau xsel. Pipe a copiat textul selectat de pe aparatul de la distanță către un ascultător de pe aparatul local prin tunelare la distanță SSH. Acest lucru este destul de implicat și voi dedica o postare dedicată pentru a o descrie.

OSX. comenzi pbcopy și pbpaste

pbcopy și pbpaste comenzile vă permit să interacționați și să manipulați clipboardul sistemului din linia de comandă.

pbcopy citește date din stdin și o stochează în clipboard. pbpaste face opusul și pune textul copiat stdout.

Ideea este să vă conectați la diverse comenzi tmux, care reușesc să copieze text în timp ce se află în modul copiere.

Să le enumerăm:

$ 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 și copy-end-of-line sunt comenzi tmux speciale pe care tmux le înțelege când panoul este în modul copiere. Există două arome de comandă copiere: copy-selection și copy-pipe.

Să rescriem Enter legarea tastelor cu comanda copy-pipe:

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

copy-pipe comanda stochează textul selectat în tamponul tmux la fel copy-selection, plus țevi textul selectat la comanda dată pbcopy. Deci, obținem text stocat în două locuri: tamponul de copiere tmux și clipboard-ul sistemului.

OSX. reatașați împachetarea spațiului de nume utilizatorului

Până acum, bine. Cu toate acestea, pe unele versiuni de OSX, pbcopy și pbpaste nu funcționează corect atunci când rulează sub tmux.

Citit mai multe detalii de la Chris Johnsen despre de ce se întâmplă:

tmux folosește funcția de bibliotecă daemon (3) la pornirea procesului de server. În Mac OS X 10.5, Apple a schimbat daemon-ul (3) pentru a muta procesul rezultat din spațiul de nume inițial bootstrap în spațiul de nume root bootstrap. Acest lucru înseamnă că serverul tmux și copiii săi vor pierde automat și necontrolat accesul la ceea ce ar fi fost spațiul de nume bootstrap original (adică cel care are acces la serviciul de carton).

O soluție obișnuită este utilizarea reatașați spațiul de nume utilizator împachetare. Acest lucru ne permite să lansăm un proces și să-l atașăm la spațiul de nume bootstrap per utilizator, ceea ce face ca programul să se comporte așa cum ne așteptăm. Trebuie să schimbați corect legarea tastelor:

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

În plus, ar trebui să-i spuneți tmux să vă ruleze shell-ul (bash, zsh, …) în interiorul unui wrapper, prin setarea default-command opțiune:

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

Notă: unele versiuni OSX funcționează bine chiar și fără acest hack (OSX 10.11.5 El Capitan), în timp ce utilizatorii OSX Sierra raportați că acest hack este încă necesar.

Linux. Interacționați cu selecția X prin xclip și xsel

Putem folosi xclip sau xsel comenzi pe Linux pentru a stoca text în clipboard, la fel ca pbcopy pe OSX. Pe Linux, există mai multe tipuri de selecții din clipboard întreținut de serverul X: primar, secundar și clipboard. Ne preocupăm doar de primar și de clipboard. Secundar a fost conceput ca alternativ la primar.

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

Sau atunci când utilizați xsel:

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

Citiți aici despre compararea de xclip vs. xsel, dacă ești curios. De asemenea, verificați această postare pe xclip utilizare și exemple. Și nu uitați să instalați unul dintre aceste utilitare, deoarece este posibil să nu facă parte din distribuția dvs.

Utilizarea secvenței de evacuare ANSI OSC 52 pentru a determina terminalul să stocheze textul în clipboard

Până acum am acoperit doar scenarii locale. Când trimiteți SSH la mașină la distanță și porniți sesiuni tmux acolo, nu puteți utiliza pbcopy, xclip sau xsel, deoarece textul va fi stocat în clipboard-ul aparatului la distanță, nu în cel local. Aveți nevoie de o modalitate de a transporta textul copiat în clipboard-ul mașinii dvs. locale.

Secvență de evadare ANSI este o secvență de octeți trimiși la terminal care sunt intercalate cu caractere tipărite obișnuite și sunt utilizate pentru a controla diferite aspecte ale terminalului: cum ar fi culorile textului, poziția cursorului, efectele textului, ecranul de curățare. Terminalul este capabil să detecteze o astfel de secvență de control a octeților care îl determină să declanșeze acțiuni specifice și să nu imprime acele caractere pe ieșire.

Secvența de evacuare ANSI poate fi detectată la început ESC Caracter ASCII (0x1b hex, 027 zecimal, 033 în octal). De exemplu, când terminalul vede fișierul 033[2A sequence, it will move the cursor position 2 lines up.

There are really a lot of those known sequences. Some of them are the same across different terminal types, while others can vary and be very specific to your terminal emulator. Useinfocmp command to query terminfo database for escape sequences supported by different types of terminals.

Okay great, but how can it help us regarding the clipboard? It turns out that there is a special category of escape sequences: “Operating System Controls” (OSC) and the “OSC 52″ escape sequence, which allows applications to interact with the clipboard.

If you’re using iTerm, try to execute following command, and then “⌘V” to see contents of system clipboard. Make sure to turn on OSC 52 escape sequence handling: “Preferences -> General -> Applications in terminal may access clipboard”.

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

Concluzia este că putem stoca text în clipboard-ul sistemului, trimițând o secvență de evacuare ANSI special concepută la terminalul nostru.

Să scriem scriptul shell yank.sh:

#!/bin/bash
set -eu
# get data either form stdin or from filebuf=$(cat "$@")
# 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="33]52;c;$( printf %s "$buf" | head -c $maxlen | base64 | tr -d 'rn' )a"

Deci, citim text din care să copiem stdin, apoi verificați dacă lungimea acestuia depășește lungimea maximă de 74994 octeți. Dacă este adevărat, o decupăm și, în cele din urmă, convertim datele în base64 și înfășurăm în secvența de evacuare OSC 52: 33]53;c;${data_in_base64}a

Atunci să-l conectăm cu legăturile noastre de taste tmux. Este destul de ușor: pur și simplu trimiteți textul selectat la yank.sh script, exact așa cum îl facem pbcopy sau xclip.

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

Cu toate acestea, a rămas o singură piesă pentru a finaliza puzzle-ul. Unde ar trebui să trimitem secvența de evadare? Se pare că tocmai îl trimiteți la stdout nu va funcționa. Ținta ar trebui să fie emulatorul nostru de terminal părinte, dar nu știm bine tty. Deci, îl vom trimite la panoul activ tmux tty, și spuneți tmux să îl retransmită în continuare la emulatorul terminalului părinte:

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

Folosim tmux list-panes comanda pentru interogarea panoului activ și este tty. De asemenea, punem secvența OSC 52 într-o secvență de evacuare suplimentară (Device Control String, ESC P), așa că tmux desfășoară acest plic și trece OSC 52 la terminalul părinte.

În versiunile mai noi de tmux, puteți spune tmux să gestioneze interacțiunile cu clipboardul pentru dvs. Vedeaset-clipboard opțiunea tmux. on – tmux va crea un buffer interior și va încerca să seteze clipboard-ul terminalului folosind OSC 52. external – nu creați un buffer, dar încercați totuși să setați clipboard-ul terminalului.

Asigurați-vă că este oricare external sau on:

set -g set-clipboard on

Deci, dacă tmux este deja capabil de această caracteristică, de ce trebuie să ne deranjăm cu cabluri manuale OSC 52? Asta-i pentru că set-clipboard nu funcționează când aveți o sesiune tmux la distanță cuibărită într-una locală. Și funcționează numai în acelea terminale care acceptă manipularea secvenței de evacuare OSC 52.

Trucul pentru sesiunile la distanță imbricate este să ocoliți sesiunea la distanță și să trimiteți secvența de evacuare OSC 52 direct la sesiunea locală, astfel încât să atingă emulatorul nostru terminal local (iTerm).

Utilizare $SSH_TTY în acest scop:

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

Asta este. Acum avem o soluție complet funcțională, fie că este o sesiune locală, la distanță sau ambele, cuibărite una în cealaltă. Credite pentru această postare grozavă, unde am citit prima dată despre această abordare.

Dezavantajul major al utilizării secvențelor de evacuare OSC este că, în ciuda faptului că a fost declarat în specificații, doar câteva terminale acceptă acest lucru în practică: iTerm și xterm o fac, în timp ce terminalele OSX, Terminator și Gnome nu. Deci, o altă soluție excelentă (mai ales în scenarii îndepărtate, atunci când nu puteți doar pipe la xclip sau pbcopy) nu are suport terminal mai larg.

S-ar putea să doriți Verificați versiunea completă de yank.sh scenariu.

Există încă o altă soluție pentru a susține scenarii la distanță, care este destul de nebună, și o voi descrie într-o altă post dedicat. Ideea este să configurați un ascultător de rețea locală la care să treacă intrarea pbcopy sau xclipsau xsel; și conductele au copiat textul selectat de la o mașină la distanță către un ascultător de pe mașina locală prin tunelare la distanță SSH. Rămâneți aproape.

Cod de evadare ANSI – Wikipedia – https://en.wikipedia.org/wiki/ANSI_escape_code#Escape_sequences

Ce sunt secvențele de control terminal OSC / codurile de evacuare? | blogul ivucica – https://blog.vucica.net/2017/07/what-are-osc-terminal-control-sequences-escape-codes.html

Copierea în clipboard de la tmux și Vim folosind OSC 52 – Terminal Programmer – https://sunaku.github.io/tmux-yank-osc52.html

Copiați ieșirea promptă a Shell în Linux / UNIX X Clipboard direct – nixCraft – https://www.cyberciti.biz/faq/xclip-linux-insert-files-command-output-intoclipboard/

recomandare software – „xclip” vs. „xsel” – Întrebați Ubuntu – https://askubuntu.com/questions/705620/xclip-vs-xsel

Tot ce trebuie să știți despre Tmux copy paste · rushiagr – http://www.rushiagr.com/blog/2016/06/16/everything-you-need-to-know-about-tmux-copy-pasting/

macos – Sincronizați tabloul între sesiunea tmux la distanță și tabloul local Mac OS – Super User – https://superuser.com/questions/407888/synchronize-pasteboard-between-remote-tmux-session-and-local-mac-os-pasteboard/408374#408374

linux – Obținerea articolelor în clipboardul local dintr-o sesiune SSH de la distanță – Stack Overflow – https://stackoverflow.com/questions/1152362/getting-items-on-the-local-clipboard-from-a-remote-ssh-session

Utilizați tmux set-clipboard în gnome-terminal (XTerm’s disallowedWindowOps) – Întrebați Ubuntu – https://askubuntu.com/questions/621522/use-tmux-set-clipboard-in-gnome-terminal-xterms-disallowedwindowops/621646