8

运行下面的代码时,只有当我使用我的 ide、TextEditor.app 或 vim 手动编辑和保存 tmp.txt 时才会触发手表。

它不是通过写入流或手动 shell 输出重定向(键入 echo "test" > /path/to/tmp.txt")的方法。

虽然如果我看文件本身,而不是它的目录名,那么它就可以工作。

var fs, Path, file, watchPath, w;

fs = require('fs' );
Path = require('path');
file = __dirname + '/tmp.txt';
watchPath = Path.dirname(file); // changing this to just file makes it trigger

w = fs.watch ( watchPath, function (e,f) {
    console.log("will not get here by itself");
    w.close();
});
fs.writeFileSync(file,"test","utf-8");

fs.createWriteStream(file, {
    flags:'w',
    mode: 0777
} )
.end('the_date="'+new Date+'";' ); // another method fails as well

setTimeout (function () {
    fs.writeFileSync(file,"test","utf-8");
},500); // as does this one
// child_process exec and spawn fail the same way with or without timeout

所以问题是:为什么?以及如何从节点脚本以编程方式触发此事件?

谢谢!

4

3 回答 3

11

问题是,您要求查看目录而不是文件

修改文件时目录不会更新,例如通过 shell 重定向;在这种情况下,文件被打开、修改和关闭。目录没有改变——只有文件是。

当您使用文本编辑器修改文件时,通常的幕后系统调用集如下所示:

fd = open("foo.new")
write(fd, new foo contents)
unlink("foo")
rename("foo.new", "foo")

这样,该foo文件要么完全是旧文件,要么完全是新文件,并且不可能有包含新内容的“部分文件”。重命名操作确实会修改目录,从而触发目录监视。

于 2012-05-26T00:39:24.377 回答
11

它不会触发,因为对文件内容的更改不是对目录的更改。

在幕后,至少从 0.6 开始,Mac 上的 fs.watch 使用 kqueue,它是 kqueue 文件系统通知的一个非常薄的包装器。所以,如果你真的想了解细节,你必须了解 kqueue,和 inode 之类的东西。

但是如果你想要一个简短的“对孩子撒谎”的解释:用户认为的“文件”实际上是两个独立的东西——实际文件和指向实际文件的目录条目。这就是允许您拥有诸如硬链接之类的东西,以及即使在您删除它们之后仍然可以读取和写入的文件等等。

通常,当您写入现有文件时,这不会对目录条目进行任何更改,因此查看目录的任何人都不会看到任何更改。这就是为什么 echo >tmp.txt 不会触发您的原因。

但是,例如,如果您编写一个新的临时文件,然后将其移到旧文件上,这确实会更改目录条目(使其成为指向新文件而不是旧文件的指针),因此您会收到通知。这就是 TextEditor.app 确实会触发您的原因。

于 2012-05-26T00:39:50.820 回答
4

尽管上述答案似乎合理,但它们并不完全准确。能够监听目录的文件更改实际上是一个非常有用的功能,而不仅仅是“重命名”。我认为此功能至少在 Windows 中按预期工作,并且在节点 0.9.2 中也适用于 mac,因为它们更改为支持该功能的 FSEvents API:

版本 0.9.2(不稳定)

于 2012-10-22T12:48:59.820 回答