如何在Go中编写安全重命名?(或者,如何在Go中编写这个Python?)

Ana*_*Ana 0 file exists go

我在Python中有以下代码:

    if not os.path.exists(src): sys.exit("Does not exist: %s" % src)
    if os.path.exists(dst): sys.exit("Already exists: %s" % dst)
    os.rename(src, dst)
Run Code Online (Sandbox Code Playgroud)

这个问题,我知道没有直接的方法来测试文件是否存在或不存在.

在Go中编写上述内容的正确方法是什么,包括打印出正确的错误字符串

这是我得到的最接近的:

package main

import "fmt"
import "os"

func main() {
    src := "a"
    dst := "b"
    e := os.Rename(src, dst)
    if e != nil {
        fmt.Println(e.(*os.LinkError).Op)
        fmt.Println(e.(*os.LinkError).Old)
        fmt.Println(e.(*os.LinkError).New)
        fmt.Println(e.(*os.LinkError).Err)
    }
}
Run Code Online (Sandbox Code Playgroud)

从错误信息的可用性来看,如果没有你解析英文自由格式字符串,它实际上没有告诉你问题是什么,在我看来,不可能在Go中写出等价物.

fuz*_*fuz 7

您提供的代码包含竞争条件:在您检查dst不存在和复制内容之间dst,第三方可能已创建该文件dst,导致您覆盖文件.请删除os.path.exists(dst)检查,因为在尝试删除目标时无法可靠地检测目标是否存在,或者使用以下算法:

  1. 创建硬链接srcdst.如果dst存在名为的文件,则操作将失败,您可以挽救.如果src不存在,操作也将失败.
  2. 删除src.

以下代码实现了Go中概述的两步算法.

import "os"

func renameAndCheck(src, dst string) error {
    err := os.Link(src, dst)
    if err != nil {
        return err
    }

    err = os.Remove(src)
    if err != nil {
        return err
    }
}
Run Code Online (Sandbox Code Playgroud)

您可以检查调用os.Link()失败的原因:

  • 如果错误满足os.IsNotExist(),则调用失败,因为src当时不存在os.Link()调用
  • 如果错误满足os.IsExist(),则调用失败,因为此时dst存在os.Link()调用
  • 如果错误满足os.IsPermission(),则调用失败,因为您没有足够的权限来创建硬链接

据我所知,其他原因(如文件系统不支持硬链接或创建srcdst处于不同的文件系统)不能可移植性测试.

  • @twotwotwo 实际上,在“rename()”系统调用出现之前,上面的算法是标准的。引入 `rename()` 是为了避免当您想用另一个文件原子覆盖一个文件时出现竞争条件。 (4认同)