Программа-помощник для освоения слепой печати на клавиатуре в Linux

Хочу показать и рассказать о небольшой программке, которая принесла пользу.

Однажды на работе мне написал друг. Диалог у нас состоялся примерно следующий:
— Привет, я тут обучаюсь технике слепой печати. Дело в том, что на линуксе нет программки, которая могла бы мне помочь. В общем, может ты сможешь быстренько написать такую?

Так как помочь другу — святое дело, да и задача выглядела интересной, помочь я согласился.

В итоге получилось вот что:



Кому интересно, подробности ниже

Предупреждение
Я не претендую на звание гуру питона, поэтому в коде (и я почти уверен в этом) присутствуют забивания гвоздей микроскопом и прочие нелепости.

Вместе с другом сделали постановку задачи:

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

Это помогает выработать рефлекс смотреть на монитор во время печати на клавиатуре.

Главные требованиями к программе:
  1. Реализовать её очень быстро;
  2. Показывать нажатые клавиши в режиме реального времени;
  3. «Переключать раскладки» в окне при переключении языка клавиатуры;
  4. Производить настройку программы через текстовый конфигурационный файл.

В процессе написания также добавились:
  1. Режим «залипания» последней нажатой клавиши (помогает сообразить куда нажимать пальцами дальше);
  2. Работа с клавишей shift;
  3. Возможность пометки цветом позиций для пальцев на клавиатуре;
  4. Возможность настройки шрифта;
  5. Возможность настройки ширины кнопок;
  6. Автоматическое сокрытие содержимого окна при наведении мышки.

На момент появления программы уже имелся опыт работы с Tkinter, работы с несколькими потоками. Плюс, по роду деятельности приходится бывать и сисадмином, поэтому работа с командной строкой была не чужда.

Общее описание внутренностей программы:

Для чтения клавиш используется найденная в google строчка на bash, позволяющая читать клавиши, нажимаемые на клавиатуре через утилиту xinput. Данный способ выбран в угоду пункту 1 требований. Процесс чтения символов запускается в отдельном потоке. Так же реализовано и чтение языка раскладки (опять таки пункт 1). Выдача информации о нажатых кнопках производится в очередь. Работа с очередью в главном окне программы производится путем периодического вызова функции periodicCall. Таким образом два потока пишут в очередь, один поток читает.
Завершение работы программы производится своеобразно — через статусные переменные в потоках.

Работа с настройками программы


Настройки программы загружаются и хранятся в экземпляре класса ConfigManager. Чтение из главноего текстового файла настроек производится с помощью ConfigParser. Данный модуль позволяет использовать похожий на INI формат конфигурационных файлов. В конструкторе класса производится проверка существования конфигурационного файла, расположенного по пути "~/.key_trainer/program.conf". Если его нет, программа читает файл program.conf, расположенный в текущей папке с программой.

Немного кода
import os
...
filename='program.conf'
home = os.path.expanduser("~")
if os.path.isfile(home+'/.key_trainer/'+filename):
    filename=home+'/.key_trainer/'+filename
...


ConfigParser — замечательный модуль. Можно считать названия всех секций, а также считать ключи с их значениями внутри секций как кортежи (tuple). Так, например, реализовано считывание названий секций, и ключей в секции «KEYBOARD SETTINGS».

Еще немного кода

from ConfigParser import RawParser
...
myParser=RawConfigParser(allow_no_value=True)
myParser.read(path_to_file)

# Получаем секции
self.sections = myParser.sections()

# Используем генератор чтобы вытащить ключи, заданные в секции KEYBOARD SETTINGS
keyboard_settings_keys=[x[0] for x in myParser.items("KEYBOARD SETTINGS")]


Помимо главного конфигурационного файла есть второй не менее важный — «keyboard.conf». Он используется для настройки отображаемых кнопок, а именно кода кнопки, текста на кнопке (с шифтом и в раскладках), положения кнопки. Убирая/добавляя записи в этот файл можно менять количество и качество кнопок (и строк с кнопками) в главном окне программы.
Формат записей в keyboard.conf
В файле содержатся записи в виде:

[код кнопки]:"[строчной символ в английской раскладке],[заглавный символ в английской раскладке],[строчной символ в русской раскладке],[заглавный символ в русской раскладке]":[номер строки кнопки],[номер столбца кнопки]

Вот несколько записей для примера:

