我正在创建一个程序,您可以通过该程序执行终端命令。我想访问一个目录(使用 cd /Users/user/Desktop),但由于 scanf 在空格处终止,我被迫将终止值更改为[^\n]. 这时候错误就出现了。每当我输入命令时,它都会执行该命令,但是下次程序进入(无限)循环时,它不会停止执行 scanf 函数之前的行。当终止值为 时,这种情况不会发生%s。这是该程序的代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void execute(){
char* command = (char *) malloc(15);
char* output = (char *) malloc(4096);
printf(">> ");
scanf("%[^\n]", command); //Here is the scanf function
FILE* cmd = popen(command, "r");
fread(output, sizeof(output), 32000, cmd);
if (strlen(output) != 0){
printf("\n%s\n", output);
}
free(output);
pclose(cmd);
}
int main(){
while (1){
execute();
}
}
Run Code Online (Sandbox Code Playgroud)
这是终止值为 时的输出[^\n]:
>> ls
Applications
Desktop
Documents
//Here the rest of the contents in my user folder appear (twice for some reason, that's also an issue related to this).
>> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >>
>> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >>
>> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >>
>> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >>
// This then goes on forever
Run Code Online (Sandbox Code Playgroud)
这是终止值为时的输出%s:
>> ls
Applications
Desktop
Documents
//Here the rest of the contents in my user folder appear (once)
>> //Here I can input things again
Run Code Online (Sandbox Code Playgroud)
有人可以告诉我如何解决这个问题吗?(我尝试过gets(),结果相同)
由于您告诉scanf不要读取\n,因此它将其保留在标准输入中,因此当循环重复时,它仍然存在并导致下一次迭代立即返回空字符串。要解决这个问题,有几个选择:
"%[^\n]"为" %[^\n]"忽略前导空格。getchar();后scanf("%[^\n]", command);以消耗换行符。fgets或 以外的一些其他读取功能scanf。(不gets;它不能安全使用!)您也没有检查是否scanf返回了任何内容,因此如果它根本无法解析任何内容,那么您会将未初始化的内存传递给popen.
旁注:您的程序中还有很多其他错误,但它们与您当前的问题无关:
command您通过循环泄漏了每次的内存。要修复此问题,请在函数末尾之后的free(command);某个位置添加。popenscanf,因此如果输入超过 14 个字符,则会出现缓冲区溢出和内存损坏。要修复此问题,请更改%[^\n]为%14[^\n](14 而不是 15,这样就有空终止符的空间)。您还应该检测部分读取的情况并正确处理它,以避免echo Their alarm is set删除名为is和 的文件set。sizeof(output)将是指针的大小,而不是您分配的它指向的内存的大小,并且32000似乎是凭空而来的。更改sizeof(output)为 132000和4096。fread不会以空终止其输出,因此打印它%s会在其后打印未初始化的内存。要修复此问题,请使用除fread获取输出之外的其他内容,或使用其返回值并确保只打印那么多字符。使用您的“固定”版本,仍然存在一些问题:
fgets现在不会缓冲区溢出,但过长的字符串仍然会导致问题。特别是,如果用户输入的命令太长,就会像在中间按下 Enter 一样,导致运行两个部分命令。cd操作不会影响父流程或任何其他子流程。为了使其工作,您需要检查他们输入的命令是否以 开头cd,如果是,则chdir直接调用而不是执行popen。这是一种有效的方法:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
int main(){
char *command = NULL;
size_t commandsize = 0;
char *output = (char *)malloc(5000);
while(1){
printf(">> ");
ssize_t commandlen = getline(&command, &commandsize, stdin);
if(commandlen < 0) {
// assume EOF. Small chance that it was an error though
break;
}
printf("%s", command);
if(!strcmp(command, "exit\n")) {
break;
}
if(!strncmp(command, "cd ", 3)) {
command[commandlen - 1] = '\0'; // remove the newline
if(chdir(command + 3)) {
perror("chdir");
}
continue;
}
FILE *cmd = popen(command, "r");
if(!cmd) {
perror("popen");
continue;
}
size_t sz;
while ((sz = fread(output, 1, 5000, cmd)) > 0){
fwrite(output, 1, sz, stdout);
}
pclose(cmd);
}
free(command);
free(output);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
关于它的一些注意事项:
mallocand ing 东西,而是将这些变量移至函数作用域,这样它只在程序运行的整个过程中发生一次。freegetline读取整行,并自动分配所需的空间。这与 一样popen,不是标准 C 的一部分,而是 POSIX 的一部分。fread和fwrite来传输我们开始的进程中的数据,以避免必须考虑空终止符。我假设您最终会对这些数据进行某种处理;如果没有,请考虑使用system代替popen,它会自动将输出写回给用户。