2015-12-26 3 views
5

Я стараюсь изо всех сил создавать простые оболочки на Linux. Просто я могу создать, чтобы узнать, как использовать базовые системные вызовы.Работа с пользовательскими вводами на linux

Сценарий: пользователя в команде, прессы вкладка (так что оболочка автоматически завершает свою команду), то команда автоматического завершения выскакивает (или предложения), пользователь нажимает введите, команду Evals и выполняет ,

Как и в bash.

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

Я знаю, какие варианты у меня есть:

  • getc() - получить каждый символ отдельно, сохранить его в буфере. Невозможно понять, как получить нажатия клавиш нажатия клавиш, поскольку он приостанавливает выполнение, пока не увидит «\ n» или Ctrl + D. Kinda дорого, так как для каждого символа команды будет 1 getc(). Кроме того, мне придется иметь дело с перераспределением буфера, амортизацией ... boo ...
  • scanf("%s") - слишком много беспокоиться о переполнении буфера. Не могу получить эти нажатия клавиш, которые я не сделал. Приостанавливает выполнение
  • read() (from unistd.h) - может быть, я не хочу. Но я видел людей здесь, которые говорили, что это настоящая боль, чтобы использовать его для этого. Я проверил. Это.
  • getline() - не удается получить нажатия клавиш.

Я изучил исходный код bash, чтобы узнать, как он работает с вводом, и OH MY GOD. Есть 450 строк кода, посвященных этой простой вещи (файл input.c).

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

+1

'if (fgetc (stdin) == '\ t') {// запуск}' –

+0

@self: приостанавливает выполнение. «Запуск» выполняется только после нажатия клавиши ввода. Мне нужно получить этот символ, прежде чем я нажимаю enter, поэтому я могу выполнить автозаполнение для того, что уже было записано в поток ввода. –

+0

использовать его в цикле 'for (;;)', как только вы получите '\ t', просто отбросьте все остальные входные данные –

ответ

4

Чтобы получить определенное автозаполнение, вы можете использовать библиотеку GNU readline, которая используется bash.

Если вам небезразличен полный ввод/вывод терминала (à la vi или emacs), рассмотрите GNU ncurses.

Терминалы являются довольно сложными и загадочными (потому что они хотят подражать странным физическим телетайпам прошлого века). Часто часть обработки строк выполняется в ядре для line discipline tty. Прочтите веб-страницу tty demystified. Следовательно, функции низкого уровня à la termios(3) являются тайными для использования, и вы должны предпочесть библиотеки, такие как readline или ncurses.

Итак, нет, нет простых решений для терминалов ввода/вывода для автозавершения, потому что ttys - сложный материал. Смотрите также tty(4) & tty_ioctl(4) & pty(7)

Вы можете также использовать strace(1) понять все сложные system calls сделано, например, интерактивная оболочка.

См. Также this & that ответы.

+0

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

+0

Действительно, gnu bash использует gnu readline для этого. – Jasen

+0

Я мог бы использовать внешнюю библиотеку для этого, но на данный момент он находится на втором месте, когда дело доходит до того, что оно «достаточно элегантно». Я узнал, что это сработает, но сейчас я не хочу проверять ответ @ chqrlie. Кажется, что это больше ... «чистый» ... –

4

Чтобы получить индивидуальные нажатия клавиш с терминала без каких-либо задержек или буферизации, вы должны изменить его режим от приготовленного к необработанному. Вы можете сделать это с помощью функции tcsetattr(), определенной в <termios.h>. Посмотрите на страницу man для деталей.

Как только вы установили терминал в соответствующий режим, целесообразно использовать системный вызов read() для считывания данных с дескриптора терминала.

Помните, что вам придется иметь дело с эхом ввода данных пользователя, и если вы начнете делать продвинутые вещи, такие как расширение TAB, вам нужно будет реализовать большую часть редактора строк ... Не говоря уже о том, как обрабатывать состав персонажа и другие странности терминал дает вам бесплатно. Как говорит Базиле, для этого нет простого решения для ручной работы, но очень поучительно погрузиться в этот беспорядок!

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

+0

Я только что понял, что вы сказали в своем редактировании. Да, погружение в это весело. И в то же время это нечто настолько наглое, что просто прикасаться к нему «оскорбляет» меня. Мне придется пойти с ответом Базиле, потому что, когда дело доходит до сырого режима, у меня начинаются мысли о моей карьере в качестве инженера-программиста. И самоубийцы тоже. Однако я должен вернуться к сопернику. –

+1

Не обижайтесь ;-) Втягиваясь в этот низкоуровневый материал чувствует себя как пилинг на луке: каждый раз, когда вы думаете, что нашли ядро, есть еще один слой неожиданного пуха, чтобы соскоблить, и это заставляет вас плакать. – chqrlie

+1

Ах, но 'termios.h' лук - необходимая специя в любом хорошем блюде I/O. Просто зажгите свечу во время пилинга, чтобы свести к минимуму дискомфорт, смешайте с щепоткой 'select' и' switch', а хороший базовый обработчик ввода-вывода можно выпекать менее чем за час. Хороший рецепт, вероятно, можно найти в S.O. поваренную книгу путем поиска '' kbhit''. Вам нужно будет изменить по вкусу ... –

Смежные вопросы