例如,我代码:
fp = popen("wc -l < myfile", "r");
但myfile
应该是解析为此项目的任何文件名。它可以是文件abc.txt
或123.txt
或xy.txt
等。
然后我想获得执行此wc -l < myfile
的输出。但问题是我不知道C中的哪个函数可以帮助我解析{shell}命令的myfile
的名称,我也可以得到输出。
有人能给我一些建议吗?
编辑:
我想读的文件非常大。我想将其数据读入数组。我不能使用list来存储它,因为在列表中找到特定数据太慢。问题是,如果我将一维数组用于数组的malloc()
内存空间,笔记本电脑上就没有足够的连续内存空间。因此,我计划使用二维数组来存储它。因此,我必须获取文件中的行数,然后通过log
确定此数组中每个维的大小。
感谢所有答案。这个项目是关于阅读两个文件。第一个文件比第二个文件大得多。第二个文件是:
1 13 0
2 414 1
3 10 0
4 223 1
5 2 0
每行中的第三个数字称为“ID”。例如,num“1”具有ID 0,num“2”具有ID 1,num“3”具有ID“0”。 (忽略每行中间的数字) 第一个文件就像:
1 1217907
1 1217908
1 1517737
1 2
2 3
2 4
3 5
3 6
如果第一个文件中的每个num都有ID“0”,我应该将每行中的num都存储到数据结构数组中。例如,我们可以看到num“1”在第二个文件中有ID“0”,所以我需要存储:
1 1217907
1 1217908
1 1517737
1 2
从我的第一个文件进入数据结构数组。 num“2”的ID为“1”,但num“3”的ID为“0”而num“4”的ID为“1”,因此需要存储:2 3
但不能存储2 4
我的第一个档案。这就是我需要使用数组来存储这两个文件的原因。如果我使用两个数组来存储它们,我可以检查这个num的ID是否在数组中快速为“0”属于第二个文件,因为使用数组快速定位特定数据,索引可以直接为num的值。
答案 0 :(得分:2)
忘记popen
- 自己动手
即
FILE *f = fopen(argv[1], "r");
int lines = 0;
int ch;
while ((ch = fgetc(f)) != EOF) {
if (c == '\n') lines++;
}
编辑 - 海报希望将整个文件加载到内存中
添加错误检查
FILE *f = fopen(argv[1], "r");
struct stat size;
fstat(fileno(f), &size);
char buf = malloc(size.st_size)
fread(buf, size.st_size, 1, f);
fclose(f);
答案 1 :(得分:2)
如果您不打算自己(没有shell),你应该这样做,至少传递文件名,使shell只会将其解释为数据而不是代码,以避免安全性事件
setenv("filename", "myfile"); /* put filename in the environment */
fp = popen("wc -l <\"$filename\"", "r"); /* check it from your shell script */
答案 2 :(得分:2)
以下所有代码均未经过测试。如果我有时间进行测试,我将删除此警告。
您可以创建自己的popen()
包装器,以允许您形成任意命令。
FILE * my_popen (const char *mode, const char *fmt, ...) {
va_list ap;
int result = 511;
for (;;) {
char buf[result+1];
va_start(ap, fmt);
result = vsnprintf(buf, sizeof(buf), fmt, ap);
va_end(ap);
if (result < 0) return NULL;
if (result < sizeof(buf)) return popen(buf, mode);
}
/* NOT REACHED */
return NULL;
}
然后,您可以这样称呼它:
const char *filename = get_filename_from_input();
FILE *fp = my_popen("r", "%s < %s", "wc -l", filename);
if (fp) {
/* ... */
pclose(fp); /* make sure to call pclose() when you are done */
}
在这里,我们假设get_filename_from_input()
将文件名输入字符串转换为可供shell使用的安全字符串。
将shell文件安全地处理的内容可靠地修复,这是相当复杂的(并且容易出错)。自己打开文件更安全。但是,执行此操作后,您可以将文件提供给命令,然后读出结果输出。问题是,您不能使用popen()
来完成此任务,因为标准popen()
仅支持单向通信。†
†<子> Some variations of
popen()
exist that support bidirectional communication. 子>
FILE * my_cmd_open (const char *cmd) {
int s[2], p, status, e;
if (socketpair(AF_UNIX, SOCK_STREAM, 0, s) < 0) return NULL;
switch (p = fork()) {
case -1: e = errno; close(s[0]); close(s[1]); errno = e; return NULL;
case 0: close(s[0]); dup2(s[1], 0); dup2(s[1], 1); dup2(s[1], 2);
switch (fork()) {
case -1: exit(EXIT_FAILURE);
case 0: execl("/bin/sh", "-sh", "-c", cmd, (void *)NULL);
exit(EXIT_FAILURE);
default: exit(0);
}
default: for (;;) {
if (waitpid(p, &status, 0) < 0 && errno == EINTR) continue;
if (WIFEXITED(status) && WEXITSTATUS(status) == 0) break;
close(s[0]); close(s[1]); errno = EPIPE;
return NULL;
}
}
close(s[1]);
return fdopen(s[0], "r+");
}
要有效地将整个文件读入内存,您可以使用mmap()
。
void * mmap_filename (const char *filename, size_t *sz) {
int fd = open(filename, O_RDONLY);
if (fd < 0) return NULL;
struct stat st;
if (fstat(fd, &st) < 0) {
close(fd);
return NULL;
}
*sz = st.st_size;
void *data = mmap(NULL, *sz, PROT_READ, MAP_PRIVATE, fd, 0);
close(fd);
return data != MAP_FAILED ? data : NULL;
}
然后,您可以这样称呼它:
size_t sz;
void *data = mmap_filename(filename, &sz);
if (data) {
/* ... */
munmap(data, sz);
}
上面的示例代码一次映射整个文件。但是,mmap()
API允许您将文件的某些部分从特定偏移映射到文件中。
答案 3 :(得分:1)
我认为,您需要先使用snprintf()
生成要传递给popen()
的字符串,然后使用该字符串调用popen()
。
伪代码
char buf[32] = {0};
snprintf(buf, 32, "wc -l < %s", myfile);
fp = popen(buf, "r");
修改强>
使其适用于任何长度的myfile
int len = strlen(myfile) + strlen("wc -l < ") + 1;
char *buf = malloc(len);
snprintf(buf, len, "wc -l < %s", myfile);
fp = popen(buf, "r");
...
free(buf);
注意:正如Ed Heal in the comment所述,32
此处仅用于演示目的。您应该根据myfile
所持有的字符串的长度,加上强制字符加上空终止符来选择临时数组长度。