在C中的Linux上搜索$ PATH中的文件

时间:2013-01-28 21:04:36

标签: c linux environment-variables

我想测试我的程序运行的系统上是否安装了GNUPlot 为此,我想我将通过stat()调用测试用户安装位置中gnuplot可执行文件的存在。

但是,我不知道如何在C中读取$ PATH环境变量,因此我可以测试这些位置中文件是否存在。

7 个答案:

答案 0 :(得分:3)

使用getenv() function.

char *paths = getenv("PATH");

要遍历以列分隔的路径列表的部分,请使用strchr()

char *dup = strdup(getenv("PATH"));
char *s = dup;
char *p = NULL;
do {
    p = strchr(s, ':');
    if (p != NULL) {
        p[0] = 0;
    }
    printf("Path in $PATH: %s\n", s);
    s = p + 1;
} while (p != NULL);

free(dup);

答案 1 :(得分:2)

使用getenv()检查特定环境变量的值。

答案 2 :(得分:1)

要阅读PATH环境变量,请使用getenv("PATH")

但是,如果您只想运行gnuplot(如果可用),并执行一些后备操作(如果不可用),那么您应该尝试运行它(例如{{1} }}和forkexecvp)并处理失败案例。

答案 3 :(得分:0)

让哪个为你工作

if (system("which gnuplot"))
    /* not installed or not in path or not executable or some other error */

如果由于某种原因需要完整路径,请使用popen运行 或者使用一些标志运行gnuplot,使其立即返回0 * /

if (system("gnuplot --version"))
    /* not installed ... */

答案 4 :(得分:0)

我也有类似的需求,并通过复制libc execvp代码源来解决。我在我能想到的最跨平台上做了(我没有担保,只在Linux上进行了测试)。如果这对您来说不是问题,并且您关心表演,则应使用acess或_acess。请注意,不会进行任何错误检查,它只会在路径中返回NULL或已建立的可打开文件。

接受的答案有时是不可接受的,当您愿意一遍又一遍地运行相同的小二进制文件时,每次通过调用execvp来重做路径搜索都是不可忽略的开销。

下面是代码和相关的测试,您将主要对search_in_path_openable_file函数感兴趣。

.h文件:

bool is_openable_file(char* path);
/*Return true if path is a readable file. You can call perror if return false to check what happened*/

char* search_in_path_openable_file(char* file_name);
/*Search into PATH env variable a file_name and return the full path of the first that is openable, NULL if not in path*/

char* search_executable(char* file_name);
/*Search file, if not openable and not absolute path(contain /), look for opennable file in the path. If nothing is openable, return NULL. If something is openable, return it as it is (not guaratented to have a full path, but garatanted to be openable)*/

.c文件:

#include "file_info.h"
#include <stdio.h>
#include <string.h> //strcpy

/*I wanted to do a really cross platform way. access or _acess may be better*/
bool is_openable_file(char *path) {
    FILE *fp = fopen(path, "r");
    if (fp) {
        // exists
        fclose(fp);
        return true;
    }

    return false;
}

bool is_openable_file_until(char *path_begin, size_t until) {
    char old = path_begin[until];
    path_begin[until] = 0;
    bool res = is_openable_file(path_begin);
    path_begin[until] = old;
    return res;
}

/*You may thinks that libc would have done this function and use it to implement execp function family, but you would be wrong. They just hardcoded the search in every execp function. Unbelievable.
 *
 * So this function is a modification of their execvp function. 
 *
 * */