24:«q,Q, й, Й»:3,2
25:«w,W, ц, Ц»:3,3
26:«e,E, у, У»:3,4
27:«r,R, к, К»:3,5

Чтение символов с клавиатуры


Для чтения символов написан класс KeyboardStatus, который принимает входным параметром класс конфигурации (см. выше). Внутрь этого класса инкапсулирована потокобезопасная очередь Queue.

Чтение символов с клавиатуры производится в два потока. Почему два — потому что на практике так оказалось проще. Один поток читает раскладку клавиатуры, второй нажатые кнопки. Оба потока порождаются через Thread, в каждом потоке затем через subprocess Popen будет запущен соответствующий процесс чтения клавиш или раскладки. Для чтения выходного потока процесса используется subprocess.PIPE. Как только текст пришел в поток выхода процесса, он читается, обрабатывается, и, если нужно, ставится в очередь Queue:

Код
from subprocess import Popen
from subprocess import PIPE
import threading
...
def doReadingKeys(self):
    self.myProcess=Popen('xinput list '+'|'+'   grep -Po \'id=\K\d+(?=.*slave\s*keyboard)\' '+'|'+'   xargs -P0 -n1 xinput test',shell=True,stdout=PIPE)

    while self.proc_started:
        symbol=self.myProcess.stdout.read(1)
        if symbol in press_release_dict:
            symbol_pressed=press_release_dict[symbol]
            while symbol!='\n':
                symbol=self.myProcess.stdout.read(1)
                if symbol.isdigit():
                    symbol_index=symbol_index*10+int(symbol)

                self.myQueue.put((symbol_index,symbol_pressed))
                symbol_index=0
...
keysThread=threading.Thread(target=self.doReadingKeys)
keysThread.start()
...


Чтобы завершить поток используется переменная класса proc_started. При закрытии главного окна программы она устанавливается в значение False, производится выход из цикла чтения, выполняется завершения процесса чтения клавиш через terminate, а затем wait — для того чтобы дождаться пока процесс завершился.

Замечание
У данного подхода есть один недостаток — разблокировка(а значит и дальнейшее завершение потока и процесса) метода read, который внутри цикла не произойдет до тех пор, пока что-нибудь не считается с выходного потока процесса myProcess. Но на практике проблем из-за этого не возникало, так как нажимаем мы на кнопки часто.

Графический интерфейс


Для того чтобы быстро сделать графический интерфейс использовался Tkinter. Данный модуль позволяет легко работать с простыми графическими интерфейсами (окна, кнопки, галочки и т.п.). Класс окна GuiManager на вход, помимо других параметров, принимает класс конфигурации. Из неё берутся настройки кнопок, затем эти кнопки создаются и добавляются на главное окно программы.

Код добавления кнопок
from Tkinter import *
import tkFont
...
        self.buttonFont=tkFont.Font(family=config.font_name,size=config.font_size)
        self.boldUnderscoredButtonFont=tkFont.Font(family=config.font_name,size=config.font_size,weight='bold',underline=1)

        for row_index in xrange(1,config.getNumOfRows()+1):
            self.gui_rows[int(row_index)]=Frame(master)
            self.gui_row_buttons[int(row_index)]=[]
            for button_num in xrange(1,config.getNumOfKeysInRow(row_index)+1):
                newButton=Button(self.gui_rows[int(row_index)])
                if self.config.padx!=-1:
                    newButton.config(padx=self.config.padx)
                if self.config.pady!=-1:
                    newButton.config(pady=self.config.pady)
                if (row_index,int(button_num)) in config.key_pos_to_index:
                    self.gui_all_buttons[config.key_pos_to_index[(row_index,int(button_num))]] = newButton
                self.gui_row_buttons[int(row_index)].append(newButton)
                newButton.pack(side=LEFT)

            self.gui_rows[int(row_index)].pack()
        self.reconfigure_text_on_buttons(config,shift_pressed=0,lang=0)
...


При добавлении кнопок на форму попутно создаются словари с ключами номера строки и значениями — объектом Frame в каждый из которых помещаются кнопки. Как видно из кода, кнопки формируются построчно, по завершении формирования строки виджет кладется в окно методом pack().

Помимо прочего, в класс добавлена функция processQueue, которая со стороны потока графического интерфейса достает из очереди кортежи (tuple) с событиями нажатых кнопок и изменяет внешний вид кнопок — «нажимает» их, «переключает раскладки» и «нажимает» кнопку shift:

