ShellScript
Zsh
Linux

rmコマンドが怖いのでゴミ箱に移動する関数を作る

はじめに

 わたしはubuntuを使用している際に、ホームディレクトリを誤って2回削除したことがあります。1回目は作業している際に、なぜか ~ という名前のファイルが作成されたので何も考えずに削除しました。私のホームディレクトリは電子の藻屑となりました。そのときにrmコマンドは怖いものだと認識しました。そしてrmコマンドを封印しようし.bashrcにalias ls='rm -rf ~'を追加しました。その当時の私はLinuxを使い初めて間もなかったので、aliasの使い方がよくわかっていませんでした。程なくして2回目の虚無が訪れます。

 こんな悲惨なことはもう3度と起こしたくないので、私はrmコマンドと距離を取りました。必要のないものは、mvコマンドで適当なディレクトリに移動するようになりました。そして、万が一のためにバックアップをとるようになりました。ubuntuに標準で入っているバックアップ機能で別のファイルシステムにバックアップをとったり、google driveをmountして重要なファイルはln -sで共有するようになりました。

 これらは1年前の出来事です。今ではlinuxにも慣れてきたので、ゴミ箱に送る関数を作成して1ヶ月後に自動で消え去るようにしています。

関数の作成

今は利便性を求めてbashからzshにubuntuからarch linuxに移行しています。
zsh --version zsh 5.4.2 (x86_64-unknown-linux-gnu)
cat /etc/os-release | sed '/^PRETTY_NAME/!d;s/.*"\(.*\)".*/\1/' Arch Linux
uname -r 4.14.15-1-ARCH
以下に関数を載せています。

function mt() {
  local trash="${HOME}/.Trash"
  local fzf="fzf --preview='less ${trash}/{}' --preview-window='right:hidden' --bind='ctrl-v:toggle-preview'"
  [[ -s ${trash} ]] || mkdir ${trash}
  local size=$(du -s ${trash} | cut -f1)

  # ゴミ箱から拾う
  if [[ $1 == '-r' ]];then
    type fzf > /dev/null 2>&1 || return
    ls -r ${trash} | eval "${fzf} --header='move files in the trash to the current directory / ${size}'" | xargs -I{} sh -c "mv \"${trash}/{}\"  \"./\$(echo {} | cut -d_ -f3-)\""
  # 焼却(緊急性がある際にしか絶対に使わない)
  elif [[ $1 == '-d' ]];then
    type fzf > /dev/null 2>&1 || return
    ls -r ${trash} | eval "${fzf} --header='delete files in the trash / ${size}'" | xargs -p -I{} rm -r "${trash}/{}"
  # ゴミ箱へ投げる
  else
    [[ $# -eq 0 ]] && type fzf > /dev/null 2>&1 && \
      set $(ls -A ./ | sed "/^${trash##*/}$/"d | eval "${fzf} --header='move files in the current directory to the trash / ${size}'") \
      > /dev/null && [[ $# -eq 0 ]] && return
    echo $@ | tr -d '\n' |  xargs -d' ' -I{} sh -c "mv {} \"${trash}/\$(date +%F_%T_)\$(basename {})\""
  fi
}

function _mt() {
  _files
}

 下記に記載しているファイルというのはディレクトリの意も含みます。

 基本的に選択的インターフェース(pecoなど)のfzfを使うようにしてます。ゴミ箱への移動はfzfがなくてもできます。ファイル名は上書きされないように日時を先頭にくっつけています。fzfのヘッダーに.Trashのデータ量を表示しています。

 まず最初にホームディレクトリに.Trashディレクトリがないか確認して、存在しなければ作成します。引数にファイルをとり.Trashディレクトリに移動します。引数を指定しない場合はカレントディレクトリから選択します。-rでゴミ箱からカレントディレクトリにファイルを移動します。-dで.Trashから完全削除します。

ゴミ箱に移動してから1ヶ月後に削除

 今は必要ないと考えても後々必要になるファイルがたまにあったりします。ですが、1ヶ月経つとさすがに必要ないと思うので自動で削除するようにします。なので、自分でファイルを削除するということは基本的にしません。

 以下のコードを.zloginに記載してログイン時にファイルを削除するようにしています。

() {
  local trash="${HOME}/.Trash"
  [[ ! -s ${trash} ]] && return
  for file in $(ls ${trash});do
    stat --format=%Z "${trash}/${file}" | \ # ファイルの日時を取得
    xargs -I{} test {} -le $(date --date '1 month ago' +%s) \ # 比較
    && rm -fr "${trash:-abcdef}/${file:-abcdef}" # 削除
  done
}

rmコマンドを使うのは怖いですが、ファイルは削除しないと溜まっていく一方なので、仕方なくここではrmコマンドを使っています。本当は-fオプションをつけたくないのですが、スクリプト実行時に削除の確認されることが稀にあるので面倒だからつけています。rmコマンド実行時の引数ですが\${trash}と\${file}がnullの際に/になります。ありえないことですが、これは相当怖いので未定義やnullの際にディレクトリやファイル名がabcdefになるようにしています。