Консольный ввод-вывод: printf и scanf не выполняются в ожидаемом порядке

#c #shell #io #stdin #console-input

#c #оболочка #io #стандартный интерфейс #консольный ввод

Вопрос:

 #include <stdio.h>
#include <stdlib.h>
#include <string.h>

void eat() // clears stdin upto and including n OR EOF
{
    int eat;while ((eat = getchar()) != 'n' amp;amp; eat != EOF);
}

int main(){

    printf("n COMMAND : "); char cmd[21]=""; scanf(" s",cmd);

    if(strcmp(cmd,"shell")==0||strcmp(cmd,"sh")==0)
    {
        getchar(); // absorb whitespace char separating 'shell' and the command, say 'ls'
        while(1)
        {
            printf("n sh >>> "); // print prompt
            char shellcmd[1024]=""; // str to store command
            scanf("23[^n]",shellcmd); eat(); // take input of command and clear stdin

            if(strcmp("close",shellcmd)==0||strcmp("x",shellcmd)==0)
                break;
            else
                system(shellcmd);
        }
    }
}
  

В коде происходит некоторое аномальное поведение, которое я не могу уловить.

После ввода sh ls и нажатия [ENTER] ожидаемый ответ:

  1. 1-й scanf() сохранен sh cmd[] и выходит lsn stdin .
  2. getchar() занимает место.
  3. printf() выводит n sh >>> на терминал
  4. второй scanf() сохраняет ls shellcmd[] , оставляет n в stdin
  5. eat() считывает n из stdin, оставляя его пустым
  6. system("ls") выполняется

Т.е. Результаты должны быть такими:

  COMMAND : sh ls

 sh >>>
 file1 file 2 file3 ...

 sh >>> | (cursor)
  

НО

Что я получаю:

 COMMAND : sh ls

file1 file2 file3 ...
 sh >>> 
 sh >>> | 
  

По-видимому, 2-й scanf() и shell() выполняется раньше printf() , или, по крайней мере, это мое предположение.

Что не так?

Скомпилирован на Clang и GCC с cc -Wall -Wextra -pedantic помощью и протестирован на bash как на macOS, так и на Linux

Ответ №1:

Как вы можете найти на странице руководства:

Если поток ссылается на терминал (как обычно делает стандартный вывод), он буферизуется по строке

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

Решения:

  1. Добавьте новую строку в конце вашего сообщения printf("n sh >>> n");

  2. Принудительно отобразите текущий буфер даже при отсутствии новой строки, вызвав flush() функцию ( fflush(stdout) )

  3. Измените текущее поведение буферизации стандартного вывода с помощью setvbuf() функции

     setvbuf(stdout,NULL,_IONBF,0);
      

Комментарии:

1. О, я вижу 0 0 … так что это не ошибка в моей программе точно 🙂

2. @user13863346 попробуйте (я бы начал с варианта 2) и дайте мне знать, если это сработает

3. Да, это сработало … исправлено! flush() работает, как и 'n' в конце моей строки формата.. Я обескуражен 3-м, поэтому не буду пробовать;)

4. @user13863346 Просто позвоните setvbuf(stdout,NULL,_IONBF,0); перед печатью чего-либо.

5. Ошибка связана с безумно неинтуитивным базовым консольным API C…