我正在尝试制作一个充当迷你外壳的程序(它执行在终端中编写的命令)。为此,我尝试测试此方法的工作原理,我创建了一个文件,将在终端中解析的参数放入其中,读取该文件,然后将其解析为数组以使用 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;
}
问题包括:
- 如果您要做的第一件事是使用
fopen()
用新文件流覆盖它,为什么要传入f
呢?它应该只是一个局部变量,而不是一个参数。 - 为什么要在返回时传入
argv
? - 传入的
argv
是一个空指针。忽略这一点并没有多大意义;您可以简单地在函数中分配空间。 - 您为什么要阅读该文件?您知道它包含
main()
中cmd
中的值,那么为什么不简单地将其传递给afficher_fichier()
呢? - 如果你传入
cmd
,你必须决定函数是否可以修改字符串。如果没有,您需要在找到参数时复制它们。 - 使用编写的代码,您只需将所有字符复制到第零个参数中的同一字符上,无需拆分(例如在空格上)。
- 没有检查溢出,但由于内存没有分配,所以实际上没有缓冲区可以溢出,而且由于它总是写入同一个位置,所以也许这并不重要。
- 您不能以 null 终止字符串。
- 您的函数传递了一个空指针;这不会带来幸福。
- 返回空指针并没有多大帮助。
- 因为您在函数中打开文件但没有关闭它,所以会泄漏文件流。
- ...这个列表可能会继续,但可能效率不高...
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/