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

时间:2016-05-20 11:42:46

标签: go symlink

我想用Go覆盖一个符号链接,但我找不到怎么做。

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

我的代码:

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

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

3 个答案:

答案 0 :(得分:5)

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

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

答案 1 :(得分:2)

请注意,@ Vadyus的答案在运行lstat时隐藏了实际的文件系统错误。例如,如果磁盘损坏并且Lstat发生故障,您仍将运行os.Remove并忽略其错误(“ DANGEROUS”,除非您想调试几个小时才能看到)。

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

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)
}

答案 2 :(得分:0)

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

  • 有一个很小的数据争用,其中新的符号链接将在此处之前但在删除之后的其他位置创建,从而使其处于潜在的不一致状态。
  • 如果该程序在创建符号链接之前但在删除前一个链接时死掉/崩溃,则可能再次使内容处于不一致状态。

更原子的方法是创建一个临时符号链接,然后将其重命名为原始符号链接:

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
}

在删除临时链接与重新创建临时链接之间仍然存在一个小小的竞争,但是这不会冒使主要链接处于不一致状态的风险。 理想地,我们可以通过使用临时链接的临时名称来解决此问题,但是Go的TempFile总是创建一个新文件,因此它并不是那么有用。 (您也许可以调用TempFile,然后删除文件名并重新使用该名称,这样做会比较冒险,但更安全,然后附加一个常量.tmp后缀。)

尽管如此,即使在种族竞赛中,您仍然会获得原子性,任何中断都不会导致链接丢失。

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