c - (C) 当终止值为 [^\n] 时 scanf 不工作
问题描述
我正在创建一个程序,您可以在其中通过该程序执行终端命令。我想访问一个目录(使用 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();
}
}
这是终止值为 时的输出[^\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
,它会将其留在标准输入中,因此当循环重复时,它仍然存在并导致下一次迭代立即返回一个空字符串。要修复它,有几个选择:
- 更改
"%[^\n]"
为" %[^\n]"
忽略前导空格。 - 添加
getchar();
afterscanf("%[^\n]", command);
以使用换行符。 - 使用
fgets
或 以外的其他阅读功能scanf
。(不gets
;它不能安全使用!)
您也没有检查是否scanf
返回任何内容,因此如果它根本无法解析任何内容,那么您将未初始化的内存传递给popen
.
旁注:您的程序中还有很多其他错误,但它们与您的直接问题无关:
- 您通过循环泄漏了
command
每次的内存。要修复它,请在函数结束之前和之后free(command);
的某个位置添加。popen
- 您没有将最大字段宽度传递给
scanf
,因此如果输入的字符数超过 14 个,则会出现缓冲区溢出和损坏的内存。要修复它,请更改%[^\n]
为%14[^\n]
(14 而不是 15,以便为空终止符留出空间)。您还应该检测部分读取的情况并正确处理,以避免echo Their alarm is set
删除名为is
andset
的文件。 sizeof(output)
将是指针的大小,而不是您分配的它指向的内存的大小,并且32000
似乎是凭空出现的。更改为sizeof(output)
1 和。32000
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;
}
关于它的几点说明:
- 我没有每次通过循环都使用
malloc
and ,而是将这些变量移到函数范围,因此它只在程序运行的整个过程中发生一次。free
- 我习惯
getline
阅读整行,并自动分配必要的空间。这和 一样popen
,不是标准 C 的一部分,而是 POSIX 的一部分。 - 我使用
fread
andfwrite
从我们开始的流程中传输数据,以避免不得不考虑空终止符。我假设您最终会对这些数据进行某种处理;如果没有,请考虑使用system
而不是popen
,它会自动将输出写回给用户。
推荐阅读
- java - 如何从坐标中获取文本属性?
- c++ - 为什么我的阵列不工作以及如何修复它
- javascript - 为什么一个值在 x 中增加而不在找到的对象中?
- python - Leetcode 90. TypeError: unhashable type: 'list'
- elasticsearch - 在 Elasticsearch 的两个不同字段中匹配相同令牌时的惩罚
- java - 在不使用 iamge 缩放的图像上绘图
- python - 最近邻分类器的 Python 属性错误
- scala - Spark Streaming 中的分区是如何工作的?
- python - 尝试为 Google Assistant SDK 生成凭据时出现回溯错误
- asp.net-core - 如何通过额外数据将 UserInfo 传递给 DBContext 实例?