GOLANG:遍历目录树并处理文件 -- err = 'no such file or directory'。

3
我正在编写一个程序来遍历目录树并为找到的每个文件创建数字签名(加盐哈希)。在测试过程中,我遇到了奇怪的行为——如果我给程序一个位于目录上方的根路径,程序可以遍历目录树并打印出文件名,但是如果我尝试打开文件以读取它的字节,则会收到“没有这样的文件或目录”的错误消息,这个错误发生在例程找到的文件上。不确定这是什么原因造成的。Walk()例程如何“看到”文件,而ioutil.ReadFile()则无法找到它?
示例代码:
// 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
}

简短的回答是fi.Name()只提供文件名,而不是路径。使用path进行绝对调用,其中包含文件的目录名和基本名称。 - DavidGamba
2个回答

5
尝试在walkFn内记录pathfullPath的值。在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)

    // check err here
}

通过这个更改,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
}

谢谢 - 我忘记了闭包。 - user2644113

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 is the path to the file.
        if err != nil {
            fmt.Println("Fail")
        }
    }
    return nil
}

Walk函数会避免通过..向上遍历文件树,因此您无需检查。考虑到绝对路径部分,我认为您也不需要它,但我不能保证;)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接