我正在创建一个程序,您可以通过该程序执行终端命令。我想访问一个目录(使用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();
以使用换行符 - 使用
fgets
或scanf
以外的其他读取功能。(不是gets
;它不能安全使用!(
您也没有检查scanf
是否返回了任何内容,所以如果它根本无法解析任何内容,那么您将把未初始化的内存传递给popen
。
附带说明:你的程序中还有很多其他错误,但它们与你眼前的问题无关:
- 每次通过循环都会泄漏
command
的内存。要修复它,请在popen
之后、函数结束之前的某个位置添加free(command);
- 您没有将最大字段宽度传递给
scanf
,因此,如果您输入的字符超过14个,就会出现缓冲区溢出和内存损坏。要修复它,请将%[^n]
更改为%14[^n]
(14而不是15,这样就有空终止符的空间(。您还应该检测部分读取的情况并正确处理,以避免echo Their alarm is set
删除名为is
和set
的文件 sizeof(output)
将是指针的大小,而不是它所指向的您分配的内存的大小,32000
似乎是凭空产生的。将sizeof(output)
更改为1,将32000
更改为4096
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;
}
关于它的几个注意事项:
- 我没有每次循环都使用
malloc
和free
,而是将这些变量移动到函数范围,这样在程序运行的整个过程中只发生一次 - 我使用
getline
读取整行,并自动分配所需的空间。这与popen
一样,不是标准C的一部分,而是POSIX的一部分 - 我使用
fread
和fwrite
从我们开始的过程中穿梭数据,以避免考虑空终止符。我想你最终会对这些数据进行某种处理;如果没有,请考虑使用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;
}