英文:
Go filepath.Walk hangs
问题
我在使用Go语言的filepath.Walk()函数时遇到了一个奇怪的问题。它开始运行,然后到达一个点就停住了。我的CPU占用率达到了100%,没有错误信息,也没有继续执行。我查看了我遍历的目录,停住的地方没有任何特殊情况,没有符号链接或其他特殊文件,只有普通的目录和文件。以下是我使用的代码:
type WatchDirs struct {
Dirs []string
}
// Watches ...Recursively walk the filesystem, entrypoint to file watching
func Watches(tops []string) error {
var dirs WatchDirs
for _, top := range tops {
err := filepath.Walk(top, func(path string, f os.FileInfo, err error) error {
if err != nil {
log.Println(err)
return err
}
log.Println("File: ", path)
if f.IsDir() {
dirs.Dirs = append(dirs.Dirs, path)
}
return nil
})
if err != nil {
log.Println(err)
}
log.Println("Continuing Loop")
}
log.Println("Starting Dedup: ")
dirs.Dedup()
log.Println("Post Dedup: ")
for _, dir := range dirs.Dirs {
log.Println(dir)
}
dirs.Watch()
return nil
}
// Dedup ...Remove all duplicate entires from configured directories
func (dirs *WatchDirs) Dedup() {
uniqueSet := make(map[string]bool, len(dirs.Dirs))
for _, x := range dirs.Dirs {
uniqueSet[x] = true
}
result := make([]string, 0, len(uniqueSet))
for x := range uniqueSet {
result = append(result, x)
}
dirs.Dirs = result
}
// Watch ...Watch the list of created directories for changes
func (dirs *WatchDirs) Watch() {
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:
log.Println("event:", event)
if event.Op&fsnotify.Write == fsnotify.Write {
log.Println("modified file:", event.Name)
}
case err := <-watcher.Errors:
log.Println("error:", err)
}
}
}()
for _, dir := range dirs.Dirs {
log.Println("Watching dir: ", dir)
err = watcher.Add(dir)
if err != nil {
log.Println(err)
}
}
<-done
}
编辑:为了更清晰,添加了额外的函数。
英文:
I'm running into a strange problem with filepath.Walk() in go. It starts to run then reaches a point where it just hangs. My CPU sits at 100%, there's no error and it does not continue. I've looked into the directory that I'm traversing and the point where it hangs has nothing special, no symlins or anything like that. Just plain directories and files. Here's the code that I'm using:
type WatchDirs struct {
Dirs []string
}
//Watches ...Recursively walk the filesystem, entrypoint to file watching
func Watches(tops []string) error {
var dirs WatchDirs
for _, top := range tops {
err := filepath.Walk(top, func(path string, f os.FileInfo, err error) error {
if err != nil {
log.Println(err)
return err
}
log.Println("File: ", path)
if f.IsDir() {
//log.Println("Path: ", path)
dirs.Dirs = append(dirs.Dirs, path)
}
return nil
})
if err != nil {
log.Println(err)
}
log.Println("Continuing Loop")
}
log.Println("Starting Dedup: ")
dirs.Dedup()
log.Println("Post Dedup: ")
for _, dir := range dirs.Dirs {
log.Println(dir)
}
dirs.Watch()
return nil
}
//Dedup ...Remove all duplicate entires from configured directories
func (dirs *WatchDirs) Dedup() {
log.Println("deduping")
uniqueSet := make(map[string]bool, len(dirs.Dirs))
for _, x := range dirs.Dirs {
uniqueSet[x] = true
}
result := make([]string, 0, len(uniqueSet))
for x := range uniqueSet {
result = append(result, x)
}
dirs.Dirs = result
}
//Watch ...Watch the list of created directories for changes
func (dirs *WatchDirs) Watch() {
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:
log.Println("event:", event)
if event.Op&fsnotify.Write == fsnotify.Write {
log.Println("modified file:", event.Name)
}
case err := <-watcher.Errors:
log.Println("error:", err)
}
}
}()
for _, dir := range dirs.Dirs {
log.Println("Watching dir: ", dir)
err = watcher.Add(dir)
if err != nil {
log.Println(err)
}
}
<-done
}
EDIT: Added additional functions for clarity.
答案1
得分: 1
我不确定你的意图是什么,但是对我来说,整体的计划似乎是你想将目录的路径追加到一个切片中。你想检查是否有新的内容被添加,然后将新的目录添加到切片中。这是我简化版本的代码。每隔一秒钟,我使用信号来同步路径。我使用信号来表示:“嘿,我想停止监视变化并打印出所有的路径”...我还使用一个通道来监听来自标准输入的命令,以执行其他操作(你可以在此基础上构建其他功能)。
我无法确定你原始代码中的故障点确切在哪里。如果你正在监视变化,那么代码应该是阻塞的,除非你另有其他要求。
package main
import (
"bufio"
"io"
"log"
"os"
"os/signal"
"path/filepath"
"syscall"
"time"
)
type Watcher struct {
Dirs []DirInfo
Ticker *time.Ticker
Stop chan os.Signal
Command chan string
}
type DirInfo struct {
LastMod time.Time
Path string
}
func New() *Watcher {
return &Watcher{
Stop: make(chan os.Signal, 1),
Ticker: time.NewTicker(1 * time.Second),
Command: make(chan string),
}
}
func (w *Watcher) addPath(path string, f os.FileInfo) {
shouldAppend := true
for i, d := range w.Dirs {
if d.Path == path {
w.Dirs[i].LastMod = f.ModTime()
shouldAppend = false
break
}
}
if shouldAppend {
w.Dirs = append(w.Dirs, DirInfo{f.ModTime(), path})
}
}
func (w *Watcher) List() {
if len(w.Dirs) == 0 {
log.Println("Nothing to show")
return
}
for _, d := range w.Dirs {
log.Println(d.Path)
}
return
}
func (w *Watcher) isNew(path string, f os.FileInfo) bool {
for _, d := range w.Dirs {
if path == d.Path {
t := f.ModTime()
return t.After(d.LastMod)
}
}
return true
}
func (w *Watcher) Sync(tops []string) {
for _, top := range tops {
err := filepath.Walk(top, func(path string, f os.FileInfo, err error) error {
if err != nil {
return err
}
if f.IsDir() && w.isNew(path, f) {
w.addPath(path, f)
}
return nil
})
if err != nil {
log.Printf("Error %v\n", err)
}
}
}
func main() {
w := New()
tops := []string{}
signal.Notify(w.Stop, syscall.SIGINT, syscall.SIGTERM)
go func() {
s := bufio.NewScanner(os.Stdin)
for {
for s.Scan() {
w.Command <- s.Text()
}
}
}()
for {
select {
case <-w.Ticker.C:
w.Sync(tops)
case <-w.Stop:
log.Println("signalled to stop")
w.List()
os.Exit(0)
case cmd := <-w.Command:
switch cmd {
case "list":
w.List()
}
}
}
}
希望对你有帮助!
英文:
I'm not 100% sure of your intent.. However the grand scheme to me seems that you want to append the path of directories to a slice. You want to check to see if something new has been added and then add that new directory to the slice. Here's my simple version of that. Where every second defined by the ticker
I sync paths. I use signals to say, "hey, I want to stop watching for changes and print out all the paths".. I also use a channel to listen to commands from stdin to do other stuff (something you can build on for sure).
I couldn't tell exactly where in your original code the point of failure was. If your watching
for changes then the code should be blocking unless you say otherwise.
package main
import (
"bufio"
"io"
"log"
"os"
"os/signal"
"path/filepath"
"syscall"
"time"
)
type Watcher struct {
Dirs []DirInfo
Ticker *time.Ticker
Stop chan os.Signal
Command chan string
}
type DirInfo struct {
LastMod time.Time
Path string
}
func New() *Watcher {
return &Watcher{
Stop: make(chan os.Signal, 1),
Ticker: time.NewTicker(1 * time.Second),
Command: make(chan string),
}
}
func (w *Watcher) addPath(path string, f os.FileInfo) {
shouldAppend := true
for i, d := range w.Dirs {
if d.Path == path {
w.Dirs[i].LastMod = f.ModTime()
shouldAppend = false
break
}
}
if shouldAppend {
w.Dirs = append(w.Dirs, DirInfo{f.ModTime(), path})
}
}
func (w *Watcher) List() {
if len(w.Dirs) == 0 {
log.Println("Nothing to show")
return
}
for _, d := range w.Dirs {
log.Println(d.Path)
}
return
}
func (w *Watcher) isNew(path string, f os.FileInfo) bool {
for _, d := range w.Dirs {
if path == d.Path {
t := f.ModTime()
return t.After(d.LastMod)
}
}
return true
}
func (w *Watcher) Sync(tops []string) {
for _, top := range tops {
err := filepath.Walk(top, func(path string, f os.FileInfo, err error) error {
if err != nil {
return err
}
if f.IsDir() && w.isNew(path, f) {
w.addPath(path, f)
}
return nil
})
if err != nil {
log.Printf("Error %v\n", err)
}
}
}
func main() {
w := New()
tops := []string{}
signal.Notify(w.Stop, syscall.SIGINT, syscall.SIGTERM)
go func() {
s := bufio.NewScanner(os.Stdin)
for {
for s.Scan() {
w.Command <- s.Text()
}
}
}()
for {
select {
case <-w.Ticker.C:
w.Sync(tops)
case <-w.Stop:
log.Println("signalled to stop")
w.List()
os.Exit(0)
case cmd := <-w.Command:
switch cmd {
case "list":
w.List()
}
}
}
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论