Обработка очереди со стороны графического интерфейса
    def processQueue(self):
        while not self.queue.empty():
            msg = self.queue.get(0)
            if msg[0] == -1:                 # -1 message is for changing language
                    self.currentLang=int(msg[1])
                    if self.config.debug:
                        print "Changed lang!"
                    self.reconfigure_text_on_buttons(self.config,0,msg[1])

            if msg[0] in self.gui_all_buttons:
                if msg[0] in self.shift_key_codes:
                    self.reconfigure_text_on_buttons(self.config,msg[1],self.currentLang)
                if msg[1]==1:
                    self.gui_all_buttons[msg[0]].config(relief=SUNKEN)
                    if self.sticky_key_behaviour:
                        if self.last_sticky_button!=msg[0]:
                            self.gui_all_buttons[self.last_sticky_button].config(relief=RAISED)
                        self.last_sticky_button=msg[0]
                else:
                    if not self.sticky_key_behaviour:
                        self.gui_all_buttons[msg[0]].config(relief=RAISED)
            if self.config.debug:
                print msg


Класс GuiManager инкапсулирован внутрь класса ThreadedClient, который принимает на вход главный поток Tkinter и выставляет вызов функции разбора очереди каждые 20 миллисекунд:

Класс, инкапсулирующий GuiManager
class ThreadedClient:
    def __init__(self, master):
        self.master = master

        self.config=ConfigManager()
        self.keyTrainer=keyboardStatus(self.config)
        keyTrainer=self.keyTrainer

        master.protocol('WM_DELETE_WINDOW', self.kill_and_destroy)

        self.guiManager=GuiManager(master,self.config,keyTrainer.myQueue,keyTrainer)

        keyTrainer.begin_scan()

        self.running = 1
        self.periodicCall()
    def kill_and_destroy(self):
        self.running = 0
        self.keyTrainer.stop_scan()
        if self.config.debug:
            print "Stopping scan..."
        self.master.destroy()


    def periodicCall(self):
        self.guiManager.processQueue()
        if not self.running:
            # import sys
            # sys.exit(1)
            self.kill_and_destroy()
        self.master.after(20, self.periodicCall)


Несколько картинок


Общий вид окна программы:



Нажата левая клавиша Alt:



Окно программы после перенастройки:



При наведении курсора мыши окно программы «уезжает» под заголовок (цвета, которые остаются на белом фоне — артефакты сжатия ролика):



Нажатие клавиши shift и переключение языка:



Заключение


Что же получилось в итоге? А получилась неплохая программа для того, чтобы помогать людям учиться печатать вслепую на клавиатуре. Да, у нее есть недостатки и неэффективности, а именно:
  • Запускаемые со стороны процессы с командами bash для чтения символов;
  • Жестко заданные языки (только русский и английский);
  • Квадратный интерфейс;
  • Работает на Ubuntu и Linux Mint (MATE), на других дистрибутивах не опробована;

Код можно скачать/посмотреть здесь: Ссылка на bitbucket
Для работы программы необходим python 2.7 и Tkinter. Чтобы установить последний, необходимо выполнить команду:

sudo apt-get install python-tk

Запуск программы выполняется скриптом Start.sh из директории с программой.

Спасибо за внимание!

P.S. Поступил вопрос: сколько времени заняло написание программы? Времени было потрачено в общей сумме часов 6-8, после первых трех было активное тестирование и допиливались всякие детали.

UPD: убрал try/except из обработки очереди со стороны GUI
Поделиться публикацией

