fprintf不在^ C上写,但在退出上写

时间:2018-11-01 21:58:28

标签: c shell printf fopen io-redirection

我正在用C编写一种名为tsh(技术外壳)的终端外壳语言。外壳程序必须能够处理输出重定向(“>”)。我将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$

这意味着该文件已打开,但是当我停止该进程时,由于某种原因未写入tsh的pwd的输出。

tsh$ pwd确实可以在没有重定向的情况下打印到stdout。

我把文件放在bash中的原因是因为tsh不支持非内置命令(但是),但是考虑到内容直到退出shell才被写入,所以我不认为它即使我能从外壳上抓猫也能正常工作。

此外,如果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 :(得分:0)

^ 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)) {}

答案 1 :(得分:0)

调用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);
    }
}