tmux عمليًا: التكامل مع حافظة النظام

كيفية بناء جسر بين المخزن المؤقت لنسخ tmux وحافظة النظام ، وتخزين النص المحدد على حافظة نظام OSX أو Linux ، بطريقة تعالج سيناريوهات الاستخدام المحلي والبعيد

هذا هو الجزء الرابع من سلسلة مقالات tmux الخاصة بي.

في الجزء السابق من سلسلة "tmux in Practice" تحدثنا عن أشياء مثل المخزن المؤقت للتمرير ، ووضع النسخ ، وتطرقنا قليلاً إلى موضوع نسخ النص إلى مخزن النسخ المؤقت لـ tmux.

عاجلاً أم آجلاً ، ستدرك أن كل ما تنسخه في tmux يتم تخزينه في مخزن النسخ المؤقت لـ tmux فقط ، ولكن لا تتم مشاركته مع حافظة النظام. تعد عمليات النسخ واللصق عمليات شائعة ، بحيث يكون هذا القيد بحد ذاته كافياً لتحويل tmux إلى لبنة عديمة الفائدة ، على الرغم من الأشياء الجيدة الأخرى.

سنستكشف في هذا المنشور كيفية بناء جسر بين المخزن المؤقت لنسخ tmux وحافظة النظام ، لتخزين النص المنسوخ في حافظة النظام ، بطريقة تعالج سيناريوهات الاستخدام المحلي والبعد.

سنناقش التقنيات التالية:

  1. OSX فقط ، مشاركة النص مع الحافظة باستخدام "pbcopy"
  2. OSX فقط ، باستخدام المجمع "reattach-to-user-namespace" لجعل pbcopy يعمل بشكل صحيح داخل بيئة tmux
  3. Linux فقط ، شارك النص مع تحديد X باستخدام xclipأو xselالأوامر

الأساليب المذكورة أعلاه تتناول السيناريوهات المحلية فقط.

لدعم السيناريوهات البعيدة ، توجد طريقتان إضافيتان:

  1. استخدم تسلسل الهروب ANSI OSC 52 للتحدث مع الطرف المتحكم / الأصل لإدارة وتخزين النص على حافظة لجهاز محلي.
  2. قم بإعداد مستمع الشبكة المحلية الذي يقوم بتوجيه الإدخال إلى pbcopyأو xclipأو xsel. قام الأنبوب بنسخ النص المحدد من آلة بعيدة إلى مستمع على الجهاز المحلي من خلال نفق SSH البعيد. هذا متضمن إلى حد ما ، وسأخصص منشورًا مخصصًا لوصفه.

OSX. أوامر pbcopy و pbpaste

pbcopyو pbpasteأوامر تسمح لك بالتفاعل والتعامل مع الحافظة النظام من سطر الأوامر.

pbcopy يقرأ البيانات stdinويخزنها في الحافظة. pbpasteيفعل العكس ويضع النص المنسوخ عليه stdout.

تكمن الفكرة في ربط أوامر tmux المختلفة ، والتي يمكنها نسخ النص أثناء وضع النسخ.

دعنا نذكرهم:

$ 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و copy-end-of-lineهناك أوامر tmux الخاصة التي tmux فهم عند جزء في وضع نسخة. هناك نوعان من صيغ أمر النسخ: copy-selectionو copy-pipe.

دعنا Enterنعيد كتابة رابط المفاتيح باستخدام الأمر copy-pipe:

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

copy-pipeيقوم الأمر بتخزين النص المحدد في المخزن المؤقت tmux نفسه copy-selection، بالإضافة إلى توجيه النص المحدد إلى الأمر المحدد pbcopy. لذلك نحصل على النص مخزّنًا في مكانين: المخزن المؤقت لنسخ tmux وحافظة النظام.

OSX. إعادة إرفاق مُجمّع مساحة الاسم بالمستخدم

حتى الان جيدة جدا. ومع ذلك، في بعض إصدارات OSX، pbcopyو pbpaste تفشل في تعمل بشكل صحيح عندما تعمل تحت tmux.

اقرأ المزيد من التفاصيل من كريس جونسن حول سبب حدوث ذلك:

يستخدم tmux وظيفة المكتبة daemon (3) عند بدء عملية الخادم. في نظام التشغيل Mac OS X 10.5 ، غيرت Apple البرنامج الخفي (3) لنقل العملية الناتجة من مساحة اسم التمهيد الأصلية إلى مساحة اسم التمهيد الجذر. هذا يعني أن خادم tmux وأبنائه سيفقدون تلقائيًا وبشكل لا يمكن السيطرة عليه الوصول إلى مساحة اسم تمهيد التشغيل الأصلية (أي تلك التي يمكنها الوصول إلى خدمة لوحة اللصق).

