Как-то раз я спросила у пользователей Mastodon, что их не устраивает в работе с терминалом, и одним из ярких замечаний оказалось «редактирование уже введённой команды».

Мне эта проблема тоже реально знакома. Несмотря на то, что ввод текста и его редактирование является «базовой» задачей, мне потребовалось около 15 лет каждодневной работы с терминалом, чтобы привыкнуть к использованию Ctrl+A для перехода к началу строки (или Ctrl+E для перехода в конец — я использовала вместо этого Home/End).

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

Несогласованность между разными программами


В значительной степени ввод текста в терминале усложняет то, что разные программы обрабатывают его по-разному. Например:

  1. Некоторые программы (cat, nc, git commit --interactive и так далее) не поддерживают использование стрелок. Если вы будете нажимать в них соответствующие клавиши, то увидите лишь ^[[D^[[D^[[C^[[C^.
  2. Многие программы (вроде irb, python3 в Linux и другие) используют библиотеку readline, которая предоставляет богатую базовую функциональность (просмотр истории, клавиши стрелок и так далее).
  3. Некоторые программы (например, /usr/bin/python3 на моём Mac) поддерживают самые простые возможности ввода вроде использования стрелок, но не другие вроде Ctrl+влево или обратного поиска с помощью Ctrl+R.
  4. В некоторых программах (вроде оболочки fish, ipython3, micro или vim) реализована собственная продуманная система ввода, специализированная конкретно под них.

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

Пункт 1: основа


Во-первых, есть «основа» — что происходит, если программа просто получает текст через вызов fgets() или другую функцию и больше никак не стремится повысить удобство в работе. Для меня использование подобных инструментов обычно выглядит так — если я запускаю установленную на моей машине версию dash (довольно минималистичную оболочку) и нажимаю стрелку влево, то она просто выводит в терминал ^[[D.

$ ls l-^[[D^[[D^[[D

Поначалу не кажется, что все эти инструменты «базового» уровня имеют много общего, но по факту есть несколько функциональных особенностей, которые вы получаете автоматически просто от самого терминала без какой-либо помощи со стороны программы.

Что же это за возможности:

  1. Естественно, ввод текста.
  2. Обратное перемещение курсора.
  3. Ctrl+W для удаления предыдущего слова.
  4. Ctrl+U для удаления всей строки.
  5. Ещё несколько возможностей, не связанных с редактированием текста (например, Ctrl+C для прерывания процесса, Ctrl+Z для его приостановки и так далее).

Такие возможности нельзя назвать крутыми, но их наличие означает, что если вы хотите удалить слово, то обычно можете сделать это с помощью Ctrl+W вместо того, чтобы 15 раз нажимать возврат, даже если работаете в среде, которая не предлагает никакой функциональности. Список всех поддерживаемых вашим терминалом комбинаций с клавишей Ctrl можно вывести командой stty -a.

Пункт 2: инструменты, которые используют readline


Следующей группой идут инструменты, использующие readline. Readline — это широко используемая библиотека GNU, которая позволяет сделать текст более опрятным. Вот мои любимые комбинации, которые предоставляет эта библиотека:

  1. Ctrl+E (или End) для перехода в конец строки.
  2. Ctrl+A (или Home) для перехода в начало строки.
  3. Ctrl+влево/вправо для перемещения вперёд/назад на 1 слово.
  4. Стрелка вверх для возврата к предыдущей команде.
  5. Ctrl+R для поиска по истории.

При этом вы также можете использовать Ctrl+W/Ctrl+U из «базового» списка, хотя Ctrl+U вместо удаления всей строки удаляет только текст от курсора и до начала строки. Думаю, что Ctrl+W также может несколько иначе трактовать понятие «слова».

Есть и многие другие комбинации (вот полный список). Я же перечислила только те, которые часто использую сама.

Наиболее известным инструментом, использующим readline, наверняка является оболочка bash (когда вы используете Ctrl+R для поиска по истории в bash, то эту возможность по факту предоставляет readline), но есть и много других программ — например, psql, irb, python3 и так далее.

▍ Совет: можно использовать readline из любого инструмента с помощью rlwrap


Одним из самых приятных моментов я нахожу то, что если у вас есть программа вроде nc без поддержки readline, то вы можете просто выполнить rlwrap nc, чтобы эту поддержку в неё встроить.

Это невероятная возможность, которая делает многие почти непригодные для использования инструменты более удобными в работе. Можно даже включить в rlwrap собственные варианты автозавершения, хотя я никогда этого не пробовала.

Почему инструменты могут не использовать readline


Думаю, что поддержка readline может отсутствовать в инструментах по следующим причинам:

  • Программа очень проста (например, cat или nc) и мейнтейнеры, возможно, не хотят привносить в неё относительно крупную зависимость.
  • Нюансы лицензирования. Если лицензия программы не GPL-совместима — readline имеет лицензию GPL, а не LGPL.
  • Интерактивной является лишь небольшая часть программы, в связи с чем поддержку readline могли не счесть как значимый элемент. Например, в git немного интерактивных функций (таких как git add -p), и обычно вы просто вводите один символ вроде y или n — если же требуется ввести что-то более значительное, git переносит вас в текстовый редактор.

Например, в idris2 говорят, что не задействуют readline ради сокращения числа зависимостей и для получения более интерактивных возможностей предлагают использовать rlwrap.

Как понять, используете ли вы readline


Простейший тест, какой мне приходит на ум, это нажатие Ctrl+R. Если вы увидите:

(reverse-i-search)`':

Значит, наверняка используете readline. Естественно, это не 100%-метод (некоторые иные библиотеки тоже могут использовать термин reverse-i-search), но я не знаю другую систему, которая бы задействовала его для поиска по истории.

Комбинации клавиш readline взяты из Emacs


Поскольку я пользуюсь vim, то далеко не сразу поняла, откуда взяты эти комбинации (почему Ctrl+A используется для перехода в начало строки??? Очень странно).

Как понимаю я, эти привязки были позаимствованы из Emacs — Ctrl+A и Ctrl+E делают в Emacs то же, что и в Readline. Так что предполагаю, что и многие другие комбинации между ними тоже совпадут, хотя я пробовала Ctrl+W и Ctrl+U в Emacs, и в нём их действия отличаются от действий в терминале. В общем, отличия всё же есть.

Подробнее об истории проекта Readline можно почитать здесь.

Пункт 3: другая библиотека ввода (вроде libedit)


На моём ноутбуке с Mac /usr/bin/python3 работает странным половинчатым образом, поддерживая лишь часть возможностей readline (например, клавиши стрелок). Скажем, при нажатии Ctrl+влево программа выводит ;5D:

$ python3
>>> importt subprocess;5D

Разобраться с этим вопросом мне помогли ребята с Mastodon. Оказывается, в предустановленном на Mac дистрибутиве Python модуль readline по факту заменён модулем libedit, который представляет аналогичную библиотеку, но с менее широкой функциональностью. Причина может заключаться в том, что Readline имеет лицензию GPL.

В конечном итоге выяснить, что в моей версии Python используется libedit, я смогла так:

$ python3 -c "import readline; print(readline.__doc__)"
Importing this module enables command line editing using libedit readline.

Тем не менее обычно Python использует именно readline, если устанавливается под Linux или через Homebrew. Просто конкретная версия, которую инженеры Apple включают в свои системы, работает на libedit.

Кроме того, в Python 3.13 собираются убрать зависимость от readline, заменив её кастомной библиотекой, так что вскоре выражение «Python использует readline» перестанет быть актуальным.

Предполагаю, что на Mac есть и другие программы, которые используют libedit, но я в этом не разбиралась.

Пункт 4: кастомное решение


К последней группе программ относятся те, в которых есть собственная (а иногда и намного более изощрённая) система редактирования текста. К ним относятся:

  • Большинство редакторов текста для терминала (nano, micro, vim, emacs и так далее).
  • Некоторые оболочки вроде fish. Например, fish позволяет при вводе команды использовать отмену через нажатие Ctrl+Z. В zsh задействуется текстовый редактор zle.
  • Некоторые REPL вроде ipython, в котором вместо readline используется библиотека prompt_toolkit.
  • Многие другие программы (вроде atuin).

Вот некоторые из их особенностей:

  • Более эффективное автозавершение, подстроенное под сам инструмент.
  • Более удобное управление историей (например, с выделением синтаксиса) в сравнении с тем, которое по умолчанию предоставляет readline.
  • Больше всевозможных комбинаций клавиш.

▍ Кастомные системы ввода зачастую основаны на readline


Я решила посмотреть, как обрабатывает ввод Atuin (прекрасный инструмент для поиска по истории оболочки, которым я начала пользоваться недавно). Если взглянуть на код и его обсуждение, то становится ясно, что хоть реализация этого инструмента и кастомная, основана она именно на readline. На мой взгляд, это разумно, так как многие пользователи привыкли к используемым в ней комбинациям клавиш, и это обеспечивает для них дополнительное удобство при работе.

Аналогично дела обстоят с prompt_toolkit (библиотекой, которую использует IPython) — фактически она поддерживает множество опций (включая привязки клавиш в стиле vi), но по умолчанию в ней используются привязки именно как в readline.

И есть немало программ, которые поддерживают самые простые привязки клавиш vim (вроде j для «вниз» и k для «вверх»). Например, Fastmail поддерживает j и k, несмотря на то, что основная часть остальных его привязок не имеют отношения к vim.

Предполагаю, что большинство кастомных систем ввода на основе readline имеют различные тонкие расхождения с этой библиотекой, но меня это не волнует, поскольку я крайне плохо разбираюсь в большинстве возможностей readline. Я использую всего где-то 5 комбинаций, поэтому до тех пор, пока такие системы поддерживают 5 базовых известных мне команд (которые в них есть всегда), меня это вполне устраивает. Причём эти кастомные системы зачастую предоставляют намного более качественное автозавершение, чем можно получить при использовании только readline, поэтому обычно я предпочитаю именно их.

Многие оболочки поддерживают комбинации клавиш из редактора vi


Bash, zsh и fish предлагают для ввода текста «режим vi». В очень ненаучном опросе, который я провела на Mastodon, 12% людей сказали, что используют именно его. Так что, похоже, этот режим довольно популярен.

В readline тоже есть «режим vi» (таким образом реализована её поддержка в Bash), то есть по аналогии эта поддержка есть и во многих других программах.

Я всегда считала, что режим vi очень крут, но хоть я и пользуюсь vim, почему-то его так и не освоила.

Важно понимать ситуацию


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

Думаю, что при вводе текста в командную строку я размышляю примерно так:

  1. Работают ли клавиши стрелок? Возможно, системы ввода нет совсем, но я по крайней мере могу использовать Ctrl+W и Ctrl+U, а также дополнить инструмент с помощью rlwrap, если мне потребуется больше возможностей.
  2. Будет ли Ctrl+R выводить reverse-i-search? Если да, то это наверняка readline, и тогда я смогу использовать все привычные мне комбинации, а также выводить какую-то базовую историю и получать предыдущую команду нажатием клавиши вверх.
  3. Делает ли Ctrl+R что-то другое? В этом случае, скорее всего, используется кастомная библиотека, которая будет работать более-менее схожим с readline образом. При этом, если я захочу узнать, как конкретно она работает, то всегда могу заглянуть в документацию.

Возможность подобным образом проверить, что конкретно происходит при использовании разных комбинаций, делает командную строку более предсказуемой и менее хаотичной.

О чём в этом посте не говорилось


При вводе текста существует и много других сложностей, о которых я не сказала. Например:
  • проблемы, связанные с ssh / tmux / и прочим,
  • переменная среды TERM,
  • то, что разные терминалы (gnome, iTerm, xterm и прочие) по-разному реализуют копирование/вставку текста,
  • Юникод,
  • и другие.
Telegram-канал со скидками, розыгрышами призов и новостями IT ?

Комментарии (30)


  1. Dick_from_mountain
    11.08.2024 09:42
    +2

    Я бы как-то ключи команд стандартизировал. Иногда разработчики создают абсолютную путаницу из ключей, только им понятную. Вот например cmake -B и --build, я их всё время путаю, как-то можно более наглядно ключи создавать?


    1. domix32
      11.08.2024 09:42
      +3

      Тут та же проблема что и со стилем кода - кому-то Олман, а кому-то КиР подавай. В итоге у одних однобуквенные флаги разных регистров делают разное, а длинных вообще не завезли (и хэлп максимально полезыный а ля sed [-Ealnru]), у кого-то однобуквы всегда капсом (ну почти - scp -P1234 но scp -i id_rsa.pub vs ssh -p1234 -i id_rsa.pub ), а у третьих однобукв примерно совпадает с длинной командой (ripgrep --help в качестве примера), а у четвертых длинные параметры через одну черточку делаются. Всё просто потому что каждый парсит аргументы каждый раз по-разному.


      1. sundmoon
        11.08.2024 09:42

        Базар жеж тут православный, а не какой-то б-гомерзкий Собор типа Powershell.


        1. domix32
          11.08.2024 09:42

          За консистентность команд ps ничего особо сказать не могу, выглядело в среднем довольно неплохо, не считая того что большинство командлетов выглядт Like-That


          1. sundmoon
            11.08.2024 09:42

            Изначальная мотивация Джефри Сновера (техлида PS, а перед этим автора WMI) - консистентность параметров (среди прочего).


      1. lennylizowzskiy
        11.08.2024 09:42
        +2

        Небольшая заметка:

        а у третьих однобукв примерно совпадает с длинной командой (ripgrep --help в качестве примера)

        В случае утилит на Rust почти все используют один и тот же crate - clap. Так что опыт ввода флагов для утилит на этом языке в 99% случаев будет одинаков.


        1. domix32
          11.08.2024 09:42

          Не только Rust едины. У питона например тоже есть стандартный argparse, который можно настраивать всяко.


  1. jackcrane
    11.08.2024 09:42
    +5

    • + много за rlwrap

    • readline настраивается через /etc/inputrc или ~/.inputrc см man 3 readline


  1. sysor
    11.08.2024 09:42
    +2

    надо было ставить лиииинукс :)


  1. sundmoon
    11.08.2024 09:42
    +4

    К сожалению карма не позволяет поставить люто-бешеный плюс. Сам хейтил линукс вплоть до прошлого десятилетия именно из-за бардака в терминалах.

    Возмущала покорность линуксоидов и, ну ладно не могут исправить положение - так хотя бы они объясняли внятно, "кто тут на ком стоял". Лет бы 15, а лучше 20 назад эту статью...


    1. slonopotamus
      11.08.2024 09:42

      А что произошло в прошлом десятилетии?


      1. sundmoon
        11.08.2024 09:42
        +2

        Пройдя через разные стадии принятия неизбежного и расставшись с иллюзорными надеждами на mc, я приучился, стиснув зубы, сходу запускать nano - где хотя бы стрелки и Del с Backspace работали по-людски. Ну и putty выручал. Встречным образом, в линуксах шире разошлись инструменты с readline.

        А со временем и вовсе настал кроссплатформенный рай... Не столь давно наткнулся на Zellij - вроде вот оно счастье - но быстро понял, что оно для меня оверкил. И tmux со screen я так и не осилил: лучше запущу столько окон и вкладок Windows Terminal, сколько мне нужно. А линь один из рантаймов, иногда остро необходимый и, наконец-то, очеловечившийся.


  1. saboteur_kiev
    11.08.2024 09:42
    +5

    cat, nc, git commit --interactive


    Что-то я тут запутался с первого абзаца.
    Какие стрелки в cat, это команда для вывода а не редактирования

    какие проблемы со стрелками в git commit, там же гит не причем, вопрос в том какая оболочка и какой редактор по умолчанию, в mingw может быть и msword прикрутить можно =) ?

    Проблемы с непечатаемыми символами в терминале, или проблемы передачи хоткеев через различные протоколы и программы (rdp/ssh/и учитывая современные технологии с контейнерами и виртуализацией, еще не всегда понятен трафик ввода-вывода). Они есть, но нужно все-таки уточнять...


    1. unreal_undead2
      11.08.2024 09:42
      +1

      Какие стрелки в cat, это команда для вывода а не редактирования

      С помощью cat >paper.tex теоретически можно статьи писать ) Хотя реально с точки зрения простого пользователя актуальнее ситуация "вывели большой файл, почему стрелками не листается?". То, что реально мы уже не в cat, а в шелле, можно ли вообще получить строки выше - зависит от терминала, а вообще для решения проблемы надо использовать less - это уже какая то чёрная магия )


      1. saboteur_kiev
        11.08.2024 09:42
        +1

        а less тут причем? Какое отношение пагинатор имеет к редактированию?


        1. unreal_undead2
          11.08.2024 09:42
          +1

          Решает проблему со стрелками в cat.


          1. saboteur_kiev
            11.08.2024 09:42
            +2

            cat никак не обрабатывает стрелки. он не редактор.

            less их обрабатывает частично, для навигации по тексту. Но он тоже не редактор, курсор он не двигает, только текст.

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


            1. unreal_undead2
              11.08.2024 09:42

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


              1. saboteur_kiev
                11.08.2024 09:42

                Так зачем бухгалтеру или музыканту ковыряться в кривом терминале?
                Есть удобный пользовательский софт.
                И я не программист, программисты, кстати, зачастую в этом не разбираются


                1. unreal_undead2
                  11.08.2024 09:42
                  +1

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

                  И я не программист

                  Ну скорее всего ITшник, а не гуманитарий )

                  Так то после Примуса, Бейсика на 8битных компьютерах и прочей истории все эти неудобства/особенности современных консолей какие то мелочи, привыкаешь и забываешь.


  1. Tony-Sol
    11.08.2024 09:42
    +1

    setopt vi

    решил для меня все проблемы с Ctrl-A/E и так далее


    1. gpin
      11.08.2024 09:42
      +1

      Полностью согласен. Но и он не идеален

      • Возврат в режим редактирования (esc i) делает нередактируемой строку до курсора. Это раздражает - неудобно и из vim такое поведение давно убрали

      • Не работают home и end клавиши. esc $ и esc 0 это круто, конечно, но можно пожалуйста home и end тоже

      • Не стирается символ переноса строки. Надо жать esc j J, чтобы убрать пустую строку

      А ещё после перехода на helix надо держать в голове 3 разных vi-like системы хоткеев: как в vi (с проблемами выше), как в vim/neovim (без проблем выше), как в helix (select->edit вместо edit->select, мне так удобнее)

      может быть есть где-то плагин для zsh, где эти проблемы vi-mode пофиксили и там хоткеи хотя бы как в vim?


      1. Tony-Sol
        11.08.2024 09:42
        +1

        может быть есть где-то плагин для zsh, где эти проблемы vi-mode пофиксили и там хоткеи хотя бы как в vim?

        есть zsh-vi-mode, но я им не пользовался

        после перехода на helix

        а helix уже адекватный в плане функционала и сообщества? а то раньше плагинов для него днем с огнем не сыщешь


    1. vtb_k
      11.08.2024 09:42
      +1

      решил для меня все проблемы с Ctrl-A/E и так далее

      А были ли проблемы?

      autoload -z edit-command-line
      zle -N edit-command-line
      bindkey "^h" edit-command-line
      

      Ctrl+h и любая команда редактируется в любимом редакторе


  1. valvalva
    11.08.2024 09:42
    +1

    Очень рекомендую:
    (echo '"\e[A": history-search-backward'; echo '"\e[B": history-search-forward') >> /etc/inputrc


  1. paintdrip
    11.08.2024 09:42
    +1

    Использование readline и советы по адаптации команд очень полезны, особенно для тех, кто ежедневно работает в командной строке. Радует, что автор предлагает простые и эффективные решения. Возможно, стоит попробовать инструменты вроде rlwrap для улучшения взаимодействия с терминалом


  1. vviz
    11.08.2024 09:42
    +2

    Открою Америку - имеется возможность поставить GNU Midnight Commander? Поставьте его и бросте выбирать наиболее кошерный редактор для консоли.


    1. unreal_undead2
      11.08.2024 09:42

      Запасаемся попкорном и смотрим на холивар "консоль против клонов NC"...


    1. saboteur_kiev
      11.08.2024 09:42
      +1

      уже можно поставить far2l


  1. siberianlaika
    11.08.2024 09:42

    Так терминалы и прочее основанное на readline по умолчанию используют же раскладку GNU/Emacs. И стрелочки там работают, просто это биндинги Emacs - Ctrl-B (лево), Ctr-F (право) и пр. При этом поддерживается режим Vi через "set -o vi". Статья про то, как не читают документацию.