Результаты printf() и system() находятся в неправильном порядке, когда вывод перенаправляется в файл

У меня есть программа на C, которая компилируется в исполняемый файл, называемый myprogram. Это его основная функция:

int main(int argc, char ** argv) {
  printf("this is a test message.\n");
  system("ls");

  return 0;
}

Когда я запускаю myprogram > output.txt в оболочке Linux, а затем проверяю output.txt, я вижу вывод ls перечисленных выше, "это тестовое сообщение".

Я чувствую, что это должно быть наоборот. Почему это происходит, и что я могу сделать, чтобы "это тестовое сообщение" появилось в верхней части output.txt?

Если это имеет значение, я новичок в C и работает в командной строке.

Ответы

Ответ 1

По умолчанию вывод в stdout выполняется при буферизации по строке при подключении к терминалу. То есть, буфер очищается, когда он заполняется или когда вы добавляете новую строку.

Однако, если stdout не подключен к терминалу, например, что происходит при перенаправлении вывода из вашей программы в файл, то stdout становится полностью буферизованным. Это означает, что буфер будет сбрасываться и фактически записываться либо при заполнении, либо при явной очистке (что происходит, когда программа выходит).

Это означает, что вывод отдельного процесса, начинающегося с вашего кода (например, то, что происходит, когда вы вызываете system), скорее всего, будет написан первым, так как буфер этого процесса будет очищен, когда этот процесс завершится, что находится перед вашим собственным процессом.

Что происходит при использовании перенаправления (или труб в этом случае):

  1. Ваш вызов printf записывается в буфер stdout.
  2. system функция запускает новый процесс, который записывается в собственный буфер.
  3. Когда внешний процесс (запущенный system вызовом) завершается, его буфер очищается и записывается. Ваш собственный буфер в вашем собственном процессе не затрагивается.
  4. Ваш собственный процесс завершается, и ваш буфер stdout очищается и записывается.

Чтобы получить результат в "правильном" (или, по крайней мере, ожидаемом) порядке, вызовите fflush перед вызовом system, чтобы явно setbuf stdout или вызвать setbuf до того, как какой-либо вывод полностью отключит буферизацию.

Ответ 2

Это связано с буферизацией вывода. Мне удалось воспроизвести такое же поведение. Принуждение флеш сделал это для меня.

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

int main(int argc, char ** argv) {
  printf("this is a test message.\n");
  fflush(stdout);
  system("ls");

  return 0;
}

Перед добавлением fflush:

$ ./main > foo
$ cat foo
main
main.c
this is a test message.

и после:

$ ./main > foo
$ cat foo
this is a test message.
foo
main
main.c

Ответ 3

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

Попробуйте добавить fflush (stdout) после инструкции printf и посмотреть, будет ли это принудительно выводить вывод.