bind -x оставляет терминал в состоянии, напоминающем ^ V

Я определяю keybinding оболочки в моем ~/.bashrc следующим образом:

 bind -x '"\e\Cw":"/usr/bin/reset"' 

Это приводит к тому, что Ctrl-Alt-w запускает исполняемый файл reset . Тем не менее, он оставляет терминал в состоянии, которое напоминает тот, с которым вы входите, нажав Ctrl-v . Так, например, если сразу после сброса таким образом я нажимаю Ctrl-p , вместо вызова действия этого ярлыка (повторить предыдущую команду) я получаю ^P напечатанным.

Любая идея, что вызывает эту проблему и как ее исправить?

EDIT : Чтобы помочь потенциальным респондентам, я подключаю программу Python, которая воспроизводит испорченное состояние терминала, которое я пытаюсь исправить с помощью reset . Я очень часто сталкиваюсь с этим условием при отладке многопоточных приложений:

 import pdb, thread, time def interrupt(): time.sleep(2) pdb.set_trace() thread.start_new_thread(interrupt, ()) raw_input('? ') 

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

Запуск reset очищает дисплей терминала, а также сбрасывает все настройки ввода по умолчанию. В частности, он устанавливает режим ввода для приготовления , т. Е. Терминал считывает одну строку за раз перед отправкой всей строки в приложение (здесь приложение bash). Редактор строк терминала является чрезвычайно примитивным, который понимает только «backspace», ничто не увлекается. Bash предоставляет сложный редактор строк; он переключает терминал в режим raw, где каждый символ отправляется в приложение сразу после его ввода.

Если вы столкнулись с переполненным терминалом (без строки или без эха в приглашении bash), самый простой способ восстановить его – запустить reset команды или запуститься. Обычно вы можете ввести их вслепую и нажать Return . Если это не работает (например, потому что терминал находится в режиме готовности, а символ отправки строки не является стандартным), вы можете запустить reset 2>/dev/pts/42 (повторная инициализация терминала) или stty sane </dev/pts/42 (повторная инициализация конфигурации ввода) (обратите внимание на различные перенаправления), где /dev/pts/42 – это терминал, в котором работает оболочка. Поиск имени терминала, если вы не можете запускать команды в нем, может занять немного угадывание. Внутри терминала команда tty отобразит ее. Если вы можете найти правильный процесс bash в выводе ps , вам нужен столбец TTY , с /dev впереди.

Выполнение этих команд, введя их в подсказке bash, делает правильные вещи, но не запускает их как часть макроса readline. Bash сбрасывает настройки терминала каждый раз, когда печатает новое приглашение, поэтому то, что вы делаете во время издания одной строки, не сохраняется до последующих команд.

Кроме того, если вы запускаете reset во время выпуска строки, это испортит параметры, на которые опирается bash: в частности, он устанавливает режим терминала для приготовления, тогда как для строки строки bash требуется, чтобы редактор строк получал входной символ по символу. Сравнивая вывод stty во время выпуска командной строки bash и, не входя в подсказку bash, я думаю, что это настройки, которые вам нужны:

 bind -x '"\e\Cw": "reset; stty -icrnl -icanon -echo </dev/tty"' 

Если вы вызываете reset только для очистки дисплея, вызовите tput rs1 rs2 rs3 rf вместо reset .

Как я уже писал выше, правильным способом сброса настроек терминала является выполнение reset в приглашении bash. Запуск его как части связывания ключей не работает, потому что bash восстанавливает параметры, оставшиеся последним приложением (тем, которое оставило настройки терминала в беспорядке), когда отображает следующую подсказку. Я не думаю, что у bash есть встроенная функция, вместо этого, чтобы сбросить настройки терминала до нормального значения по умолчанию, но вы можете сделать это с настройкой пользователя, если хотите, со следующей строкой в ​​вашем .bashrc :

 PROMPT_COMMAND="$PROMPT_COMMAND stty sane" 

Если вы действительно хотите иметь привязку клавиш, которая сбрасывает настройки терминала во время выпуска строки, вам нужно что-то более сложное. Вызов bash для запуска команды reset в приглашении (в отличие от части команды редактирования), а затем возобновить текущее редактирование. Это непросто сделать в bash, потому что привязки могут быть только макросами readline или bash, но вы не можете их смешивать. Следующий код связывает Ctrl + Meta + W с макросом readline, который вызывает функцию bash через привязку, затем вызывает функцию read accept-line readline через свою привязку \Cm , а затем вызывает другую функцию bash через другое связывание. bind -x может быть назначено только для последовательностей клавиш длиной 1 или 2 , поэтому я использую малоиспользуемые комбинации Cx LETTER для вспомогательных макросов.

 run_command_during_line_edition () { saved_READLINE_LINE=$READLINE_LINE saved_READLINE_POINT=$READLINE_POINT READLINE_POINT=0 READLINE_LINE=" $1" unset run_command_first } restore_saved_command () { READLINE_LINE=$saved_READLINE_LINE READLINE_POINT=$saved_READLINE_POINT unset saved_READLINE_LINE saved_READLINE_POINT } bind -x '"\C-xZ": "restore_saved_command"' bind -x '"\C-xR": "reset; stty sane -icrnl -icanon -echo; run_command_during_line_edition reset"' bind '"\e\Cw": "\C-xR\r\C-xZ"' 

Опять же, вам, вероятно, не нужно все это усложнение – слепое PROMPT_COMMAND в запрос запроса или включение stty sane в ваш PROMPT_COMMAND должно решить вашу проблему. О, или вы могли бы переключиться на zsh, где все это было бы легким ветром.