在C中,当从符号链接调用时,如何使用getcwd获取可执行路径

在C中,当从符号链接调用时,如何使用getcwd获取可执行路径,c,linux,file-io,symlink,C,Linux,File Io,Symlink,假设我在Linux中有一个程序,abc,它有一个方法: char *currentPath(){ char *path=NULL; path = getcwd(path, MAXPATHLEN); return path; } 直接调用abc时,此路径返回abc所在的路径 如果我创建一个指向abc的符号链接并调用该符号链接,currentPath()将返回符号链接的路径 有没有办法让这个方法返回abc的路径?我对访问与abc位置相关的文件感兴趣。

假设我在Linux中有一个程序,
abc
,它有一个方法:

char *currentPath(){
        char *path=NULL;
        path = getcwd(path, MAXPATHLEN);
        return path;
}
直接调用
abc
时,此路径返回
abc
所在的路径

如果我创建一个指向abc的符号链接并调用该符号链接,
currentPath()
将返回符号链接的路径

有没有办法让这个方法返回abc的路径?我对访问与
abc

位置相关的文件感兴趣。您不能使用
getcwd()
来帮助获取可执行文件名;查找可执行文件的路径名是不明智的

在Linux上,有一个符号链接
/proc/self/exe
,如果您使用系统调用读取可执行文件,它将为您提供可执行文件的名称。请注意,
readlink()
系统调用不会以null终止它返回的值(我也不知道为什么不为空;在我的书中,这是一种奇怪的行为,对于粗心的人来说,这是导致bug的即时原因)

你的计划有很多问题

  • 如果程序是通过$PATH找到的,则不能保证当前目录与包含可执行文件的目录相同。仔细想想,您可以运行
    /bin/ls
    ,而不必位于
    /bin
    目录中
  • 如果您担心安全性,请注意
    argv[0]
    的值受启动目标程序的程序控制。外壳表现良好;其他程序可能更恶意:

    #include <unistd.h>
    int main(void)
    {
        char *argv[] = { "/opt/you/bin/bogus", "300", 0 };
        execvp("sleep", argv);
        return(-1);
    }
    
    #包括
    内部主(空)
    {
    char*argv[]={/opt/you/bin/bogus,“300”,0};
    execvp(“睡眠”,argv);
    返回(-1);
    }
    
    这会将程序名传递为
    /opt/you/bin/bogus
    ,即使它调用程序
    sleep

  • 如果你在网上搜索,你会发现很多关于“如何获取可执行文件名”的例子,这些例子假设
    argv[0]
    是一条可行之路;事实并非如此。其他平台有其他获取可执行文件名的技术。使用
    /proc/self/exe
    是不可移植的;其他技术也一样。

    您不能使用
    getcwd()
    来帮助获取可执行文件名;查找可执行文件的路径名是不明智的

    在Linux上,有一个符号链接
    /proc/self/exe
    ,如果您使用系统调用读取可执行文件,它将为您提供可执行文件的名称。请注意,
    readlink()
    系统调用不会以null终止它返回的值(我也不知道为什么不为空;在我的书中,这是一种奇怪的行为,对于粗心的人来说,这是导致bug的即时原因)

    你的计划有很多问题

  • 如果程序是通过$PATH找到的,则不能保证当前目录与包含可执行文件的目录相同。仔细想想,您可以运行
    /bin/ls
    ,而不必位于
    /bin
    目录中
  • 如果您担心安全性,请注意
    argv[0]
    的值受启动目标程序的程序控制。外壳表现良好;其他程序可能更恶意:

    #include <unistd.h>
    int main(void)
    {
        char *argv[] = { "/opt/you/bin/bogus", "300", 0 };
        execvp("sleep", argv);
        return(-1);
    }
    
    #包括
    内部主(空)
    {
    char*argv[]={/opt/you/bin/bogus,“300”,0};
    execvp(“睡眠”,argv);
    返回(-1);
    }
    
    这会将程序名传递为
    /opt/you/bin/bogus
    ,即使它调用程序
    sleep

  • 如果你在网上搜索,你会发现很多关于“如何获取可执行文件名”的例子,这些例子假设
    argv[0]
    是一条可行之路;事实并非如此。其他平台有其他获取可执行文件名的技术。使用
    /proc/self/exe
    是不可移植的;其他技术也不适用。

    使用

    realpath()展开所有符号链接,并解析对/、/../和的引用 以路径命名的以空结尾的字符串中的额外“/”字符,用于生成 规范化的绝对路径名

    就你而言:

    char *currentPath() {
      char *path, *canon_path;
      path = getcwd(NULL, MAXPATHLEN);
      canon_path = realpath(path, NULL);
      free(path);
      return canon_path;
    }
    
    请注意,这不会获取可执行程序的路径(不清楚您正在尝试执行什么)。要做到这一点,便携是比较棘手的。您需要使用
    argv[0]
    的值来获取它:

    char *bindir(char *argv0) {
      char *canon_path = realpath(argv0, NULL);
      char *canon_dir = strdup(dirname(canon_path));
      free(canon_path);
      return canon_dir;
    }
    
    strdup
    调用是必需的,因为
    dirname
    可以修改其参数并将指针返回到该参数中,或返回指向静态分配的缓冲区的指针。

    使用

    realpath()展开所有符号链接,并解析对/、/../和的引用 以路径命名的以空结尾的字符串中的额外“/”字符,用于生成 规范化的绝对路径名

    就你而言:

    char *currentPath() {
      char *path, *canon_path;
      path = getcwd(NULL, MAXPATHLEN);
      canon_path = realpath(path, NULL);
      free(path);
      return canon_path;
    }
    
    请注意,这不会获取可执行程序的路径(不清楚您正在尝试执行什么)。要做到这一点,便携是比较棘手的。您需要使用
    argv[0]
    的值来获取它:

    char *bindir(char *argv0) {
      char *canon_path = realpath(argv0, NULL);
      char *canon_dir = strdup(dirname(canon_path));
      free(canon_path);
      return canon_dir;
    }
    
    strdup
    调用是必需的,因为
    dirname
    可能会修改其参数并将指针返回到该参数中,或返回指向静态分配的缓冲区的指针。

    请尝试此代码(修改它以满足您的需要):

    #包括
    #包括
    #包括
    #包括
    #定义MAXPATHLEN 256
    字符缓冲区[1024];
    char*currentPath(char*bin\u路径){
    char*path=NULL;
    西泽特伦;
    结构统计文件_stat;
    if(lstat(bin\u路径和文件统计)!=0)
    {
    /*处理错误*/
    fprintf(stderr,“ERROR\n”);
    }
    如果(!S_ISREG(文件统计模式))
    {
    /*文件是一个符号链接*/
    fprintf(stdout,“是一个符号链接\n”);
    if((len=readlink(bin_路径,缓冲区,sizeof(缓冲区)-1))!=-1)
    缓冲区[len]='\0';
    /*在本例中,我们返回链接的名称*/
    返回缓冲区;
    }
    其他的
    {
    fprintf(stdout,“是一个常规文件\n”)