c - 从文件(shell)执行命令

标签 c io segmentation-fault

我正在尝试制作一个充当迷你外壳的程序(它执行在终端中编写的命令)。为此,我尝试测试此方法的工作原理,我创建了一个文件,将在终端中解析的参数放入其中,读取该文件,然后将其解析为数组以使用 execl 或 execvp 等系统调用执行..ETC。到目前为止我编写的代码是这样的:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <string.h>
#define MAX 1024

FILE* ouvrir(char *cmd)
{
    FILE *commandeLog = fopen("commande.txt", "w+");
    if (commandeLog == 0)
    {
        printf("Erreur de création du fichier");
    }
    else
    {
        fprintf(commandeLog,"%s", cmd);
    }
    fclose(commandeLog);
    return commandeLog;
}

char** afficher_fichier(FILE *f,char** argv)
{
    f=fopen("commande.txt", "r");
    if (f != NULL)
    {
        char ch;
        while ((ch=fgetc(f))!= EOF)
        {
            printf ("%c",ch);
            **argv=ch;
        }
        printf("\n");
    }
    return argv;
}

void  exec(char **argv)
{
     pid_t  pid;

     if ((pid=fork()) == 0)
     {
          if (execvp(*argv, argv) < 0) 
          {
               printf("*** ERREUR: exec echoué\n");
               exit(1);
          }
     }
     wait(NULL);
}

int  main()
{
    char  cmd[MAX];
    FILE* f=0;
    char** argv=0;
    printf("Entrer -> ");
    fgets(cmd,MAX,stdin);
    int i = strlen(cmd)-1;
    if( cmd[ i ] == '\n') 
        cmd[i] = '\0';
    printf("\n");
    f=ouvrir(cmd);
    afficher_fichier(f,argv);
    exec(argv);
    return 0;
}

编译代码并运行后,它返回“段错误(核心转储)”消息。问题基本上出在“afficher_fichier”函数中。

我写的代码有逻辑吗?

如果是这样,我该如何纠正错误?

如果没有,我如何实现我寻求的目标?

最佳答案

第一遍

使函数ouvrir()返回一个FILE *;只传递FILE *,而不是FILE。不保证该结构的副本能够正常“工作”。 (C 标准 ISO/IEC 9899:2011 §7.21.3 Files 规定:用于控制流的 FILE 对象的地址可能很重要;的副本 FILE 对象不需要代替原始对象。)但是,令人费解的是您不使用返回值。

FILE *ouvrir(char *cmd)
{
    FILE *commandeLog = fopen("commande.log", "a");
    if (commandeLog == 0)
    {
        …report error…
    }
    else
    {
        fprintf(commandeLog,"%s", cmd);
    }
    return commandeLog;
}

然后,在main()中:

FILE *f = 0;    // As now

…

f = ouvrir(cmd);
if (f == 0)
{
    …whatever you need to do that uses f…
    fclose(f);
}

如果您稍后要读取该文件而不重新打开它,则需要使用 "a+" 或类似模式("w+""r+" — 这取决于 ouvrir() 是否必须创建文件,或者应该使用现有文件,以及所有写入是否必须无条件地位于文件末尾) 。或者,您可以保持 ouvrir() 不变,并安排关闭文件并重新打开它以供读取(也许是一个新函数,reouvrir())。

第二遍

问题更新后,afficher_fichier() 文件导致了大部分问题。目前是:

char** afficher_fichier(FILE *f,char** argv)
{
    f=fopen("commande.txt", "r");
    if (f != NULL)
    {
        char ch;
        while ((ch=fgetc(f))!= EOF)
        {
            printf ("%c",ch);
            **argv=ch;
        }
        printf("\n");
    }
    return argv;
}

