gpt4 book ai didi

linux - 在 fsnotify 上递归地重新生成文件删除/重命名 (Golang)

转载 作者:IT王子 更新时间:2023-10-29 01:12:20 26 4
gpt4 key购买 nike

目标:

我正试图监控一个随时可能被移动或删除的文件。如果是,我想重新生成此文件,以便应用程序可以继续写入。

尝试:

我试图通过实现两个函数来做到这一点,monitorFile() 来监听 fsnotify 事件并通过 channel 将删除的文件名发送到 listen( ) 通过非缓冲 channel mvrm(移动或重命名)接收文件路径字符串后,将递归地重新生成文件。

观察到的行为:

我可以echo 'foo' >> ./inlogs/test.log 并查看写通知,甚至可以 rm ./inlogs/test.log (或 mv) 并看到文件已重新生成...但仅生成一次。如果我第二次 rmmv 文件,则不会重新生成文件。

  • 奇怪的是,在本地 Mac OSx(系统版本:macOS 10.13.2 (17C88),内核版本:Darwin 17.3.0)上不会发生不良行为,但会在两台不同的 Linux 机器上发生:

Linux 3.13.0-32-generic#57-Ubuntu SMP x86_64 x86_64 x86_64 GNU/Linux

Linux 4.9.51-10.52.amzn1.x86_64 #1 SMP x86_64 x86_64 x86_64 GNU/Linux

尝试诊断:

不同的行为让我觉得我有竞争条件。但是 go build -race 没有提供任何输出。

我想知道 done 是否由于这样的竞争条件而收到?

很抱歉这不是“Playground-able”,但是我们欢迎任何建议或观察这可能是因为 racy 还是 buggy。

watcher.go:

package main

import (
"os"
"log"
"fmt"

"github.com/go-fsnotify/fsnotify"
)

//Globals
var mvrm chan string

func main() {
mvrm = make(chan string)
listen(mvrm)
monitorFile("./inlogs/test.log", mvrm)
}

func listen(mvrm chan string) {
go func() {
for {
select {
case fileName := <-mvrm :
fmt.Println(fileName)
newFile, err := os.OpenFile(fileName, os.O_RDWR | os.O_CREATE | os.O_APPEND , 0666)
if err == nil {
defer newFile.Close()

// Recursively re-spawn monitoring
go listen(mvrm)
go monitorFile(fileName, mvrm)
} else {
log.Fatal("Err re-spawning file")
}
default:
continue
}
}
}()
}

func monitorFile(filepath string, mvrm chan string) {
watcher, err := fsnotify.NewWatcher()
if err != nil {
log.Fatal(err)
}
defer watcher.Close()

done := make(chan bool)
go func() {
for {
select {
case event := <-watcher.Events:
switch event.Op {
case fsnotify.Write :
log.Println("Write!")
continue
case fsnotify.Chmod :
log.Println("Chmod!")
continue
case fsnotify.Remove, fsnotify.Rename :
log.Println("Moved or Deleted!")
mvrm <- event.Name
continue
default:
log.Printf("Unknown: %v\n", event.Op)
continue
}
case err := <-watcher.Errors:
log.Println("Error:", err)
}
}
}()

err = watcher.Add(filepath)
if err != nil {
log.Fatal(err)
}
<-done
}

编辑:

根据一些很好的反馈,我将其配对。在 Linux 中,它现在按预期重新生成文件,但在使用 top 进行监控后,我发现每次移动或删除文件时它都会生成一个新的 PID,所以我仍然有泄漏.欢迎就如何消除这种行为提出建议。

https://play.golang.org/p/FrlkktoK2-s

最佳答案

请查看代码注释,大部分讨论都在代码注释中。

https://play.golang.com/p/qxq58h1nQjp

在 golang 世界之外,但 facebook 有一个工具几乎可以满足您的需求,只是没有那么多 go code 的乐趣:): https://github.com/facebook/watchman

package main

import (
"log"
"os"

// couldn't find the go-fsnotify, this is what pops up on github
"github.com/fsnotify/fsnotify"
)

func main() {
monitorFile("./inlogs/test.log")
}

func monitorFile(filepath string) {

// starting watcher
watcher, err := fsnotify.NewWatcher()
if err != nil {
log.Fatal(err)
}
defer watcher.Close()

// monitor events
go func() {
for {
select {
case event := <-watcher.Events:
switch event.Op {
case fsnotify.Create:
log.Println("Created")

case fsnotify.Write:
log.Println("Write")

case fsnotify.Chmod:
log.Println("Chmod")

case fsnotify.Remove, fsnotify.Rename:
log.Println("Moved or Deleted")

respawnFile(event.Name)

// add the file back to watcher, since it is removed from it
// when file is moved or deleted
log.Printf("add to watcher file: %s\n", filepath)
// add appears to be concurrently safe so calling from multiple go routines is ok
err = watcher.Add(filepath)
if err != nil {
log.Fatal(err)
}

// there is not need to break the loop
// we just continue waiting for events from the same watcher

}
case err := <-watcher.Errors:
log.Println("Error:", err)
}
}
}()

// add file to the watcher first time
log.Printf("add to watcher 1st: %s\n", filepath)
err = watcher.Add(filepath)
if err != nil {
log.Fatal(err)
}

// to keep waiting forever, to prevent main exit
// this is to replace the done channel
select {}
}

func respawnFile(filepath string) {
log.Printf("re creating file %s\n", filepath)

// you just need the os.Create()
respawned, err := os.Create(filepath)
if err != nil {
log.Fatalf("Err re-spawning file: %v", filepath)
}
defer respawned.Close()

// there is no need to call monitorFile again, it never returns
// the call to "go monitorFile(filepath)" was causing another go routine leak
}

玩得开心!

关于linux - 在 fsnotify 上递归地重新生成文件删除/重命名 (Golang),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48001918/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com