go语言 os.Rename() cannot move the file to a different disk drive 怎么办

时值我小病在家休养生息,喜欢跳广场舞的外公来寻求我的帮助,他们跳广场舞是将存有歌曲的U盘插到音响上面,而音响大部分都是只能显示歌曲的索引index,不能直接显示歌曲名,所以为了方便他们会在U盘里面对歌曲进行排序。由于音响是寻址按顺序播放,意思就是在U盘里面的歌曲需要一首一首的按顺序复制过去,而且当对U盘歌曲进行增添的时候又需要按照顺序重新复制一遍,可以说相当麻烦。为了将我从这重复的劳动中解放出来,我用go语言写了一个小工具,本来想着分分钟写完,却没想到踩到了坑。 在os包中有一个Rename()函数具有重命名和移动的功能,其函数如下:

func Rename(oldpath, newpath string) error

Rename修改一个文件的名字,移动一个文件。可能会有一些个操作系统特定的限制。

在windows系统下面使用该函数,oldpathnewpath在同一个磁盘/卷下面能正常使用,可我需要将音乐移动到U盘上,当使用这个函数的时候出现了:

The system cannot move the file to a different disk drive.

搜索Githubissues,发现Rename在windows中不能进行跨磁盘/卷操作。 为了实现跨磁盘/卷操作,一种方法是直接调用windows API,于是在windows api docs中搜索到movefileex()函数能够实现该功能,但是在go语言的syscall包中只有movefile()函数,其函数如下:

func MoveFile(from *uint16, to *uint16) (err error)

实现文件移动的函数可以写成:

func movefile(oldpath, newpath string) error { //跨卷移动
    from, err := syscall.UTF16PtrFromString(oldpath)
    if err != nil {
        return err
    }
    to, err := syscall.UTF16PtrFromString(newpath)
    if err != nil {
        return err
    }
    return syscall.MoveFile(from, to)//windows API

}

当我们读取oldpath目录里面的文件时调用io/ioutil包的ReadDir()函数可按照文件夹内的排序方式批量读入文件名,其函数实现如下:

func filenamelist(filepath string) []string {
    var list []string
    rd, err := ioutil.ReadDir(filepath) //遍历目录
    if err != nil {
        log.Fatal(err)
    }
    for _, fi := range rd {
        if fi.IsDir() {
            fmt.Printf("[%s]is dir\n", fi.Name())

        } else {
            list = append(list, fi.Name())
            fmt.Println(filepath + fi.Name())
        }
    }
    return list//返回一个string数组

}

当然也可以普通方式读入文件名然后用sort包进行排序。 下面是完整代码:

package main

import (
    "fmt"
    "io/ioutil"
    "log"
    "os"
    "syscall"
)

func movefile(oldpath, newpath string) error { //跨卷移动
    from, err := syscall.UTF16PtrFromString(oldpath)
    if err != nil {
        return err
    }
    to, err := syscall.UTF16PtrFromString(newpath)
    if err != nil {
        return err
    }
    return syscall.MoveFile(from, to)//windows API

}
func filenamelist(filepath string) []string {
    var list []string
    rd, err := ioutil.ReadDir(filepath) //遍历目录
    if err != nil {
        log.Fatal(err)
    }
    for _, fi := range rd {
        if fi.IsDir() {
            fmt.Printf("[%s]is dir\n", fi.Name())

        } else {
            list = append(list, fi.Name())
            fmt.Println(filepath + fi.Name())
        }
    }
    return list

}
func movefilelist(oldpath, newpath string) {
    for _, fi := range filenamelist(oldpath) { //移动目录的所有文件
        err := movefile(oldpath+fi, newpath+fi)
        if err != nil {
            log.Fatal(err)
        }
        fmt.Println(fi + "--Move To -->" + newpath + "--OK!")

    }
}
func main() {
    movefilelist(os.Args[1], os.Args[2])
}

最后在终端中:

Ryan@DESKTO MINGW64 /Go/test (master)
$ go run main.go ~/Desktop/music/ /d/
C:/Users/Ryan/Desktop/music/新建文本文档 (2).txt
C:/Users/Ryan/Desktop/music/新建文本文档.txt
新建文本文档 (2).txt--Move To -->D:/--OK!
新建文本文档.txt--Move To -->D:/--OK!

有了这个脚本,我今后终于可以愉快的帮助我外公了。

转载请注明

Avatar
Ryan
Self Driving Car Engineer
下一页