问题包括:

  1. 如果您要做的第一件事是使用 fopen() 用新文件流覆盖它,为什么要传入 f 呢?它应该只是一个局部变量,而不是一个参数。
  2. 为什么要在返回时传入 argv
  3. 传入的argv是一个空指针。忽略这一点并没有多大意义;您可以简单地在函数中分配空间。
  4. 您为什么要阅读该文件?您知道它包含 main()cmd 中的值,那么为什么不简单地将其传递给 afficher_fichier() 呢?
  5. 如果你传入cmd,你必须决定函数是否可以修改字符串。如果没有,您需要在找到参数时复制它们。
  6. 使用编写的代码,您只需将所有字符复制到第零个参数中的同一字符上,无需拆分(例如在空格上)。
  7. 没有检查溢出,但由于内存没有分配,所以实际上没有缓冲区可以溢出,而且由于它总是写入同一个位置,所以也许这并不重要。
  8. 您不能以 null 终止字符串。
  9. 您的函数传递了一个空指针;这不会带来幸福。
  10. 返回空指针并没有多大帮助。
  11. 因为您在函数中打开文件但没有关闭它,所以会泄漏文件流。
  12. ...这个列表可能会继续,但可能效率不高...

exec 函数状态良好。如果将错误消息打印到 stderr 而不是 stdout 会更好,但除此之外,它也可以 - 假设它获得有效的 argv > 传递给它的字符串数组。

做出简化假设:

  • cmd 传递给 afficher_fichier()
  • cmd可以修改
  • 用空格分隔参数
  • 该函数不需要重命名 - 即使其当前名称不再合适

您最终可能会得到如下代码:

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

static
char **afficher_fichier(char *cmd)
{
    printf("-->> [%s]\n", cmd);
    size_t argx = 16;
    size_t argc = 0;
    char **argv = malloc(sizeof(*argv) * argx);
    if (argv == 0)
        return 0;

    char *src = cmd;
    while (*src != '\0')
    {
        while (isspace((unsigned char)*src))
            src++;
        if (*src == '\0')
        {
            printf("<<-- 1\n");
            if (argc == 0)
            {
                free(argv);
                argv = 0;
            }
            else
                argv[argc] = 0;
            return argv;
        }
        if (argc >= argx - 1)
        {
            size_t narg = argx * 2;
            char **argn = realloc(argv, sizeof(*argv) * narg);
            if (argn == 0)
            {
                fprintf(stderr, "Memory allocation failed\n");
                free(argv);
                return 0;
            }
            argv = argn;
            argx = narg;
        }
        argv[argc++] = src;
        while (!isspace((unsigned char)*src))
            src++;
        if (*src != '\0')
            *src++ = '\0';
        printf("%zu = [%s]\n", argc, argv[argc-1]);
    }
    *argv[argc] = 0;
    printf("<<-- 2\n");

    return argv;
}

int main(void)
{
    char cmd[] = "  \tcmd   arg1     arg2 arg3   ";
    char **argv = afficher_fichier(cmd);
    if (argv != 0)
    {
        char **arg = argv;
        while (*arg != 0)
            printf("[%s]\n", *arg++);
    }
    else
        printf("Bother!\n");
    return 0;
}

示例输出:

-->> [      cmd   arg1     arg2 arg3   ]
1 = [cmd]
2 = [arg1]
3 = [arg2]
4 = [arg3]
<<-- 1
[cmd]
[arg1]
[arg2]
[arg3]

带有双头箭头的printf()语句是诊断打印;它们会从生产代码中被省略(或者如果设置了某种调试标志,则将其制作成有条件打印的代码 - 有关更多想法,请参阅 C #define macro for debug printing)。

关于c - 从文件(shell)执行命令,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40295689/

相关文章:

c++ - 为什么 vector 会导致 Segmentation fault 错误?

c - 初始化结构段错误

c - 在同一个程序中使用 scanf 和 fgets?

c - 那么 "return 0"到底是什么意思呢?

java - 如何只从文件中读取相关信息而忽略其他所有信息?

c++ - eof() 两次返回最后一个字符

Linux:如何调试 SIGSEGV?如何追溯错误来源?

c - 如何在C中将arp地址添加到/proc/net/arp

C 取消引用 void* 指针

c - 如何在C中将阻塞文件io转换为非阻塞文件