Обычно, чтобы указать EOF для программы, подключенной к стандартному вводу на терминале Linux, мне нужно нажать Ctrl + D один раз, если я только что нажал Enter, или два раза в противном случае.Я заметил, что команда patch
отличается.При этом мне нужно дважды нажать Ctrl + D, если я только что нажал Enter, или три раза в противном случае.(Выполнение cat | patch
вместо этого не имеет этой странности. Кроме того, если я нажимаю Ctrl + D перед тем, как вообще вводить какой-либо реальный ввод, у него нет этой странности.) Копаясь в исходном коде patch
, я проследилэто возвращается к , как это происходит на fread
.Вот минимальная программа, которая делает то же самое:
#include <stdio.h>
int main(void) {
char buf[4096];
size_t charsread;
while((charsread = fread(buf, 1, sizeof(buf), stdin)) != 0) {
printf("Read %zu bytes. EOF: %d. Error: %d.\n", charsread, feof(stdin), ferror(stdin));
}
printf("Read zero bytes. EOF: %d. Error: %d. Exiting.\n", feof(stdin), ferror(stdin));
return 0;
}
При компиляции и запуске вышеуказанной программы в том виде, в каком она есть, вот график событий:
- Моя программа вызывает
fread
. fread
вызывает системный вызов read
. - Я набираю «asdf».
- Я нажимаю Enter.
read
системный вызов возвращает 5. fread
снова вызывает системный вызов read
. - Я нажимаю Ctrl + D.
- Системный вызов
read
возвращает0. fread
возвращает 5. - Моя программа печатает
Read 5 bytes. EOF: 1. Error: 0.
- Моя программа снова вызывает
fread
. fread
вызовысистемный вызов read
. - Я снова нажимаю Ctrl + D.
- Системный вызов
read
возвращает 0. fread
возвращает 0. - Моя программа печатает
Read zero bytes. EOF: 1. Error: 0. Exiting.
Почему это средство чтения стандартного ввода имеет такое поведение, в отличие от способа, которым любая другая программа, кажется, читает его?Это ошибка в patch
?Как должен быть написан этот вид цикла, чтобы избежать такого поведения?
ОБНОВЛЕНИЕ: Это, похоже, связано с libc.Первоначально я испытал это на glibc 2.23-0ubuntu3 из Ubuntu 16.04.@Barmar отметил в комментариях, что это не происходит в macOS.Услышав это, я попытался скомпилировать ту же программу для musl 1.1.9-1, также из Ubuntu 16.04, и у нее не было этой проблемы.В musl последовательность событий удалена с 12 по 14, поэтому у нее нет проблемы, но в остальном она та же (за исключением несущественной детализации readv
вместо read
).
Теперь возникает вопрос: является ли glibc неправильным в своем поведении или патч неверен, если предположить, что его libc не будет иметь такого поведения?