簡體   English   中英

如何在C中將變量傳遞給shell命令?

[英]How to pass variable to shell command in C?

例如,我編碼:

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

但是myfile應該是解析到該項目的任何文件名。 它可以是文件abc.txt123.txtxy.txt等。

然后,我想獲得執行此wc -l < myfile的輸出。 但是問題是我不知道C中的哪個函數可以幫助我將myfile的名稱解析為該shell命令,並且我也可以獲取輸出。 誰能給我一些建議?

編輯:我要讀取的文件很大。 我想將其數據讀取到數組中。我無法使用列表來存儲它,因為在列表中定位特定數據太慢了。 問題是,如果我使用一維數組將malloc()內存空間分配給該數組,則筆記本電腦上將沒有足夠的連續內存空間。 因此,我計划使用二維數組來存儲它。 因此,我必須獲取文件中的行數,然后通過log確定此數組中每個維的大小。

感謝所有答案。 這個項目是關於讀取兩個文件的。 第一個文件比第二個文件大得多。 第二個文件是這樣的:

1   13  0
2   414 1
3   10  0
4   223 1
5   2   0

每行中的第三個數字稱為“ ID”。 例如,數字“ 1”的ID為0,數字“ 2”的ID為1,數字“ 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

從我的第一個文件進入數據結構數組。 數字“ 2”的ID為“ 1”,但是數字“ 3”的ID為“ 0”,數字“ 4”的ID為“ 1”,因此需要存儲: 2 3但不存儲我的第一個文件中的2 4 這就是為什么我需要使用數組來存儲兩個文件的原因。 如果我使用兩個數組來存儲它們,我可以檢查數組中該數字的ID是否為“ 0”,快速屬於第二個文件,因為使用數組快速查找特定數據,索引可以直接為num的值。

忘記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);

如果您不打算自己(沒有外殼)進行此操作,則應至少以這樣的方式傳遞文件名:外殼將僅將其解釋為數據而不是代碼,以避免潛在的安全事故。

setenv("filename", "myfile");            /* put filename in the environment */
fp = popen("wc -l <\"$filename\"", "r"); /* check it from your shell script */

以下所有代碼均未經測試。 如果我有時間測試,我將消除此警告。

您可以為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()將文件名輸入字符串轉換為安全的外殼供使用。


可靠地將文件名固定到外殼程序可以安全處理的文件中是相當復雜的(並且容易出錯)。 自己打開文件更安全。 但是,這樣做之后,您可以將文件輸入命令,然后讀出結果輸出。 問題是,您不能使用popen()完成此操作,因為標准popen()僅支持單向通信。

存在一些popen()變體,它們支持雙向通信。

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允許您將文件的一部分從特定偏移量映射到文件中。

我認為,您需要利用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在評論中所述 ,此處的32僅用於演示目的。 顯然,您應該根據myfile持有的字符串的長度,必需的字符以及空終止符來選擇臨時數組的長度。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM