(C)当终止值为[^\n]时,扫描不起作用



我正在创建一个程序,您可以通过该程序执行终端命令。我想访问一个目录(使用cd/Users/user/DDesktop(,但由于scanf终止于空白处,我不得不将terminate值更改为[^n]。这时出现了错误。每当我输入命令时,它都会执行所述命令,但下次程序通过(无限(循环时,它不会停止执行scanf函数之前的行。当terminate值为%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%sn", output);
}
free(output);
pclose(cmd);
}
int main(){
while (1){
execute();
}
}

以下是终止值为[^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

这里是当终止值为%s:时的输出

>> ls
Applications
Desktop
Documents
//Here the rest of the contents in my user folder appear (once)
>> //Here I can input things again

有人能告诉我怎么修吗?(我尝试了gets(),结果相同(

由于您告诉scanf不要读取n,它将把它留在stdin中,因此当循环重复时,它仍然存在,并导致下一次迭代立即返回一个空字符串。要修复它,有几个选择:

  • "%[^n]"更改为" %[^n]"以忽略前导空格
  • scanf("%[^n]", command);之后添加getchar();以使用换行符
  • 使用fgetsscanf以外的其他读取功能。(不是gets;它不能安全使用!(

您也没有检查scanf是否返回了任何内容,所以如果它根本无法解析任何内容,那么您将把未初始化的内存传递给popen

附带说明:你的程序中还有很多其他错误,但它们与你眼前的问题无关:

  1. 每次通过循环都会泄漏command的内存。要修复它,请在popen之后、函数结束之前的某个位置添加free(command);
  2. 您没有将最大字段宽度传递给scanf,因此,如果您输入的字符超过14个,就会出现缓冲区溢出和内存损坏。要修复它,请将%[^n]更改为%14[^n](14而不是15,这样就有空终止符的空间(。您还应该检测部分读取的情况并正确处理,以避免echo Their alarm is set删除名为isset的文件
  3. sizeof(output)将是指针的大小,而不是它所指向的您分配的内存的大小,32000似乎是凭空产生的。将sizeof(output)更改为1,将32000更改为4096
  4. fread不会为null终止其输出,因此用%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, "exitn")) {
break;
}
if(!strncmp(command, "cd ", 3)) {
command[commandlen - 1] = ''; // 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;
}

关于它的几个注意事项:

  • 我没有每次循环都使用mallocfree,而是将这些变量移动到函数范围,这样在程序运行的整个过程中只发生一次
  • 我使用getline读取整行,并自动分配所需的空间。这与popen一样,不是标准C的一部分,而是POSIX的一部分
  • 我使用freadfwrite从我们开始的过程中穿梭数据,以避免考虑空终止符。我想你最终会对这些数据进行某种处理;如果没有,请考虑使用system而不是popen,后者将自动将输出写回用户

这是固定的代码(除了cd问题((另一个用户发布了代码的最终版本。他们的答案已标记为正确答案(忽略以下代码,它不再相关

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(){
while(1){
char* command = (char *) malloc(30);
char* output = (char *)  malloc(5000);
printf(">> ");
fgets(command, 30, stdin);
printf("%s", command);
FILE *cmd = popen(command, "r");
while (fgets(output, 5000, cmd) != NULL){
printf("%s", output);
}
pclose(cmd);
free(command);
free(output);
}
return 0;
}

最新更新