char* search_in_path_openable_file(char* file){
    char *path = getenv("PATH");
    if (path == NULL)
        return NULL;
    size_t pathlen = strlen(path);
    size_t len = strlen(file) + 1;
    int total_max_size=pathlen + len;
    char* buf=malloc(sizeof(char)*total_max_size);
    if (*file == '\0') {
        return NULL;
    }
    char *name, *p;
    /* Copy the file name at the top.  */
    name = memcpy(buf + pathlen + 1, file, len);
    /* And add the slash.  */
    *--name = '/';
    p = path;
    do {
        char *startp;
        path = p;
        //Let's avoid this GNU extension.
        //p = strchrnul (path, ':');
        p = strchr(path, ':');
        if (!p)
            p = strchr(path, '\0');
        if (p == path)
            /* Two adjacent colons, or a colon at the beginning or the end
               of `PATH' means to search the current directory.  */
            startp = name + 1;
        else
            startp = memcpy(name - (p - path), path, p - path);
        /* Try to execute this name.  If it works, execv will not return.  */
        if (is_openable_file(startp))
            return startp;
    } while (*p++ != '\0');
    /* We tried every element and none of them worked.  */
    return NULL;
}

char* search_executable(char* file_name){

    if (is_openable_file(file_name)){//See realpath manual bug. Watch out
        return file_name;
    }
    if (strchr (file_name, '/') != NULL) //Don't search when it contains a slash. 
        return NULL;
    return search_in_path_openable_file(file_name);
}

测试(如您所见,我没有对该功能进行过多测试,可能存在一些问题,使用风险自负):

 #include "file_info.h"
#include "munit.h"
#include <stdbool.h>
#include <unistd.h>
static void generate_search_executable(char* test_str, char* expected){
    char* res= search_executable(test_str);
     if (res==NULL)
        munit_assert_ptr(expected,==,NULL );
     else
    munit_assert_string_equal(expected,res);
}

static void generate_openable(char* test_str, bool expected){
    bool res= is_openable_file(test_str);
    munit_assert_true(expected==res);
}

static void generate_path_search(char* test_str, char* expected_res){
     char* res= search_in_path_openable_file(test_str);
     if (res==NULL)
        munit_assert_ptr(expected_res,==,NULL );
     else
    munit_assert_string_equal(expected_res,res);
}

//TODO do for other platform, better test would also set path to a custom folder that we control
#define EXISTING_FILE_NOT_IN_PATH "/usr/include/stdlib.h" 
#define EXISTING_FILE_IN_PATH "ls" 
#define EXISTING_FILE_IN_PATH_FULL "/bin/ls" 
#define NOT_EXISTING_FILE "/usrarfzsvdvwxv/ixvxwvnxcvcelgude/ssdvtdbool.h" 
int main() {

    generate_openable(EXISTING_FILE_IN_PATH, false);
    generate_openable(EXISTING_FILE_NOT_IN_PATH, true);
    generate_openable(NOT_EXISTING_FILE, false);

    generate_path_search(EXISTING_FILE_IN_PATH, EXISTING_FILE_IN_PATH_FULL);
    generate_path_search(NOT_EXISTING_FILE, NULL);
    generate_path_search(EXISTING_FILE_NOT_IN_PATH, NULL);

    generate_search_executable(EXISTING_FILE_IN_PATH, EXISTING_FILE_IN_PATH_FULL);
    generate_search_executable(NOT_EXISTING_FILE, NULL);
    generate_search_executable(EXISTING_FILE_NOT_IN_PATH, EXISTING_FILE_NOT_IN_PATH);

    generate_search_executable("", NULL );

    //test current folder existence(maybe it just depend on path containing .,I am not sure, in that case we should remove thoses tests
    generate_search_executable("file_info_test", "file_info_test" );


}

答案 5 :(得分:0)

要在 one of the previous answers 上构建,您可以使用 require 获取 getenv 的内容,然后迭代其组件。您可以使用 PATH 代替 strchr

strsep

答案 6 :(得分:-1)

使用 C++17 获取路径元素向量。

// implementation.mm

@implementation
std::shared_ptr<MyCppObject> _ptr;

-(id)initWithSharedPtr:(std::shared_ptr<MyCppObject>) ptr {
  self = [super init];
  if (self) {
    _ptr = ptr;
  }

  return self;
}

-(NSString *) GetString {
 // write code to return _ptr->GetString();
}