الحل الشائع هو استخدام برنامج إعادة إرفاق مساحة الاسم بالمستخدم. يتيح لنا ذلك بدء عملية وإرفاق هذه العملية بمساحة اسم التمهيد لكل مستخدم ، مما يجعل البرنامج يتصرف كما نتوقع. تحتاج إلى تغيير ارتباط المفاتيح بشكل صحيح:

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

بالإضافة إلى ذلك ، ستحتاج إلى إخبار tmux بتشغيل shell الخاص بك (bash ، zsh ، ...) داخل غلاف ، عن طريق تعيين default-commandالخيار:

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

ملاحظة : تعمل بعض إصدارات OSX بشكل جيد حتى بدون هذا الاختراق (OSX 10.11.5 El Capitan) ، بينما أبلغ مستخدمو OSX Sierra عن أن هذا الاختراق لا يزال مطلوبًا.

لينكس. التفاعل مع اختيار X عبر xclip و xsel

يمكننا الاستفادة من xclipأو xselالأوامر على لينكس إلى نص مخزن في الحافظة، نفس pbcopyعلى OSX. في Linux ، هناك عدة أنواع من تحديدات الحافظة التي يحتفظ بها خادم X: الأساسي والثانوي والحافظة. نحن نهتم فقط بالأساسي والحافظة. كان المقصود من الثانوي أن يكون بديلاً عن الابتدائي.

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

أو عند استخدام xsel:

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

اقرأ هنا عن مقارنة xclipمقابل xsel، إذا كنت غريبة. أيضًا ، تحقق من هذا المنشور حول xclipالاستخدام والأمثلة. ولا تنس تثبيت إحدى هذه الأدوات المساعدة ، فقد لا تكون جزءًا من التوزيع الخاص بك.

استخدام تسلسل هروب ANSI OSC 52 لجعل المحطة الطرفية تخزن النص في الحافظة

حتى الآن قمنا بتغطية السيناريوهات المحلية فقط. عندما تقوم SSH بجهاز بعيد ، وتبدأ جلسات tmux هناك ، لا يمكنك الاستفادة من pbcopy، xclipأو xselلأنه سيتم تخزين النص في حافظة الجهاز البعيد ، وليس في حافظة جهازك المحلي. أنت بحاجة إلى طريقة ما لنقل النص المنسوخ إلى حافظة جهازك المحلي.

تسلسل هروب ANSI عبارة عن سلسلة من البايت المرسلة إلى الجهاز الطرفي والتي يتم تشذيرها بأحرف عادية قابلة للطباعة ، وتستخدم للتحكم في الجوانب الطرفية المختلفة: مثل ألوان النص ، وموضع المؤشر ، وتأثيرات النص ، وشاشة المقاصة. الجهاز قادر على الكشف عن تسلسل التحكم من البايتات التي تجعله يطلق إجراءات محددة وليس طباعة تلك الأحرف على المخرجات.

يمكن اكتشاف تسلسل هروب ANSI عندما يبدأ بحرف ESCASCII (0x1b ست عشري ، 027 عشري ، \ 033 في رقم ثماني). على سبيل المثال ، عندما ترى المحطة الطرفية \033[2Aالتسلسل ، فإنها ستحرك موضع المؤشر سطرين لأعلى.

هناك بالفعل الكثير من تلك التسلسلات المعروفة. بعضها متماثل عبر أنواع طرفيات مختلفة ، في حين أن البعض الآخر يمكن أن يختلف ويكون خاصًا جدًا بمحاكي الجهاز. استخدم infocmpالأمر للاستعلام terminfoعن قاعدة البيانات لتسلسلات الهروب التي تدعمها أنواع مختلفة من المحطات.

حسنًا ، هذا رائع ، ولكن كيف يمكن أن يساعدنا في ما يتعلق بالحافظة؟ اتضح أن هناك فئة خاصة من تسلسلات الهروب: "عناصر التحكم في نظام التشغيل" (OSC) وتسلسل الهروب "OSC 52" ، والذي يسمح للتطبيقات بالتفاعل مع الحافظة.

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 "\033]52;c;$(printf "%s" "blabla" | base64)\a"

The conclusion is that we can store text in the system clipboard by sending a specially crafted ANSI escape sequence to our terminal.

Let’s write the shell script 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"

So, we read text to copy from stdin, then check if it’s length exceeds the maximum length of 74994 bytes. If true, we crop it, and finally convert data to base64 and wrap in OSC 52 escape sequence: \033]53;c;${data_in_base64}\a

Then let’s wire it with our tmux keybindings. That’s pretty easy: just pipe the selected text to our yank.sh script, just as we pipe it to pbcopy or 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

استخدم tmux set-clipboard في gnome-terminal (XTerm's disallowedWindowOps) - اسأل Ubuntu - //askubuntu.com/questions/621522/use-tmux-set-clipboard-in-gnome-terminal-xterms-disallowedwindowops/621646