2015-11-06 73 views
-1

我想在Linux上用C構建一個進程記錄器,但是沒有正確的解決它。我希望它有3個列:USER,PID,COM​​MAND。我正在使用ps aux的輸出並試圖將其動態追加到數組中。也就是說,對於每行ps aux輸出,我想添加一行到我的數組。爲什麼我的動態2D字符數組中的值被覆蓋?

這是我的代碼。 (爲了保持輸出短路,我只用grep的昇華。但是,這可能是任何東西。)

#define _BSD_SOURCE 
#include <stdio.h> 
#include <string.h> 
#include <stdlib.h> 

int main() 
{ 
    char** processes = NULL; 
    char* substr = NULL; 
    int n_spaces = 0; 
    int columns = 1; 

    char line[1024]; 
    FILE *p; 
    p = popen("ps -eo user,pid,command --sort %cpu | grep sublime", "r"); 
    if(!p) 
    { 
     fprintf(stderr, "Error"); 
     exit(1); 
    } 

    while(fgets(line, sizeof(line) - 1, p)) 
    { 
     puts(line); 
     substr = strtok(line, " "); 
     while(substr != NULL) 
     { 
      processes = realloc(processes, sizeof(char*) * ++n_spaces); 

      if(processes == NULL) 
       exit(-1); 

      processes[n_spaces - 1] = substr; 

      // first column user, second PID, third all the rest 
      if(columns < 2)//if user and PID are already in array, don't split anymore 
      { 
       substr = strtok(NULL, " "); 
       columns++; 
      } 
      else 
      { 
       substr = strtok(NULL, ""); 
      } 
     } 
     columns = 1; 
    } 
    pclose(p); 

    for(int i = 0; i < (n_spaces); i++) 
     printf("processes[%d] = %s\n", i, processes[i]); 

    free(processes); 

    return 0; 

} 

的for循環結尾處的輸出看起來是這樣的。

processes[0] = user 
processes[1] = 7194 
processes[2] = /opt/sublime_text/plugin_host 27184 

processes[3] = user 
processes[4] = 7194 
processes[5] = /opt/sublime_text/plugin_host 27184 

processes[6] = user 
processes[7] = 27194 
processes[8] = /opt/sublime_text/plugin_host 27184 

processes[9] = user 
processes[10] = 27194 
processes[11] = /opt/sublime_text/plugin_host 27184 

但是,從puts(line)我得到的數組實際上應該包含這樣的:

user  5016 sh -c ps -eo user,pid,command --sort %cpu | grep sublime 
user  5018 grep sublime 
user  27184 /opt/sublime_text/sublime_text 
user  27194 /opt/sublime_text/plugin_host 27184 

因此,顯然所有的值都被覆蓋,我想不通爲什麼...(另外,我沒有得到processes[0] = 7194processes[4] = 7194的值7194來自哪裏)。

我在這裏做錯了什麼?是否有可能使輸出看起來像puts(line)的輸出?

任何幫助,將不勝感激!

+3

這裏沒有二維數組。你有一個一維數組的指針。對於每個指針,分配一塊內存並將讀取的子字符串複製到那裏。 – Lundin

回答

1

strtok的返回值是一個指向您表示字符串的指針。 (該令牌已通過覆蓋第一個分隔符後以'\0'進行空終止。)

當您閱讀fgets的新行時,將覆蓋此行的所有內容以及所有標記,而不僅僅是來自最後解析,指向該行的實際內容。 (指向先前令牌的指針保持有效,但這些位置的內容會改變。)

有幾種方法可以解決這個問題。

  • 您可以讓令牌保存字符數組和標記爲strcpy的解析內容。
  • 您可以使用(非標準)strdup複製解析的標記,它爲堆上的字符串分配內存。
  • 您可以讀取一行數組,以便令牌非常獨特。
+0

感謝您的幫助!我嘗試了'strdup'的方式,它的工作原理!還有一個問題:什麼是最快的(在執行時間方面)方法? – drZaius

+0

很難說哪種方法最快。重複的分配需要一些時間(因此,爲每個字符串重新分配'processes'應該很慢),但在您的示例中,我希望外部調用對於性能比存儲字符串的方式更重要。 'strdup'變體當然是最靈活的方法,但請記住稍後'釋放'重複的字符串。 –

+0

我應該在哪裏釋放重複的字符串? while循環之後(就像'free(processes)')一樣? 有沒有替代'popen()',也許替代'ps'來提高性能?也許沒有使用外部電話? – drZaius

1

strtok將指針返回到它所處理的字符串中。該字符串始終保存在變量line中。因此,數組中的所有指針指向內存中的相同位置。

由於@Lundin在評論中寫道,這裏沒有二維數組,只有一維指針數組。