2017-02-24 58 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