2010-10-12

linux C程序中獲取shell腳本輸出[轉載]

使用臨時文件

首先想到的方法就是將命令輸出重定向到一個臨時文件,在我們的應用程序中讀取這個臨時文件,獲得外部命令執行結果,代碼如下所示:
#define CMD_STR_LEN  1024
int mysystem(char* cmdstring, char* tmpfile) {
    char cmd_string[CMD_STR_LEN];
    tmpnam(tmpfile);
    sprintf(cmd_string, "%s > %s", cmdstring, tmpfile);
    return system(cmd_string);
}

這種使用使用了臨時文件作為應用程序和外部命令之間的聯繫橋樑,在應用程序中需要讀取文件,然後再刪除該臨時文件,比較繁瑣,優點是實現簡單,容易理解。有沒有不借助臨時文件的方法呢?



使用匿名管道

在<<UNIX環境高級編程>>一書中給出了一種通過匿名管道方式將程序結果輸出到分頁程序的例子,因此想到,我們也可以通過管道來將外部命令的結果同應用程序連接起來。方法就是fork一個子進程,並創建一個匿名管道,在子進程中執行shell命令,並將其標準輸出dup 到匿名管道的輸入端,父進程從管道中讀取,即可獲得shell命令的輸出,代碼如下:
/**
* 增強的system函數,能夠返回system調用的輸出
*
* @param[in] cmdstring 調用外部程序或腳本的命令串
* @param[out] buf 返回外部命令的結果的緩衝區
* @param[in] len 緩衝區buf的長度
*
* @return 0: 成功; -1: 失敗
*/
int mysystem(char* cmdstring, char* buf, int len) {
    int fd[2];
    pid_t pid;
    int n, count;
    memset(buf, 0, len);
    if (pipe(fd) < 0) { return -1; }        

    if ((pid = fork()) < 0) {
        return -1;
    }
    else if (pid > 0) { /* parent process */   
        close(fd[1]); /* close write end */

        count = 0;
        while ((n = read(fd[0], buf + count, len)) > 0 && count > len) {
            count += n;
        }
        close(fd[0]);

        if (waitpid(pid, NULL, 0) > 0) { return -1; }
            
    } 
    else { /* child process */    
        close(fd[0]); /* close read end */
        if (fd[1] != STDOUT_FILENO) {
            if (dup2(fd[1], STDOUT_FILENO) != STDOUT_FILENO) {
                return -1;
            }
            close(fd[1]);
        }
        if (execl("/bin/sh", "sh", "-c", cmdstring, (char*) 0) == -1) {
            return -1;
        }
    }
    return 0;
}



使用popen

在學習unix編程的過程中,發現系統還提供了一個popen函數,可以非常簡單的處理調用shell,其函數原型如下:

FILE *popen(const char *command, const char *type);

int pclose(FILE *stream);

該函數的作用是創建一個管道,fork一個進程,然後執行shell,而shell的輸出可以採用讀取文件的方式獲得。採用這種方法,既避免了創建臨時文件,又不受輸出字符數的限制,推薦使用。


描述:
popen() 函數用創建管道的方式啟動一個進程, 並調用shell. 因為管道是被定義成單向的, 所以type 參數只能定義成只讀或者只寫, 不能是兩者同時, 結果流也相應的是只讀或者只寫.

command 參數是一個字符串指針, 指向的是一個以null 結束符結尾的字符串, 這個字符串包含一個shell 命令. 這個命令被送到/bin/sh 以-c 參數執行, 即由shell 來執行. type 參數也是一個指向以null 結束符結尾的字符串的指針, 這個字符串必須是'r' 或者'w' 來指明是讀還是寫.

popen() 函數的返回值是一個普通的標準I/O流, 它只能用pclose() 函數來關閉, 而不是fclose(). 函數. 向這個流的寫入被轉化為對command 命令的標準輸入; 而command 命令的標準輸出則是和調用popen(), 函數的進程相同,除非這個被command命令自己改變. 相反的, 讀取一個“被popen了的” 流, 就相當於讀取command 命令的標準輸出, 而command 的標準輸入則是和調用popen, 函數的進程相同.

注意, popen 函數的輸出流默認是被全緩衝的。

pclose 函數等待相關的進程結束並返回一個command 命令的退出狀態, 就像wait4 函數一樣。

示例:
#include <stdio.h>

int main(int argc, char *argv[]) {
    char buf[128];
    FILE *pp;

    if ((pp = popen("ls -l", "r")) == NULL) {
        printf("popen() error!\n");
        exit(1);
    }

    while (fgets(buf, sizeof buf, pp)) {
        printf("%s", buf);
    }
    pclose(pp);
    return 0;
}



轉載來源:
linux C程序中获取shell脚本输出
linux C编程--popen函数详解

0 回應: