Golang Go语言中如何在移动文件?

import (
"os"
)

func main(){ err := os.Rename("/home/go/v2ex/user.conf", “/home/go/v2ex/bak/userX.20240731”) fmt.Println(err) }

  • /home/go/v2ex/userX.conf 为 文件
  • /home/go/v2ex/userX.20240731 为备份目录

问题

  • 同样的操作在 linux 下使用 mv 是正常如预期的操作(将文件移动到目录中)
  • 同样的操作在 go 下是 error: file exists ,go 应该调用的是 c 的 rename

复现链接: https://go.dev/play/p/GmHflbjn1uG


Golang Go语言中如何在移动文件?

更多关于Golang Go语言中如何在移动文件?的实战教程也可以访问 https://www.itying.com/category-94-b0.html

10 回复

首先, 其次, 然后你的代码改这个就可以了
err = os.Rename(“a.conf”, “a.bak/a.conf”)

更多关于Golang Go语言中如何在移动文件?的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


你理解 mv a.conf a.bak 和 mv a.conf a.bak/ 的区别, 就不会那样写代码了

可是无论 mv a.conf a.bak 和 mv a.conf a.bak/ 在 unix 下都是相同的表现啊? mv a.conf a.bak 就是把文件移动到目录中

mv 命令不单单是调用了 rename_at 这个 syscall ,还做了很多别的事情。比如跨 filesystem 的时候,rename_at 会失败,但是 mv 会复制过去再删掉原本的文件。

这个的确可以解决,用了这个方式,我就不需要用#替代.了

即使是 mv 这样看起来很简单的 command tool ,做的工作也比想象中得多,和 syscall 肯定不是一个粒度的,这个 case 里你用 strace 来跟踪下 mv 产生的 syscall 就知道了。

mv 先会尝试去直接 rename 到 dir 这个目录上,但是 syscall 报错 file exists
renameat2(AT_FDCWD, “test-file”, AT_FDCWD, “dir”, RENAME_NOREPLACE) = -1 EEXIST (File exists)

mv 应该特殊处理了这个错误,第二次会尝试 rename 到 dir/test-file
renameat2(AT_FDCWD, “test-file”, AT_FDCWD, “dir/test-file”, RENAME_NOREPLACE) = 0

如下语言内置库支持 move
https://docs.python.org/3/library/shutil.html
https://learn.microsoft.com/en-us/dotnet/api/system.io.file.move
https://docs.oracle.com/javase/tutorial/essential/io/move.html

如下语言不支持 move ,但是注释写的很明确
https://doc.rust-lang.org/std/fs/fn.rename.html
On Unix, if from is a directory, to must also be an (empty) directory. If from is not a directory, to must also be not a directory

这是 go 语言的注释
https://pkg.go.dev/os#Rename
Rename renames (moves) oldpath to newpath. If newpath already exists and is not a directory, Rename replaces it. OS-specific restrictions may apply when oldpath and newpath are in different directories. …

好像不需要了解太多原理……

单纯想告诉你,L:52 应该改为 err = os.Rename(“a.conf”, “a.bak/a.conf”)

在 Go 中,os.Rename() 函数是对 POSIX 标准中 rename() 系统调用的包装。虽然有些系统没有 rename()(比如 macOS ),但 os.Rename()仍然在尽力保持 rename() 包装的调用,为此会用一些当前系统支持的其他命令来达到。

所以它没办法单纯的被理解为 mv 指令。

在Golang中移动文件通常可以使用标准库中的os包来实现,虽然os包没有直接提供一个名为“移动文件”的函数,但可以通过重命名文件(如果源文件和目标文件位于同一文件系统)或使用组合操作(复制然后删除)来实现跨文件系统的文件移动。

方法一:使用os.Rename(适用于同一文件系统)

如果源文件和目标文件在同一个文件系统中,可以使用os.Rename函数直接重命名文件,这本质上就是移动文件。

package main

import (
    "fmt"
    "os"
)

func main() {
    src := "source.txt"
    dst := "destination.txt"
    err := os.Rename(src, dst)
    if err != nil {
        fmt.Println("Error renaming file:", err)
    } else {
        fmt.Println("File moved successfully")
    }
}

方法二:复制并删除(适用于跨文件系统)

如果源文件和目标文件在不同的文件系统中,需要先复制文件内容,然后删除源文件。这可以通过io/ioutil包中的ioutil.ReadFileioutil.WriteFile函数,或者更高效地通过os包的os.Openos.Create结合io.Copy来实现。

注意,无论哪种方法,都需要处理可能出现的错误,例如文件不存在、权限问题等。在实际开发中,根据需求选择合适的方法,并确保对错误进行适当的处理。

回到顶部