英文:
GOLANG: Walk Directory Tree and Process Files -- err = 'no such file or directory
问题
我正在编写一个遍历目录树并为找到的每个文件创建数字签名(加盐哈希)的例程。在测试时,我遇到了奇怪的行为 - 如果我给程序一个位于目录上方的根路径,程序可以遍历树并打印出文件名,但如果我尝试打开文件以读取其字节,则会收到“没有此文件或目录”的错误消息,而这个文件是例程找到的 - 不确定出了什么问题。Walk()例程如何“看到”文件,但ioutil.ReadFile()却找不到它?
示例代码:
// 从树的上层开始,比如$HOME
func doHashWalk(dirPath string) {
err := filepath.Walk(dirPath, walkFn)
// 在这里检查错误
}
func walkFn(path string, fi os.FileInfo, err error) (e error) {
if !fi.IsDir() {
// 如果第一个字符是“。”,则跳过它,因为它是隐藏文件
if strings.HasPrefix(fi.Name(), ".") {
return nil
}
// 读取文件字节->获取绝对路径
fullPath, err := filepath.Abs(path)
if err != nil {
log.Printf("Abs Err: %s", err)
}
// 这总是失败并显示错误
bytes, err := ioutil.ReadFile(fullPath) // <-- (fi.Name()也不起作用)
if err != nil {
log.Printf("Err: %s, Bytes: %d", err, len(bytes))
}
// 创建加盐哈希
...
}
return nil
}
英文:
I'm writing a routine to walk a directory tree and create a digital signature (salted-hash) for each file I find. When testing it I get this weird behavior - if I give the program a root path "above" the directory, the program can walk the tree and print out the file names, but if I try and open the file to read it's bytes, I get the error message "no such file or directory" on the file that the routines found - not sure what gives here. How can the Walk() routine "see" the file, but ioutil.ReadFile() not be able to find it?
Sample Code:
// start with path higher up the tree, say $HOME
func doHashWalk(dirPath string) {
err := filepath.Walk(dirPath, walkFn)
// Check err here
}
func walkFn(path string, fi os.FileInfo, err error) (e error) {
if !fi.IsDir() {
// if the first character is a ".", then skip it as it's a hidden file
if strings.HasPrefix(fi.Name(), ".") {
return nil
}
// read in the file bytes -> get the absolute path
fullPath, err := filepath.Abs(fi.Name())
if err != nil {
log.Printf("Abs Err: %s", err)
}
// THIS ALWAYS FAILED WITH ERROR
bytes, err := ioutil.ReadFile(fullPath) // <-- (fi.Name() also doesn't work)
if err != nil {
log.Printf("Err: %s, Bytes: %d", err, len(bytes))
}
// create the salted hash
...
}
return nil
}
答案1
得分: 5
尝试在walkFn
函数内部记录path
和fullPath
的值。
在walkFn
函数内部使用filepath.Abs()
无法得到你想要的结果:它会将文件名解析为相对于当前工作目录的路径,而不是原始的dirPath
。
一种选择是在doHashWalk
函数中提前将目标目录解析为绝对路径:
func doHashWalk(dirPath string) {
fullPath, err := filepath.Abs(dirPath)
if err != nil {
log.Println("path error:", err)
return
}
err = filepath.Walk(fullPath, walkFn)
// 在这里检查错误
}
通过这个改变,walkFn
回调函数将始终接收到一个完全合格的path
参数;不需要再次调用filepath.Abs()
:
func walkFn(path string, fi os.FileInfo, err error) (e error) {
// ...
bytes, err := ioutil.ReadFile(path)
// ...
}
如果对于你的应用程序来说,查看每个文件相对于原始的dirPath
根目录的路径很重要,你可以通过闭包将该路径传递给walkFn
回调函数:
func doHashWalk(dirPath string) error {
fullPath, err := filepath.Abs(dirPath)
if err != nil {
return err
}
callback := func(path string, fi os.FileInfo, err error) error {
return hashFile(fullPath, path, fi, err)
}
return filepath.Walk(fullPath, callback)
}
func hashFile(root string, path string, fi os.FileInfo, err error) error {
if fi.IsDir() {
return nil
}
rel, err := filepath.Rel(root, path)
if err != nil {
return err
}
log.Println("hash rel:", rel, "abs:", path)
return nil
}
以上是翻译好的内容,请确认是否满意。
英文:
Try logging the values of path
vs. fullPath
inside of walkFn
.
Using filepath.Abs()
inside of walkFn
does not give the result you want: it's resolving a filename relative to the current working directory, instead of the original dirPath.
One option is to resolve the target directory to an absolute path up-front in doHashWalk
:
func doHashWalk(dirPath string) {
fullPath, err := filepath.Abs(dirPath)
if err != nil {
log.Println("path error:", err)
return
}
err = filepath.Walk(fullPath, walkFn)
// check err here
}
With that change, the walkFn
callback will always receive a fully-qualified path
argument; no need to call filepath.Abs()
again:
func walkFn(path string, fi os.FileInfo, err error) (e error) {
// ...
bytes, err := ioutil.ReadFile(path)
// ...
}
If it's important for your application to see the path of each file relative to the original dirPath
root, you can sneak that path into the walkFn
callback via a closure:
func doHashWalk(dirPath string) error {
fullPath, err := filepath.Abs(dirPath)
if err != nil {
return err
}
callback := func(path string, fi os.FileInfo, err error) error {
return hashFile(fullPath, path, fi, err)
}
return filepath.Walk(fullPath, callback)
}
func hashFile(root string, path string, fi os.FileInfo, err error) error {
if fi.IsDir() {
return nil
}
rel, err := filepath.Rel(root, path)
if err != nil {
return err
}
log.Println("hash rel:", rel, "abs:", path)
return nil
}
答案2
得分: 0
问题在于你没有正确使用filepath.Walk
函数。
你已经从path
参数中获取了文件的路径。
文档可能并不是非常清楚,但它们确实对filepath.Walk
函数有以下说明:
Walk函数遍历以root为根的文件树,对树中的每个文件或目录调用walkFn,包括root本身。
所以你可以将walkFn
简化为以下形式:
func walkFn(path string, fi os.FileInfo, err error) (e error) {
if !fi.IsDir() {
bytes, err := ioutil.ReadFile(path) // path是文件的路径。
if err != nil {
fmt.Println("Fail")
}
}
return nil
}
Walk
函数会处理不通过..
向上遍历文件树的情况,所以你不需要检查那个。至于绝对路径部分,我认为你也不需要,但我不能保证。
英文:
The problems is that you're not using the filepath.Walk
exactly the way it's intended.
You allready get the path to the file from the path
parameter.
The docs might not exactly be abundantly clear but they do say this for filepath.Walk:
> Walk walks the file tree rooted at root, calling walkFn for each file or directory in the tree, including root.
So you could shorten walkFn
to something like this:
func walkFn(path string, fi os.FileInfo, err error) (e error) {
if !fi.IsDir() {
bytes, err := ioutil.ReadFile(path) // path is the path to the file.
if err != nil {
fmt.Println("Fail")
}
}
return nil
}
Walk will take care of not walking up the file-tree via ..
there's no need for you to check for that. Considering the absolute path part I don't really think you need that either, but I make no promises
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论