Цикл while никогда не завершается для EOF

Я пишу свою собственную оболочку на C, и мне нужно обнаружить EOF (когда я запускаю ./myshell < commands.txt)

commands.txt содержит:

ls
pwd

Оба они работают нормально отдельно из программы. Но когда я запускаю его с текстовым файлом, я получаю бесконечный цикл.

В моем цикле while(1) для оболочки первое, что я делаю, это:

if (feof(stdin)) { my_exit(); }

my_exit это просто:

void my_exit() {
    printf("End of file! Bye\n");
    exit(0);
}

Разве exit(0) не завершает программу (и цикл)? Почему я получаю

Конец файла! Пока Конец файла! Пока Конец файла! Пока Конец файла! Пока Конец файла! Пока Конец файла! До свидания.... и т.д.

Я также пробовал делать fgets == NULL. Тот же цикл


person user1687558    schedule 22.09.2012    source источник
comment
Да, exit(0) обязательно должен выйти из процесса. Покажите нам еще немного кода.   -  person Keith Randall    schedule 22.09.2012
comment
Трудно сказать, не видя больше кода, но вы должны определить конец файла, проверив значение, возвращаемое любой функцией ввода, которую вы используете. feof() и ferror() предназначены для выяснения причины сбоя операции ввода. Если есть ошибка ввода, ferror() вернет true, а feof() вернет false, что может привести к бесконечному циклу.   -  person Keith Thompson    schedule 22.09.2012
comment
Я добавил остальную часть своего кода - я также пытался выполнить это обнаружение с помощью fgets == NULL, все равно это привело к бесконечному циклу, без выхода. Странно то, что он выводит текст выхода... так что он нажимает на эту функцию, но выход (0) не завершает программу.   -  person user1687558    schedule 22.09.2012


Ответы (3)


Проблема в том, что feof() сообщает вам, столкнулась ли ПОСЛЕДНЯЯ операция ввода с EOF. Но вы не проверите это до следующей итерации. Итак, когда вы находитесь в EOF, вы делаете fgets(), а затем пытаетесь использовать пустой результат, который он вернул.

Происходит следующее: вы fork() создаете дочерний процесс, а затем вызываете execvp() с пустым именем команды. Это не удается, поэтому дочерний процесс возвращается к началу цикла и делает то же самое. Тем временем родительский процесс вызывает my_exit(). Таким образом, каждый дочерний процесс разветвляет другой собственный дочерний процесс, а затем завершается.

person Barmar    schedule 22.09.2012
comment
Так что не утруждайте себя получением результата execvp(); если он возвращается, это не удалось. Рассмотрите возможность сообщения об ошибке и exit(1); для обозначения ошибки — все в блоке, представляющем дочерний процесс. Вы также можете включить текущий идентификатор процесса в приглашение, по крайней мере, в качестве меры отладки. - person Jonathan Leffler; 22.09.2012
comment
По сути, в этой программе отсутствует множество проверок ошибок. В игрушечных программах это часто опускают, но в данном случае это помогло бы найти проблему. Вам нужно проверить, что fgets() возвращает ошибку, поэтому вы пропускаете остальную часть тела цикла, и что execvp() возвращает что-либо, чтобы вы не возобновляли цикл в дочернем элементе. - person Barmar; 22.09.2012

Принцип работы feof заключается в том, что он возвращает false до тех пор, пока ни одно чтение не достигло EOF. feof сам по себе не проверяет поток, а проверяет, установлен ли индикатор EOF, что происходит при сбое чего-то вроде fgets.

На самом деле не очень хорошая практика использовать feof в цикле управления.

Пример проверки EOF может быть таким:

while( fgets(line, sizeof(line), fp) != NULL )
  fputs(line, stdout);
person Pradeep Nayak    schedule 22.09.2012

Можно попробовать так...

int a=1;
while(a)
{
    if (feof(stdin)) {
        a = 0;
    }
}
printf("End of file! Bye\n");
exit(0);
person Raj Adroit    schedule 22.09.2012
comment
Это выполнит остальную часть тела цикла, даже если он находится в EOF. Почему бы просто не использовать break; для выхода из цикла? - person Barmar; 22.09.2012