如何在C中将变量传递给shell命令?

时间:2015-05-22 21:08:50

标签: c linux shell

例如,我代码:

fp = popen("wc -l < myfile", "r");

myfile应该是解析为此项目的任何文件名。它可以是文件abc.txt123.txtxy.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的值。

4 个答案:

答案 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所持有的字符串的长度,加上强制字符加上空终止符来选择临时数组长度。