Mac应用程序如何能够跟踪文件的位置?


18

我在Mac上观察到这样的行为:

  • 使用PDF Expert打开PDF,对文件进行一些更改,在Finder中移动文件,将其保存在PDF Expert中,然后将其正确保存到新位置。
  • 在目录中打开外壳程序,例如~/foo,将目录与另一个应用程序一起使用,外壳程序的pwd正确输出~/.Trash/foo

到底发生了什么事?这些情况似乎表明应用程序不仅仅拥有emacs之类的文件的绝对路径(我对吗?),或者这是一种完全不同的机制?

Answers:


21

macos具有/.vol/映射到实际目录和文件的特殊系统。/.vol/<device_id>/<inode_number>无论文件在文件系统上的什么位置,都可以通过访问文件和目录。

这是一个很好的小系统。

因此,程序可以例如获取inode号,/Users/jdoe/someFile.txt然后通过来打开它/.vol/12345/6789(在这种情况下,设备ID为12345,inode号为6789)。然后,您可以将其移至所需的/Users/jdoe/someFile.txt任何位置(相同的音量),并且一切正常。您甚至可以编写支持此功能的Shell脚本magic

ls -di <file> 获取索引节点号。

$ ls -di /User/jdoe/someFile.txt
6789 /User/jdoe/someFile.txt

编辑:

您可以stat根据IMSoP突出显示的链接答案来获取卷的ID和索引节点号。

GetFileInfo /.vol/12345/6789将返回先前位于中的文件的当前位置/Users/jdoe/someFile.txt

有关更多信息,请参见/programming/11951328/is-there-any-function-to-retrieve-the-path-associated-with-an-inode


1
根据链接的答案,stat这里的命令比更为有用ls -di,因为它告诉您卷/设备ID以及文件ID /索引节点号。
IMSoP

4
在Debian中,我没有,/.vol/并且仍然会发生(尽管我需要pwd -P,只有这样,plain的输出pwd才会更新)。我猜程序不必通过任何特殊的路径打开文件,因为通常它们会获取(并保留)内核始终映射到inode的文件描述符。我怀疑在Mac /.vol/上也不是必不可少的。
卡米尔Maciorowski

因此,如果将文件移动到其他磁盘,此方案将中断。
Joel Coehoorn

1
@JoelCoehoorn是的,但是从技术上讲,您不能文件移动到其他磁盘。您可以将其复制到另一张磁盘上,然后将其删除,并且有一些快捷方式可以将其作为“一步”进行,但是它仍然是一个复制删除操作,而不是移动操作,因此从技术上讲是一个不同的文件。
ibrewster

1
许多文本编辑器读取给定文件,将其关闭,使用其副本并保存到同一路径,因此他们在旧位置重新创建文件。但是他们可能会一直保持文件打开状态并在最后写入文件。我bash在Debian上做到了。我运行exec 3<>foo,然后foo在同一文件系统中移动echo whatever >&3,然后foo在新位置签入-并进行了更改。尽管bash无法在文件中查找,但其他程序通常可以。我的观点/.vol/不是必须的,没有它,程序可以很容易地像这样工作。或者我不明白有什么区别。
卡米尔Maciorowski

1

以下答案是错误的(请参阅评论)。请无视


除了thecarpy给出的良好答案之外,您的程序可能还只是持有一个文件句柄,而该文件句柄与目录树中的文件位置无关(并且在Unix系统上,甚至持续删除文件,至少直到您将其关闭为止) )。

文件句柄基本上是对文件的直接访问,而与文件在目录结构中的位置或存在频率(在硬链接的情况下)无关。


不,我想您也没有得到...请参阅我对@KamilMaciorowski的评论。filehandle不会更改,然后保存文件时,会在原始位置创建一个新文件.... macOS不会这样!
Thecarpy

1
您是正确的,这是非常意外的,并且与Unix非常不同。:(
汤姆

同意并赞成!
thecarpy

0

尽管我不确定macos为什么使用此功能而不是标准C功能,但假设我几年前在“ Mac OS X Unleashed”中阅读的内容是正确的,事实证明我又学到了一些新知识。

请看下面的简单C程序:

#include <stdio.h>
#include <time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>

int main()
{
    struct timespec ts;
        ts.tv_sec = 10;
        ts.tv_nsec = 0;
    FILE * fp;

    fp = fopen("file.txt", "a");
    int f = fileno(fp);

    if (fp == NULL)
    {
        printf("Error opening file!\n");
        exit(1);
    }

    struct stat file_stat;
    int ret;
    ret = fstat (f, &file_stat);
    printf("inode number is %d\n", file_stat.st_ino);
    nanosleep(&ts, NULL);

    printf("Finished sleep, writing to file.\n");

/* print some text */
    const char *text = "Write this to the file";
    dprintf(f, "Some text: %s\n", text);

/* print integers and floats */
    int i = 1;
    float py = 3.1415927;
    dprintf(f, "Integer: %d, float: %f\n", i, py);

/* printing single characters */
    char c = 'A';
    dprintf(f, "A character: %c\n", c);

    close(f);
}

编译该程序,在后台运行,mv file.txt file2.txt然后在程序打印“完成睡眠,写入文件”之前快速运行它。(您有10秒)

请注意,file2.txt尽管在文本打印到文件之前已移动程序(通过文件描述符),但它具有程序的输出。

$ gcc myfile.c
$ ./a.out &
[1] 21416
$ inode number is 83956
$ ./mv file.txt file2.txt
$ Finished sleep, writing to file.
[1]+  Done                    ./a.out
$ cat file2.txt
Some text: Write this to the file
Integer: 1, float: 3.141593
A character: A

免责声明:我还没有删掉“包含”列表,所以很快就被黑了,以证明这一点。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.