Завершение Bash иногда сбивает мой терминал, когда функция завершения читает файл

Итак, у меня возникла проблема с некоторыми программами cli. Иногда, когда я завершаю запущенный процесс с помощью Ctrl+C, он оставляет терминал в странном состоянии (например, эхо выключено). Теперь этого следует ожидать во многих случаях, так как уничтожение процесса не дает ему возможности восстановить состояние терминала. Но я обнаружил, что во многих других случаях виновником является завершение bash. В качестве примера попробуйте следующее:

  1. Запустите новый сеанс bash следующим образом: bash --norc чтобы убедиться, что никакие завершения не загружены.
  2. Определите функцию завершения: _completion_test() { grep -q foo /dev/null; return 1; }.
  3. Определите завершение, использующее указанную выше функцию: complete -F _completion_test rlwrap.
  4. Введите точно следующее: r l w r a p Пробел c a t Tab Enter (т. е. rlwrap cat, затем Tab, а затем Enter).
  5. Подождите секунду, а затем завершите процесс с помощью Ctrl+C.
  6. Эхо терминала не должно было быть отключено. Таким образом, если вы наберете какой-либо символ, он не будет воспроизведен терминалом.

Что действительно странно, так это то, что если я уберу, казалось бы, безобидный grep -q foo /dev/null из функции завершения, все будет работать правильно. Фактически, добавление grep -q foo /dev/null (или даже чего-то еще более простого, такого как cat /dev/null) к любой функции завершения, установленной в моей системе, вызывает ту же проблему. Я также воспроизвел проблему с программами, которые не используют readline и без Ctrl+C (например, find /varTab| head, с приведенным выше завершением определено для find).

Почему это происходит?

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

_completion_test() {
    if grep -q "$1" /some/file; then
        #do something
    else
        #do something else
    fi
}

Для более конкретного примера попробуйте следующее:

_completion_test() {
    if grep -q foo /dev/null; then
        COMPREPLY=(cats)
    else
        return 1
    fi
}

Но сам факт того, что я звоню grep, вызывает проблему. Я не понимаю, почему я не могу позвонить grep в этом случае.


person safsaf32    schedule 22.08.2016    source источник
comment
Какая это версия Баша?   -  person Etan Reisner    schedule 22.08.2016
comment
Пробовал с 4.3.42, 4.3.46, 4.4 beta 2, но пробовал и с более старыми версиями.   -  person safsaf32    schedule 22.08.2016
comment
Воспроизведено с 4.1.2, но даже не требует grep/cat. Я заставил это случиться с _c_t() { blah; }.   -  person Etan Reisner    schedule 22.08.2016
comment
Что вы использовали для blah? Буквально blah? Я обнаружил, что некоторые команды вызывают проблему, а некоторые нет.   -  person safsaf32    schedule 22.08.2016
comment
Этот вопрос не относится к теме переполнения стека, как указано в справочном центре. Возможно, он больше подходит для сайта Unix и Linux Stack Exchange.   -  person Toby Speight    schedule 22.08.2016
comment
Да, буквально blah (как в несуществующей команде).   -  person Etan Reisner    schedule 22.08.2016


Ответы (2)


Ну, ответ на это очень прост; это ошибка:

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

person safsaf32    schedule 24.08.2016

Вы просто неправильно реализуете функцию завершения. См. руководство

-F функция

Функция функции оболочки выполняется в текущей среде оболочки. Когда он выполняется, $1 — это имя команды, аргументы которой завершаются, $2 — это завершаемое слово, а $3 — это слово, предшествующее завершаемому слову, как описано выше (см. Программируемое завершение). Когда он завершается, возможные завершения извлекаются из значения переменной массива COMPREPLY.

например, следующая реализация:

_completion_test() { COMPREPLY=($(cat /dev/null)); return 1; }

не ломает терминал.

Что касается вашего первоначального вопроса, почему ваша функция завершения ломает терминал, я немного поиграл с strace и увидел, что есть ioctl вызовы с -echo аргументом. Я предполагаю, что когда вы завершаете его с помощью Ctrl+C, аргумент ioctl с аргументом echo просто не вызывается для восстановления исходного состояния. Ввод stty echo вернет echo.

person Yuri G.    schedule 22.08.2016
comment
Спасибо за ответ, но он не совсем отвечает на мой вопрос. cat /dev/null был просто примером. В моей фактической реализации я использовал grep. Я обновил свой вопрос, чтобы сделать это более ясным. Я действительно не хочу записывать вывод этой команды в COMPREPLY. - person safsaf32; 22.08.2016