Часть рутины за рабочим столом с Linux состоит из мышиных ритуалов: выделить вывод команды, скопировать в тикет, вставить путь из письма в терминал, вернуть строку обратно. Две крошечные утилиты, xclip и xsel, превращают буфер обмена в полноправного участника конвейера: на вход подаётся поток, из буфера поток же и забирается. Обе ставятся из репозиториев каждого дистрибутива, не тянут тяжёлых зависимостей и в скриптах ведут себя так же незаметно, как grep или sed. Привычка прогонять вывод команд через буфер экономит минуты ежедневно, а собранные вокруг этих утилит скрипты выглядят как обычный шелл, только без возни с мышью. Ниже устройство выделений, ключи обоих инструментов и приёмы, которые переносятся в ежедневную работу без переделок.
Три выделения X11 и первичный буфер, о котором забывают
X11 держит не один буфер, а три выделения: PRIMARY, SECONDARY и CLIPBOARD. Соглашение ICCCM описало механизм выделений ещё на заре системы, и с тех пор модель почти не менялась. Системный буфер, куда попадает ctrl+c, это CLIPBOARD. PRIMARY живёт отдельной жизнью: только что выделенный текст уже скопирован сам по себе, без единого нажатия, и вставляется средней кнопкой мыши. SECONDARY достался от ранних лет X Window и в живых приложениях почти не встречается, хотя запись в него через -selection secondary по-прежнему проходит.
Разница всплывает именно в скриптах. Обе утилиты по умолчанию работают с PRIMARY, и новичков это регулярно путает: скрипт положил строку в выделение, а ctrl+v всё ещё вставляет старое содержимое. Явное указание цели снимает вопрос: у xclip это -selection clipboard или сокращение -sel c, у xsel флаг -b.
Бонус PRIMARY работает в обе стороны. Записать в него можно так же, как в системный буфер, только с -selection primary. А прочитать текущее выделение пользователя скрипт умеет мгновенно: пользователь выделил строку в журнале, скрипт тут же забрал её командой xclip -o -selection primary и продолжил работу, без единого копирования. Старые приложения и xterm часто понимают только PRIMARY, и тогда запись именно в него спасает сценарий, который с CLIPBOARD не сработал бы вовсе. Самый наглядный пример вживую: выдели адрес в выводе traceroute, щёлкни средней кнопкой в окне браузера, и адрес уже в строке.
Ключи xclip для выбора буфера, вывода и формата
Xclip читает стандартный ввод и кладёт содержимое в выбранное выделение:
ls -l | xclip -selection clipboard
xclip -selection clipboard -o | head -n 5
Ключ -o выводит текущее содержимое на стандартный вывод, что открывает обратные конвейеры: из буфера в grep, в скрипт проверки, в файл. Ключ -f работает как фильтр: поток уходит и в выделение, и дальше по конвейеру, поэтому копия получается попутно, без повторного запуска команды:
ps aux | xclip -selection clipboard -f | wc -l
Ключ -l ограничивает число запросов на вставку: конструкция -l 1 отдаёт содержимое один раз и завершает процесс, что удобно для разовых сценариев. Ключ -rmlastnl срезает последний перевод строки, который так часто оказывается лишним при вставке в формы.
Отдельная сила у ключа -t, задающего целевой формат содержимого. Команда -t TARGETS перечисляет, что владелец буфера сейчас умеет отдавать: плоский текст, разметку html, картинку. Запрос text/html вернёт форматированный фрагмент вместо голого текста, что выручает при переносе заметок в редактор писем. А запись изображения делает вставку в графический редактор по-настоящему работающей:
xclip -selection clipboard -t image/png -i /tmp/screen.png
После такой команды ctrl+v в редакторе изображений вставит настоящий png, а не путь к файлу. Тот же механизм позволяет возить через буфер табличные фрагменты в своих форматах, если обе стороны умеют их читать. Для отладки обмена форматами пригождаются режимы -quiet и -verbose: первый показывает каждый запрос на вставку, второй добавляет подробности, и сразу видно, кто и когда обратился за содержимым.
Особые режимы xsel с накоплением, обменом и очисткой
Xsel решает те же базовые задачи, но добавляет редкие режимы, которые пригодятся в повседневных сценариях:
echo "первая находка" | xsel -b -a
echo "вторая находка" | xsel -b -a
xsel -b -o
Ключ -a дописывает к текущему содержимому: за день в буфере копится собранный список ссылок, команд и заметок, к вечеру он вставляется одним движением. Чтение первичного выделения работает зеркально: xsel -p -o отдаёт то, что пользователь выделил в последний момент, и на этом строится половина удобных скриптов. Ключ -c очищает выделение: полезно сразу после копирования пароля или токена, чтобы они не гуляли по системе. Ключ -x меняет местами содержимое CLIPBOARD и PRIMARY, спасая при перекрёстных вставках, когда нужный текст оказался в не том буфере. Ключ -k делает текущее содержимое постоянным: оно переживёт закрытие приложения, которое его положило. Режим --logfile дописывает всё, что попадает в буфер, в файл, то есть ведёт журнал обмена, и по нему легко вспомнить, что копировалось утром. Флаг -n (--nodetach) запрещает xsel уходить в фон, и это сильно упрощает отладку собственных скриптов.
Отдельного внимания заслуживает санитария буфера. Скопированный пароль живёт в буфере до следующей копии, а если в системе работает менеджер истории буфера, то и дольше. Привычка сразу чистить выделение командой xsel -c или затирать его пустой строкой через printf '' | xclip -sel c выглядит параноидально ровно до первого громкого случая. Журнал --logfile тоже стоит хранить с оглядкой: там оседает всё, включая случайные секреты. На собраниях с показом экрана вспоминать об этом особенно своевременно.
Владение выделением и жизнь процесса после копирования
В X11 буфер обмена не лежит в системной ячейке: содержимое держит процесс-владелец, и запросы на вставку уходят именно к нему. Отсюда странное для новичка поведение: утилита не может скопировать и тут же умереть, отвечать на запросы будет некому. Xsel после своей работы уходит в фоновый режим и тихо ждёт, xclip остаётся живым процессом в терминале, пока выделение не заберут.
Для скриптов из этого следует практическое правило. Разовая конструкция без ограничителей оставляет за собой фоновый процесс, и если это мешает, выручает xclip -l 1: содержимое отдаётся один раз, процесс завершается. А если владелец всё-таки умер до вставки, приложение получит пустоту, так что загадочные пустые вставки первым делом проверяют именно здесь. Размер тоже имеет значение: владелец держит содержимое в памяти, и копия крупного файла повиснет в оперативной памяти до первой вставки, так что для гигабайтных объёмов буфер подходит плохо. Менеджеры буфера обмена вроде Klipper или CopyQ меняют картину: они подхватывают содержимое в свою историю, и когда исходный владелец уже закрылся, вставка всё равно срабатывает, ведь кэш уже у них. С ними же связана маленькая ловушка: менеджер может перехватить выделение быстрее, чем ожидает скрипт, и тогда тестировать конструкцию лучше с одним и тем же таймингом.
Готовые приёмы для команд, git и tmux
Проще всего приживаются привычки из macOS через псевдонимы в конфиге оболочки:
alias pbcopy='xclip -selection clipboard'
alias pbpaste='xclip -selection clipboard -o'
Дальше конвейеры собираются сами. Патч в тикет одним движением: git diff | xclip -sel c. Имя ветки для строки заголовка задачи: git branch --show-current | xclip -sel c. Публичный ключ для панели хостинга: xclip -sel c < ~/.ssh/id_ed25519.pub. Текст со скриншота: tesseract shot.png stdout | xclip -sel c. Результат поиска по журналам улетает коллеге без промежуточного файла: grep -i timeout /var/log/app.log | xclip -sel c. Утренний отчёт собирается скриптом в буфер до начала планёрки, и на совещании остаётся нажать вставку. В tmux содержимое панели переносится в системный буфер командой tmux show-buffer | xclip -sel c, а копирование из режима копирования подключается привязкой клавиши:
bind-key -T copy-mode-vi y send-keys -X copy-pipe-and-cancel "xclip -in -selection clipboard"
Автоматизация ввода в графические программы собирается из пары команд: шаблон кладётся в буфер, затем xdotool эмулирует вставку в активное окно:
xclip -selection clipboard -i template.txt
sleep 0.2
xdotool key ctrl+v
Так заполняются формы без единого ручного движения, а различия между шаблонами живут в git и ревьюятся как обычный текст. Тот же тандем выручает в связке с хоткеями рабочего стола: менеджер окон запускает скрипт по комбинации клавиш, скрипт читает буфер, обрабатывает его и показывает итог уведомлением через notify-send. Один такой скрипт с нормализацией пробелов и переводов строк быстро становится любимой кнопкой на клавиатуре:
clip=$(xclip -selection clipboard -o)
printf '%s' "$clip" | tr -d '\r' | xclip -selection clipboard -i
Отдельная привычка из этого же мира: многострочную вставку в терминал лучше сначала показывать глазами. Вставка с переводами строк выполняется строка за строкой немедленно, и команда из чужого текста может запуститься раньше, чем её успели прочитать. Режим вставки со скобками в эмуляторах терминала частично снимает риск, но привычка смотреть перед вставкой надёжнее любых опций.
Скрипты по расписанию, переменная DISPLAY и диагностика сбоев
Из cron или таймера systemd утилиты часто молча не работают: у запущенной задачи нет связи с графической сессией. Чтобы конвейер с буфером переживал запуск по расписанию, достаточно пройтись по шагам:
- Задать DISPLAY=:0 или номер нужного дисплея в самом скрипте, не надеясь на окружение cron;
- При необходимости добавить XAUTHORITY с путём к файлу авторизации пользовательской сессии;
- Явно указать нужное выделение, поскольку по умолчанию пишется PRIMARY, а приложения ждут CLIPBOARD;
- Для xclip задать лимит запросов через -l, чтобы процесс не висел вечно после разовой вставки;
- Прогнать задачу сначала из терминала, затем из cron с журналом и проверить вставку в целевом приложении.
В пользовательских юнитах systemd достаточно запустить задачу от имени владельца сессии и добавить строку Environment=DISPLAY=:0 с типом oneshot, дальше переменная наследуется. Номер дисплея при необходимости подсматривают в выводе who или в переменной окружения живой сессии, а не угадывают. Диагностика при этом идёт по короткому пути: если из терминала скрипт работает, а из расписания нет, виновата почти всегда отсутствующая связь с дисплеем, а не сама утилита. Когда буфер пуст без видимых причин, содержимое смотрят в сыром виде: xclip -o | od -c | head покажет невидимые символы, лишние переводы строк и пробелы, которые портят вставку. Запуск на удалённой машине по ssh требует того же DISPLAY и доступного X-сервера: переадресация X11 подтягивает переменную автоматически, но крупные буферы по такому каналу идут мучительно, и чаще логичнее перенести сам скрипт на рабочий стол пользователя.
Границы применимости на Wayland и выбор между утилитами
Обе утилиты дети X11. В чистой Wayland-сессии они видят только приложения, работающие через слой совместимости XWayland, и не общаются с нативными, а в гибридной сессии программа на одном тулките может сидеть по другую сторону слоя совместимости от соседней. Выход давно существует: пара wl-copy и wl-paste из пакета wl-clipboard общается с Wayland-композитором напрямую. Универсальный скрипт ветвится по типу сессии:
if [ "$XDG_SESSION_TYPE" = "wayland" ]; then
wl-copy < snippet.txt
else
xclip -selection clipboard -i snippet.txt
fi
Текущую среду подсказывает переменная XDG_SESSION_TYPE, и проверка одной строкой экономит время на гадания. Выбор между утилитами несложен: xclip берут за форматы через -t, за удобный лимит -l и предсказуемое поведение в переднем плане, xsel за накопление, обмен буферами, очистку и журнал. Ставить можно обе сразу: вес копеечный, интерфейсы похожи, и команда, собранная вокруг одной утилиты, переезжает на другую за минуты. Главное приходит быстро: любое окно терминала начинает работать как буфер обмена, и очередная мышиная рутина превращается в строчку скрипта.