如何使用 MacOs 将输出文件保存到 c 中它所属的文件夹中

How do I save an output file to the folder it belongs to in c using MacOs

提问人:ben 提问时间:11/15/2023 最后编辑:Chrisben 更新时间:11/15/2023 访问量:67

问:

该文件当前保存到 /Users/benreaby,而我希望它保存到可执行文件的源文件夹中

// Filename that I want to save to the source 
// folder that the executable is located in
#define FILENAME "TACmFile.txt" 

int main(int argc, const char * argv[]) {
    fp = fopen(FILENAME, "w");
    fprintf(fp, "hello world");
    fclose(fp);
    return 0;
}
C 文件 目录

评论

1赞 greg spears 11/15/2023
您使用的是什么操作系统?
0赞 ben 11/15/2023
我使用的是 MacOS
1赞 user3386109 11/15/2023
如果从命令行生成,然后从命令行运行,则不会问这个问题。因此,我假设您正在 IDE 中构建和运行。在这种情况下,您需要在项目配置中设置工作目录。详细信息取决于您使用的 IDE。

答:

3赞 Chris 11/15/2023 #1

当运行可执行文件并使用相对文件路径时,文件 TACmFile.txt 将写入运行可执行文件时当前工作目录的任何位置。这不一定与源代码所在的目录有任何相似之处。

如果要覆盖它,则需要指定绝对路径。您可能需要从配置文件中读取可执行文件才能获取文件路径信息。

评论

0赞 Aconcagua 11/15/2023
据推测,可执行文件的位置可能是所需的引用,而不是源代码目录,如果是这样,则可以从 .argv[0]
1赞 greg spears 11/15/2023 #2

这是一个解决方案。这取决于 argv[0] 中是否存在可执行文件路径和文件名。此处修改了代码以添加新函数 merge_pf(),该函数使用在 argv[0] 中找到的可执行文件的路径。它从路径中剥离可执行文件名称,并将所需的文件名追加到同一路径。这个新字符串可用于在程序目录中打开您选择的文件。

这段代码被我测试为win32。路径标记('/')是为Unix添加的 - 因此,也应该在那里工作 - 但未经过测试在使用此代码之前,请测试/检查您的操作系统是否支持包含程序路径和文件名的 argv[0] 的关键原则

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


/* merge_pf() expects path to have exe name, ie: 'F:/dir/program.exe'.
   Param fname is some filename, ie: 'file.txt'
   Example final string would be: 'F:/dir/file.txt'
*/
char *merge_pf(const char *path, const char *fname)
{
    int  iii;
    char *path_file = malloc(strlen(path)+strlen(fname)+1);

    if(path_file)
    {
        strcpy(path_file, path);
        iii=(int)strlen(path_file);
        while(iii--)
        {
            if(path_file[iii]=='\\' || path_file[iii]=='/') /* DOS or Unix path token*/
                break;  /* Stop if we hit the last token */
            path_file[iii]='\0';    /*strip .exe name from path*/
        }
        strcpy(&path_file[iii+1], fname); /* merge path and file name*/
    }
    return path_file;
}

// Filename that I want to save to the source 
// folder that the executable is located in
#define FILENAME "TACmFile.txt" 

int main(int argc, const char *argv[]) 
{
    char *pf = merge_pf(argv[0], FILENAME);
    FILE *fp;

    if(pf)
    {
        printf("Will open: %s\n", pf);
        fp = fopen(pf, "w");
        if(fp)
        {
            fprintf(fp, "hello world");
            fclose(fp);
        }
        free(pf);
    }
    return 0;
}

查看您的操作系统是否在 arv[0] 中具有程序路径和文件名的测试可能如下所示......(可能存在一种更安全的方法):

#include <stdio.h>

int main(int argc, const char *argv[]) 
{
    const char *pf = argv[0];

    if(pf)
    {
        printf("Program Path and File name: %s\n", pf);
    }
    return 0;
}

成功将如下所示:

    Program Path and File name: f:/PROJECTS/32bit/_HELP/C/H118/math.exe 
0赞 Luis Colorado 11/24/2023 #3

在每个 POSIX 系统中,当内核加载程序时,它会从指定为任何 exec*(2) 系统调用的第一个参数的文件加载到内存中。系统调用的不同变体通常实现不同的 API 和查找可执行文件的方法。

exec 系统调用的最通用格式要求您向其传递程序名称(文件系统上存在的文件的名称)、表示程序参数的字符串数组(包括其名称)以及表示程序环境的字符串数组。由于这三件事可以由程序任意指定,因此程序没有固定且可预测的方法来知道程序在文件系统中的位置,除非该信息由执行系统调用的进程传递给程序。argvenvpexec(2)

例如,如果调用

    execle("bin/program", "a", "b", "c", NULL, envp);

您将要求内核在当前工作目录下的目录中查找程序,但由于第 0 个参数是程序将收到一个由字符串“a”(程序名称)、“b”(第一个参数)和“c”(第二个参数)组成的参数列表,因此没有遵循确定程序调用方式的正常方法,并且不会告诉我们有关加载到内存中的文件的任何有用信息。由于环境也被指定,即使我们对参数有一些想法或访问权限,环境也是不同的,并且在调用的情况下,如下所示:"bin""a"argv[0]argv[0]

    execle("program", "program", "a", "b", NULL, envp);

程序将无法使用环境变量找到程序的位置,因为用于搜索程序的环境变量与通过 exec() 调用传递的环境变量不同。PATHPATH

如果幸运的话,shell 会告诉你一些事情(如果你运行一个好的 shell),因为它会自动向你传递导出的环境,以及用于调用程序的程序名称,因此你可以按照 shell 用来查找你的程序的相同算法,找到一个可执行文件,该文件可以被 但是仍然存在程序文件被重命名并且无法从该路径访问的竞争条件。exec()

综上所述,您可以检查该目录是否存在并使用该目录(但从最后一个“/”字符切入)并将其用作目录。argv[0]

在 linux 中,可以检查“/proc/self/cmd”以获取用于调用 exec 的文件名,但如果路径名是相对的(不存在“/”字符),并且变量已更改,您将无法遵循用于查找可执行文件的相同目录列表。PATHexec()

通常,这不会失败,常见的过程包括检查字符串并尝试从该路径中提取可执行文件名,并将其用作要写入文件的目录的目录名称。这并不完全可靠,但在大多数情况下都有效。argv[0]