fprintf не пишет на ^ C, но пишет при выходе - PullRequest
0 голосов
/ 02 ноября 2018

Я пишу терминальный язык оболочки на C, называемый tsh (tech shell). Оболочка должна иметь возможность обрабатывать перенаправление вывода («>»). Я использую fprintf с fopen для достижения этой цели. Проблема выглядит следующим образом:

Если я использую перенаправление, как оно есть в моей оболочке, с командой выхода:

tsh$ pwd > out.txt
tsh$ exit

bash$ cat out.txt
/tmp
bash$

Но если я сделаю то же самое с Ctrl + C вместо exit:

tsh$ pwd > out.txt
tsh$ ^C

bash$ cat out.txt
bash$

Это означает, что файл открывается, но когда я останавливаю процесс, вывод команды pshd из tsh по какой-то причине не записывается.

tsh$ pwd однако печатает на стандартный вывод, если перенаправление отсутствует.

Причина, по которой я отслеживаю файл в bash, заключается в том, что tsh не поддерживает не встроенные команды (пока), но, учитывая, что содержимое не записывается до тех пор, пока я не выйду из своей оболочки, я не думаю, что это будет работать, даже если бы я мог кошку из моей раковины.

Кроме того, если out.txt еще не существует, создает в обоих случаях, но все равно записывается только в первом случае. Короче говоря, у меня нет проблем с получением ссылки на файл, просто пишу в него.

Это текущий источник для tsh.c, скомпилированный с gcc:

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

const char EXIT[4] = "exit";
const char PWD[3] = "pwd";
const char * DELIM = " \n\r\t";

FILE * outfile, * infile;

char cwd[1024]; //directory to print at beginning of each line

char in[1024]; //user input
char * token;

int main()
{

    while(1)
    {

        outfile = stdout;   
        infile = stdin;

        getcwd(cwd, sizeof(cwd));
        printf("%s$ ",cwd); //prompt with cwd       

        fgets(in, sizeof(in), stdin);

        char * fileref;
        if((fileref = strstr(in,">")))
        {

            fileref = strtok(++fileref, DELIM);
            outfile = fopen(fileref, "w");

        }

        token = strtok(in, DELIM);
        if (token != NULL)
        {
            if(!strncmp(token, EXIT, sizeof(EXIT)))
            {

                token = strtok(NULL, DELIM);
                if (token == NULL)
                {
                    printf("Exiting with status code 0.\n");
                    exit(0);
                }
                else if (isdigit(*token))
                {
                    int code = *token - '0';
                    printf("Exiting with status code %d\n",code);
                    exit(code);
                }
            }
            else if(!strncmp(token, PWD, sizeof(PWD)))
            {
                fprintf(outfile, "%s\n",cwd); //This should get written to the file if I provide a redirection
                continue;
            }

            fputs("\n", outfile);

        }

        fclose(outfile);

    }

}

Существует ли какой-то встроенный механизм отката, который предотвращает запись файла, если процесс не завершается корректно? Должен ли я перехватить и переопределить сигнал ctrl + c?

Я знаю, что здесь много проблем и плохих практик , так как это мой первый опыт работы с C, но я был бы признателен, если бы предложения могли быть ограничены конкретной проблемой, с которой я сталкиваюсь.

Заранее спасибо.

Ответы [ 2 ]

0 голосов
/ 04 ноября 2018

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

С другой стороны, когда вы нажимаете CTRL + C, вы отправляете SIGINT своему процессу, и поведение процесса по умолчанию при получении такого сигнала должно завершаться; но это не чистое завершение процесса (это похоже на то, что происходит, когда вы получаете ошибку сегментации), поэтому все открытые файлы закрываются некорректно, и если есть какие-либо буферизованные выходные данные, они будут потеряны.

В вашем приложении вы сможете решить описанную проблему, исправив логическую ошибку в коде: внутри основного цикла while() вы вызываете fopen(), когда встречаете символ «>» в пользовательский ввод, но когда вы выполняете команду 'pwd', вы не закрываете файловый поток, возвращаемый fopen(), а на следующей итерации цикла while() вы перезаписываете указатель файлового потока с помощью stdout, фактически вытекая открытый файл. Вместо этого вы должны обязательно закрыть файл перед началом следующей итерации цикла. Как ниже:

while(1)
{

    outfile = stdout;
    infile = stdin;

    getcwd(cwd, sizeof(cwd));
    printf("%s$ ",cwd); //prompt with cwd

    fgets(in, sizeof(in), stdin);

    char * fileref;
    if((fileref = strstr(in,">")))
    {

        fileref = strtok(++fileref, DELIM);
        outfile = fopen(fileref, "w");

    }

    token = strtok(in, DELIM);
    if (token != NULL)
    {
        if(!strncmp(token, EXIT, sizeof(EXIT)))
        {

            token = strtok(NULL, DELIM);
            if (token == NULL)
            {
                printf("Exiting with status code 0.\n");
                exit(0);
            }
            else if (isdigit(*token))
            {
                int code = *token - '0';
                printf("Exiting with status code %d\n",code);
                exit(code);
            }
        }
        else if(!strncmp(token, PWD, sizeof(PWD)))
        {
            fprintf(outfile, "%s\n",cwd); //This should get written to the file if I provide a redirection
        }
        else
        {
            fputs("\n", outfile);
        }
    }
    if(fileref)
    {
        fclose(outfile);
    }
}
0 голосов
/ 02 ноября 2018

^ C отправляет сигнал KILL в вашу программу (KILL). Вы должны использовать ^ D для завершения ввода вместо (EOF).

   fgets() returns s on success, and NULL on error or when end of file occurs while no characters have been read.

Итак, вы должны использовать: while (fgets(in, sizeof(in), stdin)) {}

...