實現system函數功能(shell命令執行狀況判斷)

以前寫Linux應用程序的時候,最喜歡使用system命令了,後來發現這個命令使用須要很謹慎。以前使用該命令來進行MD5校驗,經過返回值來判斷校驗是否成功不夠嚴謹。有時候由於system調用MD5sum文件不存在致使的錯誤,應用並不可以直觀發現。反而一直在md5校驗碼上花費太多心思。因而打算重寫一下system函數來玩玩。html

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#define BUF_SIZE 100
/******************************************************************* ** 函數名: my_system ** 函數描述: shell命令執行判斷,保存執行結果。 ** 參數: cmd: shell命令;result: shell命令執行結果 ** 返回: shell命令執行失敗返回-1;執行成功返回shell命令結束碼 ********************************************************************/
int my_system(char *cmd, char *result) {
    int rc = 0;
    int ret = -1;
    FILE *fp = NULL;

    strcat(cmd, " 2>&1");
    fp = popen(cmd, "r");
    if (NULL == fp) {
        perror("popen error\n");
        goto EXIT;
    }
    while (NULL != fgets(result, BUF_SIZE, fp)) {
    	/* 去除換行符 */
        if ('\n' == result[strlen(result)-1]) {
            result[strlen(result)-1] = '\0';
        }
    }
    rc = pclose(fp);
    if (-1 == rc) {
        perror("pclose error\n");
        goto EXIT;
    }
    printf("子進程結束狀態 = %d\n", rc);
    if (!WIFEXITED(rc)) {
        perror("Run command failed\n");
        goto EXIT;
    } else {
        ret = WEXITSTATUS(rc);
    }
EXIT:
    if (NULL == fp || -1 == rc) {
        strncpy(result, strerror(errno), BUF_SIZE);
        //printf("errno = %s\n", strerror(errno));
    }
    fp = NULL;
    return ret;
}
void main() {
    char cmd[BUF_SIZE] = {0};
    char result[BUF_SIZE] = {0};
    int cmd_ret;

    snprintf(cmd, BUF_SIZE, "diff a b");
    cmd_ret = my_system(cmd, result);
    printf("命令返回值 = %d\n", cmd_ret);
    if (0 != cmd_ret) {
        printf("failed reason :[%s]\n", result);
    } else {
        printf("success\n");
    }
}
複製代碼

運行結果:linux

子進程結束狀態 = 512
命令返回值 = 2
failed reason :[diff: a: No such file or directory]
複製代碼

代碼知識點解讀

1、2>&1 | tee

一、2>&1是什麼意思? 0 stdin,1 stdout,2 stderr 標準輸入 -》 鍵盤 標準輸出 -》 屏幕shell

2>&1應該分紅兩個部分來看:緩存

  • 2>做用將標準出錯重定向到某個特定的地方;
  • &1指不管標準輸出在哪裏。

2>&1的意思就是說不管標準出錯在哪裏(哪怕是沒有),都將標準出錯重定向到標準輸出中。函數

二、 | 管道符   管道的做用是提供一個通道,將上一個程序的標準輸出重定向到下一個程序做爲下一個程序的標準輸入。 一般使用管道的好處是一方面形式上簡單,另外一方面其執行效率要遠高於使用臨時文件。測試

三、tee的做用   tee從標準輸入中讀取,並將讀入的內容寫到標準輸出以及文件中,配合管道符使用。   make kernel 2>&1 | tee -a kernel.log (- a表示追加到文件末尾) 標準輸出的緩存有限制,因編譯內核產生的log信息不少,所以經過tee來保存到文件中。ui

2、popen

一、函數說明   popen()函數經過建立一個管道,調用fork()產生一個子進程,執行一個shell以運行命令來開啓一個進程。這個管道必須由pclose()函數關閉,而不是fclose()函數。pclose()函數關閉標準I/O流,等待命令執行結束,而後返回shell的終止狀態。若是shell不能被執行,則pclose()返回的終止狀態與shell已執行exit同樣。spa

  type參數只能是讀或者寫中的一種,獲得的返回值(標準I/O流)也具備和type相應的只讀或只寫類型。若是type是"r"則文件指針鏈接到command的標準輸出;若是type是"w"則文件指針鏈接到command的標準輸入。.net

  command參數是一個指向以NULL結束的shell命令字符串的指針。這行命令將被傳到bin/sh並使用-c標誌,shell將執行這個命令。指針

  popen()的返回值是個標準I/O流,必須由pclose來終止。前面提到這個流是單向的(只能用於讀或寫)。向這個流寫內容至關於寫入該命令的標準輸入,命令的標準輸出和調用popen()的進程相同;與之相反的,從流中讀數據至關於讀取命令的標準輸出,命令的標準輸入和調用popen()的進程相同。

二、返回值   若是調用fork()或pipe()失敗,或者不能分配內存將返回NULL,不然返回標準I/O流。popen()沒有爲內存分配失敗設置errno值。若是調用fork()或pipe()時出現錯誤,errno被設爲相應的錯誤類型。若是type參數不合法,errno將返回EINVAL。

三、狀態判斷   子進程的結束狀態返回後存於status,可經過宏判斷結束狀況 。

  • WIFEXITED(status)若是子進程正常結束則爲非0值。
  • WEXITSTATUS(status)取得子進程exit()返回的結束代碼,通常會先用WIFEXITED 來判斷是否正常結束才能使用此宏。
  • WIFSIGNALED(status)若是子進程是由於信號而結束則此宏值爲真 。
  • WTERMSIG(status)取得子進程因信號而停止的信號代碼,通常會先用WIFSIGNALED 來判斷後才使用此宏。
  • WIFSTOPPED(status)若是子進程處於暫停執行狀況則此宏值爲真。通常只有使用WUNTRACED 時纔會有此狀況。
  • WSTOPSIG(status)取得引起子進程暫停的信號代碼,通常會先用WIFSTOPPED 來判斷後才使用此宏。

3、實際測試

一、a b文件內容不相同時

運行結果:
子進程結束狀態 = 256
命令返回值 = 1
failed reason :[> 不一樣的地方]
複製代碼

二、a b文件內容相同時

運行結果:
子進程結束狀態 = 0
命令返回值 = 0
success
複製代碼

三、a或者b文件不存在時

運行結果:
子進程結束狀態 = 512
命令返回值 = 2
failed reason :[diff: a: No such file or directory]
複製代碼

四、diff bin文件不存在時

運行結果:
子進程結束狀態 = 32512
命令返回值 = 127
failed reason :[sh: 1: diff: not found]
複製代碼

4、參考資料

Linux下system與popen函數 - u013485792的專欄 - CSDN博客 https://blog.csdn.net/u013485792/article/details/52525702

Linux下使用popen()執行shell命令 - 功夫Panda - 博客園 http://www.cnblogs.com/caosiyang/archive/2012/06/25/2560976.html

linux system函數是否執行成功判斷方法 - weixin_39020720的博客 - CSDN博客 https://blog.csdn.net/weixin_39020720/article/details/80917126

相關文章
相關標籤/搜索