如何覆盖 Go 中的符号链接?

How to overwrite a symlink in Go?

我想使用 Go 覆盖符号链接,但我找不到如何操作。

如果我尝试创建符号链接但它已经存在,则会返回错误。

我的代码:

err := os.Symlink(filePath, symlinkPath)
if err != nil {
    fmt.Println(err)
}

我想符号链接必须删除然后重新创建。那正确吗?如果是这样,我如何取消链接符号链接?

只需检查符号链接是否存在并在创建新符号链接之前将其删除

if _, err := os.Lstat(symlinkPath); err == nil {
  os.Remove(symlinkPath)
}

请注意,@Vadyus 的回答在 运行ning lstat 时隐藏了实际的文件系统错误。例如,如果您的磁盘损坏并且 Lstat 失败,您仍然会 运行 os.Remove 并忽略它的错误(危险,除非您喜欢调试数小时)。

以下代码段正确检查文件是否存在和其他错误:

if _, err := os.Lstat(symlinkPath); err == nil {
  if err := os.Remove(symlinkPath); err != nil {
      return fmt.Errorf("failed to unlink: %+v", err)
  }
} else if os.IsNotExist(err) {
    return fmt.Errorf("failed to check symlink: %+v", err)
}

这里的其他答案都是正确的...但是有两个小问题:

  • 有一个微小的数据竞争,新的符号link将在此处之前但在删除之后的其他地方创建,使其处于可能不一致的状态。
  • 如果程序在创建 symlink 之前死掉/崩溃,但在删除前一个 sym 之后,它可能会再次使事情处于不一致的状态。

处理这个问题的更原子的方法是创建一个临时符号link,然后将其重命名为原始符号:

symlinkPathTmp := symlinkPath + ".tmp"
if err := os.Remove(symlinkPathTmp); err != nil && !os.IsNotExist(err) {
  return err
}

if err := os.Symlink(filePath, symlinkPathTmp); err != nil {
  return err
}

if err := os.Rename(symlinkPathTmp, symlinkPath); err != nil {
  return err
}

在删除临时 link 和重新创建它之间仍然存在一个小的竞争,但它不会冒险让主要的 link 处于不一致的状态。 理想情况下,我们可以通过为临时文件使用随机名称来解决这个问题 link,但是 Go 的 TempFile 总是创建一个新文件,所以它不太像有用。 (您可以调用 TempFile,然后删除文件名并重新使用该名称,这比仅附加常量 .tmp 后缀风险更大但仍然更安全。)

然而,即使在那个比赛中,你仍然获得原子性,任何中断都不会导致丢失 link。

请注意,这取决于 Posix 行为,可能不适用于 Windows(为什么你要在 Windows 上使用 symlinks?),但这是许多 macOS/Linux 需要原子符号 link 替换的工具共享的技术。