2017-02-24 62 views
3

我想寫一個腳本,它具有獲取過程詳細信息的功能。閱讀/ proc程序

到目前爲止,我有

#include <stdio.h> 
#include <string.h> 
#include <ctype.h> 

char* getField(FILE* file, char* prop, int len){ 
char line[100], *p; 

while(fgets(line, 100, file)) { 
    if(strncmp(line, prop, len) != 0) 
      continue; 

    p = line + len + 1; 
    while(isspace(*p)) ++p; 

    break; 
} 

return p; 
} 

int main(int argc, char *argv[]) { 

    char tgid[40], status[40], *s, *n; 
    FILE* statusf; 

    printf("Please Enter PID\n"); 

    if (fgets(tgid, sizeof tgid, stdin)) { 
    //Remove new line 
    strtok(tgid, "\n"); 
    snprintf(status, 40, "/proc/%s/status", tgid); 

    statusf = fopen(status, "r"); 
    if(!statusf){ 
     perror("Error"); 
     return 0; 
    } 

    s = getField(statusf, "State:", 6); 
    n = getField(statusf, "Name:", 5); 

    printf("State: %s\n", s); 
    printf("Name: %s\n", n); 

    }else{ 
    printf("Error on input"); 
    } 

    fclose(statusf); 
    return 1; 
} 

我還在尋找指針和記憶有點模糊。當我運行這個腳本沒有

n = getField(statusf, "Name:", 5); 

我得到正確的輸出(如S - 睡覺);

但是,當我調用該函數來獲取進程名稱,我似乎得到了同樣的輸出,例如。

國家:ntary_ctx 名稱:ntary_ctx

而這甚至不是正確的名稱。我認爲問題必須是變量保持價值的功能。但我認爲,當一個函數返回它的內存然後彈出堆棧。

+7

只是爲了語義的緣故,一個C程序是不是一個腳本 – rtur

+2

@rtur感謝,剛改了稱呼。我有很多東西需要學習 –

+0

編譯所有警告和調試信息('gcc -Wall -g'),然後使用調試器('gdb')。順便說一句,閱讀更多關於C(例如[here](http://en.cppreference。com/w/c))並考慮使用'scanf'來讀取一些'int' pid。 –

回答

4

代碼被重新調整指針的局部變量。
這是無效的 - 它是未定義的行爲(UB)。 @stark
這解釋了「我似乎得到同樣的輸出」,因爲一個可能的UB是相同的緩衝區被重新使用。代碼崩潰的另一個可能性是其他候選人。

// Bad code 
char* getField(FILE* file, char* prop, int len){ 
char line[100], *p; 
... 
p = line + len + 1; 
... 
return p; // `p` points to `line[]` 
} 

代碼需要複製。可以通過分配或傳遞目標來完成此操作,如下所示。

char* getField(FILE* file, char *dest, const char* prop, int len){ 
    if (problem) return NULL; 
    ... 
    return strcpy(dest, p); 
} 

// Example call 
char prop_state[100]; 
if (getField(statusf, prop_state, "State:", 6)) Success(); 
else Handle_Problem(); 
... 
char prop_name[100]; 
if (getField(statusf, prop_name, "Name:", 6)) Success(); 
... 

更好的代碼將通過在dest大小,以便getField()可以搞定

char* getField(FILE* file, char *dest, size_t size, const char* prop, int len){ 
    ... 
    if (strlen(p) >= size) return NULL; // Not enough room 
    return strcpy(dest, p); 
} 

// usage 
if (getField(statusf, prop_state, sizeof prop_state, "State:", 6)) Success(); 
... 
+0

@davidKirwan注意:建議將提示傳遞給'getField()',以便根據需要重新提示。 「繼續」不會向用戶提供反饋。 [示例](http://stackoverflow.com/a/41146306/2410359) – chux

+0

非常感謝您的幫助 –

0

如果您使用該方法,則需要在每次讀取該字段時關閉並重新打開該文件,或者在回退到開始時重新打開該文件。編寫C輸入文件解析器並不是一個好方法,但它可以用於短文件和快速程序。

+0

啊,好吧。是的,這可能是各種各樣的錯誤,我會做很多適當的C編程實踐的研究。 –

+0

通常你會聲明一個包含所有選項的結構體,然後編寫一個從配置文件中填充它的函數。所以配置文件只讀一次。 –

1

一個問題是,在getField()你要返回p這是一個指向line加上一些偏移量。但line是該函數中的局部變量,因此當函數終止時它超出了範圍。 The answer是一個很好的解釋。

作爲第一步,你可以讓它static char line[100]被允許在函數返回後使用指針,但是第二個調用仍然會覆蓋你第一次讀到的內容。 所以最好的辦法是通過一個額外的緩衝值:

char* getField(FILE* file, char* prop, int len, char *value){ 
    char line[100], *p; // now it's ok 
    ... // everything at it is now before return 
    strcpy(value, p); 
    return value; 
} 

,並在main(),你有兩個不同的緩衝區

char name[100], state[100]; // at least same size as line - length of label 
.... 
s = getField(statusf, "State:", 6, state); 
n = getField(statusf, "Name:", 5, name); 
+0

靜態不會幫助,因爲他多次調用該函數。 – stark

+0

@stark oops,你說得對,'沒有看過那個。我將更正答案 –

+0

顯示多個調用'getField()'的好答案。 – chux