2016-05-11 3 views
6

Как упражнение в понимании моего компьютера лучше, и как инструмент, я пишу my own shell в C++. Stephen Brennan's article on writing a simple shell был очень полезным.Написание моей собственной оболочки: как реализовать историю команд?

Однако, что мне смутно, как справиться с нажатием стрелки вверх и стрелки вниз для прокрутки моей истории команд.

  • Я попытался ncurses, но заменяет весь экран, в то время как система, при условии, оболочка кажется просто продолжать писать в терминал.

  • Я попытался использовать tcgetattr, чтобы отключить канонический режим, но хотя это позволяет мне нажимать клавиши со стрелками при их вводе, он также отключает всю обработку клавиш со стрелками влево/вправо для текстовой навигации и клавиши возврата, и Ctrl-C ... Хотя я мог бы, вероятно, послать сигнал сам в ответ на Ctr-C, я понятия не имею, как заставить терминал переместить курсор назад (кроме вывода «возврата» и повторной записи начала линии). Кажется, это также дает мне разные escape-последовательности для ключей, в зависимости от того, запущен ли я в «тупиком» терминале Xcode или в Terminal.app моего Mac.

  • Я смотрел на источники для fish Shell и bash, но только кажется, что так происходит, что я не могу найти соответствующие части.

Как стандартная оболочка обрабатывает принимающие нажатия клавиш? Как они обрабатывают перемещение курсора и выполняют обратное пространство? Как они переписывают части строки без необходимости перехватывать экран? Есть ли где-то стандарт, определяющий, что нужно делать оболочке?

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

+1

Используйте [GNU Readline] (https://cnswww.cns.cwru.edu/php/chet/readline/rltop.html) или эквивалент [BSD editline] (HTTP: // thrysoee.dk/editline/). –

+1

Close-voters: Надеюсь, вы прочитали обновленный вопрос, кажется, сейчас хорошо :) –

+0

Я вообще не знаю, как это реализовано, но мое подозрение в том, что оболочка переписывает весь экран (à la ' ncurses'). Например, если я нажимаю C-l в 'bash', он очищает весь экран. Это говорит о том, что оболочка делает больше, чем просто управление одной строкой за раз. Я думаю, что библиотеки, подобные 'readline', так же захватывают весь терминал. –

ответ

1

Вы должны отключить ICANON и ECHO и интерпретировать escape-последовательности с помощью клавиш со стрелками самостоятельно.

Вы должны сохранить свой собственный «фактический» буфер того, что находится на экране и где находится курсор. Вам также нужен «желаемый» буфер того, что вы хотите на экране, и где вы хотите использовать курсор. Эти буферы не охватывают весь экран, просто строки, содержащие ваше приглашение, и пользовательский ввод (который вы эхом отдавали вручную, потому что вы отключили ECHO). Поскольку вы печатали все на этих строках, вы знаете их содержимое.

Перед тем как подождать следующего входного байта, вы обновите экран в соответствии с желаемым буфером. Когда вы были на 300 (или даже 9600) бод-соединении, вы очень заботились о том, чтобы сделать это обновление настолько эффективным, насколько это возможно, ища оптимальную последовательность печатных байтов и последовательностей управления терминалом, чтобы преобразовать фактический буфер в нужный буфер. В наши дни гораздо менее важно быть оптимальным.

Эти буферы будут охватывать линии, если входные обертывания, поэтому вам нужно знать и отслеживать ширину терминала (используя TIOCGWINSZ и SIGWINCH). Вы можете придерживаться единственной строки с горизонтальной прокруткой вместо переноса строки, но вам все равно нужно знать ширину терминала.

Теоретически вы ищете свой тип терминала (от $TERM) в базе данных termcap или terminfo. Это говорит вам, какие escape-последовательности следует ожидать, когда пользователь нажимает специальные клавиши (стрелки, дом, конец и т. Д.), и какие escape-последовательности для отправки, чтобы перемещать курсор, очищать части экрана, вставлять или удалять символы или строки и т. д.

В эти дни вполне можно предположить, что все довольно совместимо с xterm, особенно для проекта хобби ,

Для bash все это делается в библиотеке readline GNU. Обновление экрана (называемое «redisplay») выполняется в display.c. Декодирование выходного выхода выполняется в input.c.

Однако, если вы хотите использовать примерный код, вы должны, вероятно, взглянуть на linenoise, который составляет менее 2000 строк. Предполагается, что терминал VT100 (и, следовательно, xterm) совместим.

Смотрите также “Is there a simple alternative to Readline?”