Pull to refresh

Безопасный «ctrl+v» в терминале

Reading time 3 min
Views 20K
Думаю каждый хоть раз аккуратно выделив кусок кода\консольную команду из окна любимого браузера, нажав «ctrl +c», потом «ctrl+v» в активное окно эмулятора терминала очень удивился — "\n" случайно оказался выделенным, соответственно вы уже запустили команду.


Предлагаю легкий и простой вариант решения этой проблемы. Так как самый лучший язык программирования это python, это единственное что я как-то знаю, то будем его использовать.

Итого — нужно перехватить данные из буфера обмена при нажатии «ctrl+v», проверить наличие "\n" и дальше опять «вызвать» «ctrl+v». Делать проверку буфера для всех прикладных программ не совсем логично, и например в gnome-terminal «Paste» назначен по умолчанию на «Shift+Ctrl+V», то я заменил это на «Ctrl+V». Дальше нужно на привычную комбинацию «Shift+Ctrl+V» поставить проверку буфера.
Существует много вариантов это сделать, которые зависят от используемого рабочего окружения. Конкретно в моем случае это

~/.config/openbox/rc.xml
  <keyboard>
    <keybind key="S-C-v">
      <action name="Execute">
        <command>/home/user/scripts/safe_paste.py</command>
      </action>
    </keybind>
  </keyboard>


Нужно не забыть chmod +x /home/user/scripts/safe_paste.py.

/home/user/scripts/safe_paste.py
#!/usr/bin/env python
from paste_it import main


main()


/home/user/scripts/paste_it.py
#!/usr/bin/env python
import os
from subprocess import Popen, PIPE


def main():
    # берем текущие значение буффера, через вызов xsel
    sys_exec = Popen('xsel', stdout=PIPE)

    stdout = sys_exec.stdout.read()

    #если количество строк в буфера не равно 1, то сообщим об это этом
    if len(stdout.splitlines()) != 1:
        n_detected = Popen('notify-send "\\n detected"', shell=True)
        return False

    #самое интересно, об этом чуть ниже
    codes = map(ord, stdout)

    for code in codes:
        if code <= 31 or code == 127:
            bad_code_detected = Popen('notify-send "bad code detected"', 
                shell=True)
            return False

    paste = Popen('%s/pasteit &' %
        os.path.dirname(__file__), shell=True)
    return stdout

if __name__ == '__main__':
    main()


Разбиение кода на 2 файла даст небольшой прирост производительности, конечно же и можно сразу назначать paste_it.py.

Кроме "\n" к нам в буфер может попасть www.asciitable.com, а именно 0-31 и 127, которые тоже могут вызвать неожиданное поведение терминала, следовательно об это нужно проинформировать.

Дальше надо вызвать «crtl+v» для вставки безопасного текста. Ну а если содержание не безопасно то тут открыть текстовый редактор\выбросить некоторые символы\автоматом убрать "\n", тут уже кому как удобнее, реализация тоже будет не сложной. Лично для меня достаточно знать что я задел при выделении что-то лишние.

Способов вызвать «ctrl+v» из под линукса найдено было много:
  • xdotool — на взгляд замечательная вещь — в Centos 6.3 в репах нет, собрать не получилось
  • xvkbd — собирается imake'ом (Imake is a deprecated source code configuration and build system), но не собралось
  • python-uinput — из пушки по воробьям, эмуляция устройства, hal правило
  • python-virtkey — так же не собралось
  • xautomation — было в репах, но очень древние и работало ужасно, и залипало ctrl
  • под виндовс есть autohotkey, autoit, pywin32


Надо писать самому.
На с\с++ погулив я забил, так как с пол пинка не выйдет.

Было реализовано очень компактное решение на java:

PasteIt.java
import java.awt.AWTException;
import java.awt.Robot;
import java.awt.event.KeyEvent;

public class PasteIt{
    public static void main(String[] args) {
        try {
            Robot robot = new Robot();
            robot.keyPress(KeyEvent.VK_CONTROL);
            robot.keyPress(KeyEvent.VK_V);
            robot.delay(20);
            robot.keyRelease(KeyEvent.VK_V);
            robot.keyRelease(KeyEvent.VK_CONTROL);
        } catch (AWTException e) {
            e.printStackTrace();
        }
    }
}


javac PasteIt.java

На не совсем новом ноутбуке вызов этого класса занимал 1-2 сек, было очень не комфортно, но работало.

GCJ выглядит заброшенным, но

gcj -O2 --main=PasteIt PasteIt.java

и был создан бинарник a.out, который я переименовал в pasteit и он работает 0.1 сек.

Теперь я как обычно нажимаю «ctrl+shift+v», и если в буфере что-то не подходящие, то я получу уведомление, так же я могу использовать «ctrl+v» если мне нужно вставить данные без проверки.

Решение получилось не наилучшие, тянущие за собой кучу технологий, но код получился очень компактный и понятный, что легко позволит каждому подстроить всё под себя. Работоспособность получилась вполне нормальная.

Заменив xsel на решения с stackoverflow.com, с помощью mingw так же можно использовать GCJ, но я не совсем уверен в надобности всего этого для использования в powershell\cmd, но для putty может будет полезно.
Tags:
Hubs:
+39
Comments 26
Comments Comments 26

Articles