この記事はゴミ箱について得た知見1 2を基に、ファイルをゴミ箱に移動するgo-trash
を作ってみました。
ゴミ箱にファイルやディレクトリを移動
Linux
ゴミ箱に移動させたいファイルを$HOME/.local/share/files
に移動させることと、$HOME/.local/share/info
に<移動させたいファイル名>.trashinfo
ファイルを用意する必要があるだけです。
気を付けないといけないのは、ファイルやディレクトリをゴミ箱に移動させる前に$trash/info
に対応するファイルを作成しなければならないことです。
実装は以下の通りです。こちらはオプション等を一切考えずファイル/ディレクトリをゴミ箱に移動させる機能だけ実装してあります。
package main
import (
"fmt"
"os"
"os/user"
"path/filepath"
"strings"
"time"
)
// ~/.local/share/Trash/info/
type Info struct {
path string
deletionDate time.Time
}
// trashinfoの文字列作成
func convertTrashInfo(i Info) string {
return fmt.Sprintf("[Trash Info]\nPath=%s\nDeletionDate=%s\n", i.path, i.deletionDate.UTC().Format(time.RFC3339))
}
func moveToTrashBox(path string) (err error) {
filename := filepath.Base(path)
abs, err := filepath.Abs(path)
if err != nil {
fmt.Errorf("Failure to get absolute representation of path: %s", err)
return err
}
info := Info{abs, time.Now()}
user, err := user.Current()
if err != nil {
fmt.Errorf("Failure to get user's home directory: %s", err)
return err
}
// 絶対パスに変更
trashBase := strings.Replace("~/.local/share/Trash", "~", user.HomeDir, 1)
err = os.WriteFile(trashBase+"/info/"+filename+".trashinfo", []byte(convertTrashInfo(info)), os.ModePerm)
if err != nil {
fmt.Errorf("Failure to writeFile source file: %s", err)
return err
}
err = os.Rename(path, trashBase+"/files/"+filename)
if err != nil {
fmt.Errorf("Failure to rename source file: %s", err)
return err
}
return
}
func main() {
// カレントディレクトリのtestをゴミ箱に移動
err := moveToTrashBox("test")
if err != nil {
fmt.Println("go-trash: cannot move to trashbox: ", err)
}
}
Windows
Windowsの場合はSHFileOperationというWindowsAPIを叩く必要があります。
このAPIを実行するためにSHFILEOPSTRUCT構造体やいくつかの定数を定義してあります。
実装は以下の通りです。こちらもオプション等を一切考えずファイル/ディレクトリをゴミ箱に移動させる機能だけ実装してあります。
package main
import (
"fmt"
"syscall"
"unsafe"
"golang.org/x/sys/windows"
)
// https://learn.microsoft.com/en-us/windows/win32/api/shellapi/nf-shellapi-shfileoperationw
var (
modshell32 = syscall.NewLazyDLL("Shell32.dll")
procSHFileOperationW = modshell32.NewProc("SHFileOperationW")
)
// https://learn.microsoft.com/en-us/windows/win32/api/shellapi/ns-shellapi-shfileopstructa
type SHFILEOPSTRUCT struct {
Hwnd uintptr
Func uint32
From *uint16
To *uint16
Flags uint16
AnyOperationsAborted int32
NameMappings *byte
ProgressTitle *uint16
}
func _SHFileOperation(
shFileOp *SHFILEOPSTRUCT,
) (r1 uintptr, err error) {
r1, _, err = procSHFileOperationW.Call(
uintptr(unsafe.Pointer(shFileOp)),
)
return
}
func moveToTrashBox(path string) (ret uintptr, err error) {
// https://pinvoke.net/default.aspx/Enums/FileFuncFlags.html
FO_DELETE := 0x3
// https://www.pinvoke.net/default.aspx/Enums/FILEOP_FLAGS.html
// https://groups.google.com/g/microsoft.public.vb.winapi/c/htqEx2zQjGo
FOF_SILENT := 0x4
FOF_NOCONFIRMATION := 0x10
FOF_ALLOWUNDO := 0x40
var fileOp SHFILEOPSTRUCT
fileOp.Hwnd = uintptr(0)
fileOp.Func = uint32(FO_DELETE)
fileOp.From = windows.StringToUTF16Ptr(path)
fileOp.Flags = uint16(FOF_SILENT | FOF_ALLOWUNDO | FOF_NOCONFIRMATION)
ret, err = _SHFileOperation(&fileOp)
return
}
func main() {
// カレントディレクトリのtestをゴミ箱に移動
ret, err := moveToTrashBox("test")
if err != nil {
fmt.Println("go-trash: cannot move to trashbox: ", ret, err)
}
}