Когда выполняется команда cat
, терминал находится в каноническом режиме ввода . Короче говоря, это означает, что линейная дисциплина терминала обрабатывает редактирование строки и реагирует на все специальные символы , настроенные для терминала (, которые можно просмотреть и установить с помощью stty
команда ).
Команда cat
просто read()
выполняет вызов из своего стандартного ввода до тех пор, пока вызов read()
не вернет нулевые прочитанные байты, соглашение POSIX для достижения конца файла.
Терминалы на самом деле не имеют "конца". Но есть такое обстоятельство, что read()
терминального устройства возвращает нулевые байты. Когда линейная дисциплина получает специальный символ «EOF», что бы он ни был настроен в то время, он заставляет read()
вернуться с тем, что находится в буфере редактирования в этот момент. Если буфер редактирования был пуст, это возвращает нулевые байты, прочитанные из read()
, что приводит к выходу из cat
.
cat
также завершает работу в ответ на сигналы, действия которых по умолчанию заключаются в завершении процесса. Линейная дисциплина также генерирует сигналы в ответ на специальные символы. Специальные символы «INTR» и «QUIT» вызывают отправку сигналов INT
и QUIT
процессу переднего плана (группе ), которая будет/содержит процесс cat
. По умолчанию эти сигналы завершают процесс cat
.
Что приводит к наблюдаемым различиям:
cat
, если в данный момент строка фактически не пуста. Однако прерывание, сгенерированное Ctrl + C , будет.cat
на языке C блокирует стандартный вывод буфера, если обнаруживает, что он направлен на файл, как в вопросе. Теоретически это может привести к потере буферизованных и еще не выведенных строк, если cat
завершается с помощью SIGINT
. На практике библиотеки C BSD и GNU реализуют режим буферизации, который не описан в стандартах языка C или C++. Стандартный вывод при перенаправлении в файл или канал — это интеллектуальная буферизация . Это блочная буферизация; за исключением того, что всякий раз, когда библиотека C обнаруживает, что приближается read()
к началу новой строки из любого файлового дескриптора, открытого для терминального устройства, она сбрасывает стандартный вывод. (Библиотеки C BSD и GNU не совсем реализуют одинаковую семантику и, строго говоря, делают больше, чем это, но такое поведение является общим подмножеством. )Таким образом, сигнал прерывания не приведет к потере буферизованного вывода, когда cat
построен поверх такой библиотеки C.
cat
является частью командного конвейера, некоторые другие процессы могут буферизовать данные после cat
до того, как эти данные попадут в выходной файл. Итак, еще раз, когда линейная дисциплина отправляет SIGINT
, что (по умолчанию )завершает все процессы в конвейере, входные данные, буферизованные и еще не записанные, будут потеряны; тогда как обычное завершение cat
специальным символом "EOF" приведет к нормальному завершению конвейера, при этом все данные будут переданы нижестоящему процессу до того, как он получит индикацию EOF от его read()
. ] его стандартного ввода. Обратите внимание, что это имеет очень мало отношения к тому, что происходит, когда ваша интерактивная оболочка считывает от вас строку ввода. Когда ваша оболочка ожидает ввода, терминал находится в не-каноническом режиме ввода , в котором линейная дисциплина не выполняет никакой специальной обработки специальных символов.То, как ваша оболочка обрабатывает Ctrl + D и Ctrl + C , полностью зависит от библиотеки редактирования ввода, которую использует ваша оболочка (. libedit, readline или ZLE )и как эта библиотека редактирования была настроена (с привязками клавиш и т.п. ).