🗑️

rmが怖いのでWindowsのゴミ箱にファイルを送るコマンドを作った

に公開

Git Bash for WindowsやMSYS2を使っているとき、ついrmコマンドでファイルを完全削除してしまい困ったことはありませんか?

わたしはたくさんあります。

  • プロジェクトの掃除中…
    gitにコミットする前の必要なファイルまで削除してしまった

  • もう使わないと思って削除したけど…
    後々必要になり復元したくなった

  • タイプミスで…
    rm * .logと*の後ろにスペースが入ってしまい、すべてが消え去ってしまった…

この記事で紹介するtrashコマンドはこれらの問題を解決します。

mvで退避させる方法と違い、Windows標準のゴミ箱で一元管理できるのが利点です。

本題の前に、上位互換ツールの紹介

trash-cliというNode.jsの完全上位互換ツールがあります。
Windowsのゴミ箱に対応しているツールはなさそうだなと思い作ったんですが、再度調べるとありました…

Node.jsの環境がある方はこちらの方が導入が簡単でLinuxやMacでも使用できて便利です。
Windows用の処理はC++で実装されておりおそらくより高速です。

コマンド作成方法

導入は3ステップで完了します。

ステップ1: PowerShellスクリプトを作成

以下のPowerShellスクリプトをファイルに保存します。
場所と名前は任意ですが、例として~/scripts/trash.ps1に保存します。

Add-Type -AssemblyName 'Microsoft.VisualBasic'

foreach ($item in $args) {
  try {
    if (Test-Path -Path $item -PathType Leaf) {
      [Microsoft.VisualBasic.FileIO.FileSystem]::DeleteFile($item, 'OnlyErrorDialogs', 'SendToRecycleBin')
    }
    elseif (Test-Path -Path $item -PathType Container) {
      [Microsoft.VisualBasic.FileIO.FileSystem]::DeleteDirectory($item, 'OnlyErrorDialogs', 'SendToRecycleBin')
    }
    else {
      Write-Warning "Path not found or unsupported type: $item"
    }
  }
  catch {
    Write-Error "Error: Failed to move '$item' to the Recycle Bin. Details: $($_.Exception.Message)"
  }
}

ステップ2: .bashrcに関数を追加

~/.bashrcに、以下のtrash関数を追加します。
関数3行目のlocal ps_file=...の部分を、ステップ1で保存した実際のパスに置き換えます。

~/.bashrc
# ゴミ箱にファイルを移動するコマンドを定義
trash() {
  # PowerShellスクリプトtrash.ps1のパスを設定
  # パスをダブルクォートで囲うと~が展開されずエラーになるので注意
  local ps_file=~/scripts/trash.ps1

  # 引数がなければ終了
  if [ $# -eq 0 ]; then
    echo "trash: Please specify files or directories." >&2
    return 1
  fi

  # PowerShellに渡すためwindows形式の絶対パスに変換
  local win_paths=()
  for item in "$@"; do
    if [ ! -e "$item" ]; then
      echo "trash: '$item': No such file or directory." >&2
      continue
    fi
    win_paths+=("$(cygpath -a -w "$item")")
  done

  # 有効なパスが一つもなければ終了
  if [ ${#win_paths[@]} -eq 0 ]; then
    return 1
  fi

  # PowerShellスクリプトを実行して、ファイルをゴミ箱に送る
  ps_file="$(cygpath -a -w "$ps_file")"
  powershell.exe -NoProfile -ExecutionPolicy Bypass -File "$ps_file" "${win_paths[@]}"
}

ステップ3: .bashrcを再読み込み

最後に、.bashrcを再読み込みしてtrashコマンドを有効化します。

$ source ~/.bashrc

これで完了です!

使い方

使い方はrmコマンド(オプションなし)とほぼ同じです。
trashに続けて、ごみ箱に送りたいファイルやディレクトリを指定するだけです。

ファイルやディレクトリをゴミ箱に送る

# 単一のファイルを削除
trash file.txt

# 複数のアイテムを一度に削除
trash my_folder/ file1.txt file2.log

ワイルドカードを使う

Bashのワイルドカードも利用できます。

# .tmpファイルをすべてごみ箱へ
trash *.tmp

# buildディレクトリの中身をすべてごみ箱へ
trash build/*

なぜPowerShellスクリプトが必要なのか

シェルスクリプトで完結させたかったのですが、ゴミ箱の仕様が不明確なためWindows標準のAPIを使用することにしました。

ゴミ箱は特殊なフォルダになっており、実際のフォルダパスはC:\$Recycle.Bin\<ユーザーのSID>になるようです。削除元のファイルに応じてドライブとSIDを適切に指定する必要があります。またゴミ箱の仕様は公開されていないようで、他にどのような制約があるかも不明なため、動作が保証される.NETの[Microsoft.VisualBasic.FileIO.FileSystem]::DeleteFileを使用することにしました。

rmをtrashのエイリアスにする運用の良し悪し

.bashrcにalias rm=trashを追記することで、rmコマンドでtrashコマンドを実行できます。
これは一見便利ですがデメリットもあります。

メリット

誤削除のリスクを軽減できます。初心者の利用時でも取り返しのつかない事態を回避できます。

デメリット

rmのつもりで使用することによる混乱を招きやすいです。複数人がアクセスするサーバー等では特に注意が必要です。

  • ストレージの圧迫
  • rmを含む既存スクリプトへの影響
  • パフォーマンスの低下
  • 真にrmコマンドを使いたいとき\rmとするなどひと手間必要

これらを考慮してわたしはエイリアスを設定していません。

Discussion