V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
The Go Programming Language
http://golang.org/
Go Playground
Go Projects
Revel Web Framework
qW7bo2FbzbC0
V2EX  ›  Go 编程语言

如何在 go 移动文件?

  •  
  •   qW7bo2FbzbC0 · 49 天前 · 1927 次点击
    这是一个创建于 49 天前的主题,其中的信息可能已经有所发展或是发生改变。
    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

    9 条回复    2024-08-01 16:27:51 +08:00
    seth19960929
        1
    seth19960929  
       49 天前
    首先, 其次, 然后你的代码改这个就可以了
    err = os.Rename("a.conf", "a.bak/a.conf")
    seth19960929
        2
    seth19960929  
       49 天前
    你理解 mv a.conf a.bak 和 mv a.conf a.bak/ 的区别, 就不会那样写代码了
    qW7bo2FbzbC0
        3
    qW7bo2FbzbC0  
    OP
       49 天前
    @seth19960929 可是无论 mv a.conf a.bak 和 mv a.conf a.bak/ 在 unix 下都是相同的表现啊? mv a.conf a.bak 就是把文件移动到目录中
    Reficul
        4
    Reficul  
       49 天前
    mv 命令不单单是调用了 rename_at 这个 syscall ,还做了很多别的事情。比如跨 filesystem 的时候,rename_at 会失败,但是 mv 会复制过去再删掉原本的文件。
    qW7bo2FbzbC0
        5
    qW7bo2FbzbC0  
    OP
       49 天前
    @seth19960929 这个的确可以解决,用了这个方式,我就不需要用#替代.了
    Kumo31
        6
    Kumo31  
       49 天前   ❤️ 2
    即使是 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
    qW7bo2FbzbC0
        7
    qW7bo2FbzbC0  
    OP
       49 天前
    如下语言内置库支持 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. .....
    konakona
        8
    konakona  
       49 天前
    好像不需要了解太多原理……

    单纯想告诉你,L:52 应该改为 err = os.Rename("a.conf", "a.bak/a.conf")
    konakona
        9
    konakona  
       49 天前
    在 Go 中,os.Rename() 函数是对 POSIX 标准中 rename() 系统调用的包装。虽然有些系统没有 rename()(比如 macOS ),但 os.Rename()仍然在尽力保持 rename() 包装的调用,为此会用一些当前系统支持的其他命令来达到。

    所以它没办法单纯的被理解为 mv 指令。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1078 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 23ms · UTC 22:36 · PVG 06:36 · LAX 15:36 · JFK 18:36
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.