Комментарии 34

    0
    круто придумано, поставил, посмотримс профит
      0
      Спасибо) В надежде что еще кому пригодится и писалась статья.
      0
      Здорово.
      Полагаю, можно взять упаковщик типа cx_Freeze и сделать готовые сборки для винды, мака.
        0
        Спасибо! Попробуем. Есть пара вопросов:

        Размер окна только через размер шрифта настраивается?
        Нельзя ли как-то сделать окно полупрозрачным?
          0
          Да, размер окна подстраивается автоматически — чтобы все кнопки влезали.

          Полупрозрачность поддерживается, хотя лично у себя не тестил) По идее, если композитинг включен, то прозрачность должна быть. В Main Program.py есть параметр alpha, в самом низу. Уже не помню, почему, видимо не успел его вынести в конфиг)
          +7
          В Ubuntu/Mint и так уже есть «из коробки» виртуальные клавиатуры и тренажёры: matchbox-keyboard, KTouch, Gtypist, Klavaro, Florence…
            +1
            Да, программ с похожим функционалом море. Отличие состоит в том, что писАлась она для меня и друга, исключительно под наши требования)
              0
              Надо поддержать отечественного производителя. Или, хотя бы, дать шанс.
                0
                я поддержал — плюс статью дал
                  –2
                  Импортозамещение же!
                +3
                Не представляю, как подобная программа может помочь в освоении слепой печати. Ведь слепая печать это прежде всего рефлексы, которые нарабатываются ежедневными наборами текстов.

                Мне кажется подобная программа будет только отвлекать, т.к. человек, глядя в монитор (на нарисованную клавиатуру), большую часть времени будет искать нужные кнопки, а не оттачивать рефлексы.
                  0
                  Сколько людей — столько мнений. Другу помогла, мне — не очень. Но, по крайней мере, образовательную ценность лично для меня она имела) Чтобы помогать учиться печатать необходимо прежде всего желание, а только затем инструмент. И ежедневными наборами текстов вопрос не решается. Пруф — зайдите в любую контору где сидят страховщики и посмотрите как они печатают на клавиатурах.
                    0
                    У меня желание было, но намеренно научиться не получалось, ибо тогда не понимал каким образом клавиатурные тренажёры могут помочь в этом (а они помогают быстрее развить рефлексы, если N часов каждый день этому уделять).

                    PS А страховщики пишут не сильно много текста. Особенно в сравнении с тем, сколько можно написать за день в клавиатурном тренажёре.
                      0
                      я видел, как приходится изгаляться страховщикам при заполнении карточек которые наш собрат им наваял и прекрасно понимаю, почему они так «печатают на клавиатурах» — чуть быстрее или чуть не так что нажмёшь и начинай форму заново заполнять.
                      0
                      одной из рекомендаций к обучению раньше был способ «повесить листик с распечатанной раскладкой клавиатуры рядом с монитором справа/слева».

                      Ещё неплохо в былые времена обучение происходило при общении в чатах с N-цатью участниками

                      А самый идеальный способ — клавиши без надписей. У меня сейчас китайская клава, кириллицы нет — хочешь или нет, а подсмотреть уже не получится.
                        0
                        >А самый идеальный способ — клавиши без надписей. У меня сейчас китайская клава, кириллицы нет — хочешь или нет, а подсмотреть уже не получится.

                        Тут уже вопрос отвыкания от смотрения на клавиатуру. Рефлексы нахождения клавиш можно наработать смотря на клавиатуру, а рефлексы в слепой печати главное.
                          0
                          У меня тоже нет кирилицы на клавиатурах, однако это не помогло избавиться от привычки смотреть на нее.
                          0
                          Согласен, при том, что нормальная программа для тренировки слепой печати в линуксе есть и не одна. Сам когда осваивал дворак использовал gnu typist.
                            +1
                            Это слишком индивидуально. У кого-то работает так, у кого-то — иначе.

                            Например, мне было проще посидеть в чате пару дней, что бы освоить русскую печать вслепую (залил клавиатуру с русскими буквами, а на замену была только обычная. Старая клавиатура стояла передо мной). Общение на англо-язычных форумах дало слепую печать на английской раскладке. А когда кодил, то освоил ещё одну сторону слепой печати.
                            Я знаю людей, кому было проще освоить когда они видели раскладку на экране и потом свободно печатали уже без неё.

                            То есть, надо пробовать и смотреть что хорошо работает для тебя.
                              0
                              Правда не ваша. Конечно, когда идешь на курсы машинописи, тебя вообще заставляют первую неделю долбить по столу пальцами, чтобы отработать рефлексы.
                              Суть слепой печати в том, что ты знаешь каким пальцем и куда ткнуть (рефлекс), но не знаешь самого расположения кнопок. Чтобы сказать где какая кнопка, скажем мне, придётся в голове думать о каждой букве и рисовать траекторию пальца, чтобы понять, где же она находится. Расположения всех кнопок наизусть я не знаю, владея слепой печатью. Это не нужно.

                              Смотря на виртуальную клавиатуру, человек получает подсказку куда сдвинуть палец без зрительного контакта с клавиатурой (это важно). Чем меньше человек смотрит на клавиатуру при печати, тем быстрее пальцы запоминают где что находится. Вопрос в другом, что лучше делать это методично, скажем, как было в своё время в программе «соло на клавиатуре», где за ошибки наказывали сбросом прогресса по набору, где заданий было много, где они по началу были очень тупыми типа «аоа аоа аоа аоа аоа аоа», чтобы тупо наработать механику движения пальцев.

                              Я распинаюсь тут, а о главном забыл, эта клавиатура поможет научиться печатать в слепую, но очень плохо. Потому что она не учитывает главного — она не подсказывает какой рукой набирать пробел и шифт. А это очень важно. Шифт надо всегда нажимать противоположной рукой, а пробел рукой, которая была свободна на момент набора последнего символа. Если последней была буква «а», то правой, если «о», то левой. Важно это потому, что при нажатии пробела только одной рукой неимоверно увеличивается количество ошибок при печати. А этому эта клавиатура не учит.
                              0
                              Думаю программа пригодится, как многие выше отметили, процесс обучения слепому набору индивидуален.

                              Я в своё время просто заклеил все надписи на клавиатуре, сначала мучаешься, через неделю уже не замечаешь.
                              Таким образом и на русском и на английском научился печатать не глядя на клавиатуру.
                                0
                                Идея программы взята из какого то курса, какого то колледжа.
                                Смысл в том, что бы не смотреть на клавиатуру когда не помнишь где находится нужная клавиша.
                                Они в колледже рисовали клавиатуру на картонках и ставили рядом с монитором, а кнопки самой клавиатуры заклеивали.

                                Отличие от тренажеров в том, что можно практиковать на работе не сильно вредя продуктивности, то есть работать и обучаться одновременно, а не тратить N свободного времени на практику на тренажере.
                                0
                                Штука интересная, но не очень понимаю, как добиться такой длинной пропорциональной клавиатуры, как на скриншотах. У меня она какая-то квадратная… фонт поставил аж 16, а все равно что-то не так.
                                Но задумка оригинальная и наверняка кому-то полезная.
                                Может, стоит как-то попиарить это дело в англоязычном инете тоже?
                                  0
                                  Поставьте в program.conf параметр button_padx побольше — и она расползется по горизонтали
                                  0
                                  Не хотите ли поверх клавиатуры наложить полупрозрачные изображения кистей рук так, чтобы не только клавиши нажимались, но и пальцы тянулись к нажатой клавиши.
                                  Правильные пальцы.
                                    0
                                    На мой взгляд, лучшая программа для обучения слепой печатью — Соло на клавиатуре. Производитель отечественный, стоит совсем не дорого, есть онлайн версия. Когда захотел освоить слепую печать, начал заниматься сначала по книге, потом сам написал небольшую прогу — чтобы задания из книги выполнять. А уже потом нашел готовое решение.
                                      0
                                      Altlinux P7
                                      Требуемые компоненты:
                                      apt-get install xinput python-modules-tkinter

                                      Результат: не отслеживаются нажатия клавиш, показывает только нажатие первой клавиши, дальше эта клавиша показана вечно нажатой.

                                      Посмотрел, что делает «строчка на bash» /bin/sh -c xinput list | grep -Po 'id=\K\d+(?=.*slave\s*keyboard)' | xargs -P0 -n1 xinput test
                                      $ xinput list
                                      ⎡ Virtual core pointer                    	id=2	[master pointer  (3)]
                                      ⎜   ↳ Virtual core XTEST pointer              	id=4	[slave  pointer  (2)]
                                      ⎜   ↳ GenPS/2 Genius Mouse                    	id=10	[slave  pointer  (2)]
                                      ⎣ Virtual core keyboard                   	id=3	[master keyboard (2)]
                                          ↳ Virtual core XTEST keyboard             	id=5	[slave  keyboard (3)]
                                          ↳ Power Button                            	id=6	[slave  keyboard (3)]
                                          ↳ Video Bus                               	id=7	[slave  keyboard (3)]
                                          ↳ Power Button                            	id=8	[slave  keyboard (3)]
                                          ↳ AT Translated Set 2 keyboard            	id=9	[slave  keyboard (3)]
                                      [stas@sd alien713cea-key_trainer-9de6d7e87588]$ xinput list |   grep -Po 'id=\K\d+(?=.*slave\s*keyboard)'
                                      5
                                      6
                                      7
                                      8
                                      9
                                      

                                      Меня сильно смутило, что выделяются аж пять событий, IMHO нужно только одно.
                                      Проверил, действительно все нажатия клавиш отрабатываются только по id 9.
                                      Дополнил условие grep:
                                      $ xinput list | grep -Po 'AT\s.*id=\K\d+(?=.*slave\s*keyboard)'
                                      9

                                      Хотя не уверен, что это это универсально.

                                      В работоспособности программы это не помогло, где искать ошибку, мне непонятно — в питоне я полный ноль.

                                      Ещё есть неаккуратность: при закрытии окошка в консоль выводится сообщение об ошибке:
                                      Exception in Tkinter callback Traceback (most recent call last): File "/usr/lib64/python2.7/lib-tk/Tkinter.py", line 1470, in __call__ xargs: xinput: завершен по сигналу 15 return self.func(*args) File "./MainProgram.py", line 27, in kill_and_destroy self.keyTrainer.stop_scan() File "/home/stas/Загрузки/alien713cea-key_trainer-9de6d7e87588/KeyboardStatus.py", line 96, in stop_scan os.kill(self.myProcess.pid,signal.SIGTERM) AttributeError: keyboardStatus instance has no attribute 'myProcess'

                                      Ошибка видна чётко :), завершать программу надо другим методом.
                                      (И, вообще говоря, xinput лучше завершать SIGQUIT или SIGHUP.)
                                        0
                                        Ваша правда, неаккуратностей полно, отсюда и спойлер про «забивание гвоздей микроскопом».

                                        1) То что выделяются все события ИМХО ничего плохого, т.к. есть люди которые печатают с двух клавиатур (например, на ноутбуке неудобная клавиатура, поэтому подключают нормальную по USB). Да, grep по keyboard по-идее должен выбирать клавиатуру, но где гарантии? В общем, оставьте себе только то, что считаете нужным)

                                        2) Ошибка после завершения xinput говорит что «keyboardStatus instance has no attribute 'myProcess' », а быть он быть должен. Посмотрите на KeyboardStatus.py, строку 58. Там, кстати, как раз запускается процесс xinput. Исходя из этого есть несколько вариантов, но я бы посмотрел сначала на то, как на вашем Altlinux выводятся события о нажатых кнопках вот этой строчкой в терминале: xinput list | grep -Po 'id=\K\d+(?=.*slave\s*keyboard)' | xargs -P0 -n1 xinput test
                                        Понажимайте на кнопки на клавиатуре, должно быть что-то такое:
                                        key press 27
                                        rkey release 27
                                        key press 28
                                        tkey release 28
                                        key press 29
                                        ykey release 29
                                        key press 30
                                        ukey release 30
                                        Если не такое, то присылайте вывод команды мне в сообщения. И, кстати, пришлите что у вас выводит команда: cat /etc/lsb-release, как разберемся с ошибкой — добавим Altlinux в поддерживаемые)

                                        3) Чтобы завершить по другому сигналу нужно открыть KeyboardStatus.py, и исправить последнюю строчку. Тут я с вами согласен, поправлю, конечно)
                                          0
                                          В копилку к второму написанному, вы, случайно, не меняли строку KeyboardStatus.py номер 58?
                                            0
                                            Строку 58 в KeyboardStatus.py я пытался изменять после того, как убедился в невосприимчивости программы к нажатиям клавиш. Это не помогло.

                                            Наверняка проблема в пайпе, но вот как её диагностировать?
                                              0
                                              Пришли мне что сейчас у тебя в этой 58ой строчке написано
                                            0
                                            1) Точно не нужны «Power button» и «Virtual core XTEST keyboard», правда, чтобы их исключить, придётся использовать sed или awk, либо запускать цепочку grep.

                                            2) Команда показывает нажатия клавиш, сделанные в любом окне.
                                            Файл /etc/lsb-release присутствует только в дебианодах, а в краношапкоидах вместо него /etc/redhat-release и симлинки,
                                            $ cat /etc/altlinux-release ALT Linux 7.0.5 Centaurus (Pholus)

                                            3) я так и сделал сразу, сообщение об ошибке не выводится.
                                              0
                                              2) и формат вывода точно такой-же?

                                      Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                                      Самое читаемое