LoginSignup
0
0

[Go] ゴミ箱にファイルを移動するgo-trashを作ってみた

Posted at

この記事はゴミ箱について得た知見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)
	}
}

参考

  1. https://qiita.com/redh00k/items/1e56d8bfd9e4e011f61c

  2. https://qiita.com/redh00k/items/690c55cd4167582e4019

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0