rcmdnk's blog
Last update

20130423_trash_200_200

rmコマンドを使っていてなんか時間がかかるな、と思ったら 変数が空だったり($a/$b/)、 引数の中に無駄なスペースがあったり(~/*.log~/* .log)して うっかり重要なファイルやディレクトリを rmしてしまうことは1度や2度は経験していると思います。

最近下の参照先の流れを見て、最初は自分のゴミ箱スクリプトを もうちょっとマシにアップデートしよう、と思ったのですが、 rm関連の事でも調べたら改めて知ったこともいくつかあったので その辺をまとめておこうと思います。 基本的にbashでの話です。

rmコマンドを安全にする

rm -i

rm-iオプションを加えると消去する時に1つ1つ消すかどうか確認しながら 消せる様になるわけですが、通常デフォルトでは確認する様になっていないので、 上記リンクの2番めにもある様に、

alias rm="rm -i"

などと.bashrc等に書いておけば-fを与えない限り確認する様に出来ます。

ただ、後から-fを与えればそれが優先されるので、同記事内にあるような

rm -rf ~/

は防げません。これを防ぎたいなら

function rm { command rm $@ -i;}

の様に関数でラッパーを作ってやれば常に-iを有効に出来ます。

逆に、この様にしてしまうと-fを与えても意味が無いので、 確認無しで消すには/bin/rm acommand rm aなどとして 元のrmを直接呼び出す必要があります。

rm –preserve-root

--preserve-rootオプションは/を引数として使用できなくします。

参照

これは環境にようってデフォルトでそうなっていたり そうでなかったりするみたいです。

root状態やsudoでのみ関係あるところなので、 コマンドラインから作業する時はこれ抜きでも慎重になるべきですが、 結構簡単なことでも、

rm -rf $abc/$def

としたつもりが

rm -rf $abv/$deg

等タイポしたりそもそも変数が空(未定義)だったりすると大変な事になるので、 これくらいはaliasに(rootのものにも)入れておいても(デフォルトでなければ) 良いかと思います。

set -u

未定義変数を絶対に使わない、と言う状況であれば

set -u

をしておくと、タイポで未定義の変数を使おうとした時に エラーを吐く様になるので、rootで限られた作業しかしない場合や そこで使うスクリプト内ではset -uをしておくと安全です。

${VAR:?}

set -uだと、もし$abcが一時的に空文字になる可能性がある時は それを検出することは出来ません。

また、シェルスクリプトの特性上、敢えて変数が未定義な状態と空文字な状態と 同等に扱ってコードを書いている事もあるので set -uはあまり良い選択肢ではないかもしれません。 (逆にその辺をきちんと考えてset -uしても問題ないコードの方が安全だ、という 人もいるかもしれませんが。)

いずれにせよset -uだとまだ危ない部分がありますが、 これを避けるためには

rm -rf ${abc:?}/$def

としておくとabcが未定義もしくは空文字の時にエラーになりそこで終了します。

SC2115 · koalaman/shellcheck Wiki

これであればset -uと違いrm -rfに関する部分だけ のチェックになりますし定義済みの空文字にも対応できます。

ホームをプロテクトする1

寸前にcdしてしまい気づかないままふとrm -rf *をしてしまったり、 上の場合同様変数が空だったり、引数を書いてる途中でスペースが 入ってしまって*~が独立してしまって間違って展開されて 恐ろしい事になったことは、誰しもが経験し得ることかと思います。

Linuxを初めて使い始めた頃に、1回やりかけて恐ろしくなって プロテクトするようなラッパーを使っていました。 冗長過ぎる感が強かったので、結局すぐに消してしまって どんなだったか覚えてなかったのですが、 改めて書いてみました。

rm_wrapper.sh
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#alias rm="rm -i"
#function rm={ rm $@ -i; }

function rm {
for f in "$@";do
  if [[ "$f" =~ ^- ]];then
    continue
  fi
  if [ "$f" = "$HOME" ] || [ "$f" = "$HOME/" ];then
    echo -n 'Are you sure to remove your HOME? '
    read yes
    if ! [[ "$yes" =~ ^[yY] ]];then
      return 0
    fi
  elif [ -d $f ];then
    dn="$(cd "$(dirname $f)";pwd -P)"
    if [ "$dn" = "$HOME" ] || [ "$dn" = "$HOME/" ];then
      echo -n 'Are you sure to remove your HOME? '
      read yes
      if ! [[ "$yes" =~ ^[yY] ]];then
        return 0
      fi
    fi
  fi
done
command rm --preserve-root $@
#command rm -i --preserve-root $@
#command rm --preserve-root $@ -i

}

ホームディレクトリそのものを消そうとした時や ホームディレクトリ下にあるディレクトリを消そうとした時に rmコマンドの前に別に1回確認を取ってくれる様に しています。

といくつか簡単にrmにラッパーをかける方法を書きましたが、 特に最初の2つの様な-iを要求する変更は スクリプト内で、.bashrc等設定を読み込んだ状態で rmを素のまま使っていると、 スクリプトがそこで止まるので、cronジョブで使ってるものなど 影響が出る可能性があるので気をつけてください。

最後の物は全ての引数をチェックしているので 引数が多いと結構邪魔くさいです。

(現在は上の様な設定はせずにrmは素のrmのまま使っています。)

ホームをプロテクトする2

ホームでのrm -rf *を予防する方法としてホームで見える ファイルやディレクトリを全てシンボリックリンクにしてしまう、 と言う方法もあります。

~/usrという様なディレクトリがある場合、~/.usr~/.home/usrと言った.で始まるディレクトリ内に 実体を作ってホームにはそこからシンボリックリンクを貼っておくと、 消されるファイルはシンボリックリンクだけなので被害を 最小限に抑えられます。

$ cd ~
$ mkdir ./.home
$ mv ./usr ./.home/usr
$ ln -s ./.home/usr ./usr

この方法ではrm -rf ~/とディレクトリを指定された場合は防げませんが、 rmコマンド自体はそのままなので他への影響を 最小限に抑えられます。

ただ、環境によっては$HOMEに沢山シンボリックリンクがあると 余計な負荷をかける事もあるので注意して下さい。

その他見つけたもの

上でやっているような事も含めて色々な物がweb上に転がっていると思いますが、 今回ちょっと調べてるうちに、 割とよさそうなものとしてsafe-rm: http://www.safe-rm.org.nz/ というものを見つけました。

perlで書かれていて、消したくないディレクトリを指定しておいて 消せないようにする、と言うものですが、 設定ファイルに書くことで簡単にディレクトリが追加出来るのが 良い感じです。

コマンドラインでゴミ箱を使う

以上は直接ファイルやディレクトリを消してしまう rmをする時に如何に気をつけるか、という話でした。

一方で、パソコンを使い始めて凄い便利だな、と思うことの1つに 当たり前のことですが簡単に元に戻す事が出来る事があると思います。 (もうちょい前から始めてる人はそれは当たり前のことではない、と言うかもしれませんが…) ゴミ箱に捨てても後から簡単に戻す事が出来る事もその1つ。

その様なゴミ箱に慣れてる状態で、初めて黒い画面でrmを使うと そのファイルが簡単に元に戻せない、ということに軽く恐怖を覚えます。

そういう物だと慣れれば、rmコマンドを使う時に ちゃんと考えてから消すようになるわけですが、 今でもたまにちょっとしたファイルを消して暫くした後に 消したファイルを確認出来れば便利だな、と思うことがあります。

そこで、これまではファイルを消す代わりに 適当なゴミ箱ディレクトリに移す簡単なスクリプトや関数を 使っていました。

今回、アップデートしてコマンドラインから元に戻せる様にもしたので 載せておきます。

trash.sh

インストール

スクリプトはGitHubにあります:

wget https://raw.github.com/rcmdnk/trash/master/bin/trash

するか、trashフォルダ毎

git clone [email protected]:rcmdnk/trash.git

してスクリプトbin/trashを取ってください。

インストールはこのスクリプトを適当なPATHの通ったディレクトリに入れるなり aliasで使える様にしてください。

自分の環境ではcloneしたディレクトリから~/usr/bin(PATHが通っている) にリンクを張り(リンク先では.shは除いてます)、 .bashrc内でalias del="trash"としています。 (元からスクリプト名をdelにしても良いわけですがなんとなく…)

rmを変更しても良いと思いますが、上にも書いたようにあまり rm自体を変更するのは好きではないので別コマンドにしています。

使い方

rmと同じ様なオプションを使える様にしてあります。 詳しくはtrash.sh -hやスクリプトを御覧ください。

例1:

$ cd tmp/
$ ls
$ touch a
$ trash.sh a
~/tmp/a was moved to ~/.Trash/my_trash_box/20130423/a
$ touch a
$ trash.sh a
~/tmp/a was moved to ~/.Trash/my_trash_box/20130423/a.1
$ ls
$ trash.sh -b
  2 20130423-03-24-29,~/tmp/a
  1 20130423-03-24-37,~/tmp/a
choose trash number: 1
 ~/tmp/a was restored from ~/.Trash/my_trash_box/20130423/a.1
$ ls
a
$ trash.sh -l
  1 20130423-03-24-29,~/tmp/a
$

消去するファイルはゴミ箱(~/.Trash/my_trash_box)下の 日付ごとのディレクトリに移動されます。

もし、同じファイル名の物が削除されると、数字を付けて区別します。

-bオプションを使うことで一覧を表示させ、その中から 番号を選択することで該当物を元に戻すことが出来ます。

-lオプションは削除物をリストするだけのコマンドです。

これ以外に-r(ディレクトリ削除)、-f(確認しない、デフォルト)、 -i(確認を行う)というrm同様のオプションがあります。

また、trash.sh -cとすると、下のオプションで設定する ゴミ箱容量を超えた分を古い物から消していきます。 ゴミ箱ディレクトリを自動的に削除されないディレクトリに指定している場合、 cronなどでtrash.sh -cを定期的に行う様にしてください。 Macの~/.Trashに指定したり、/tmp以下に作る様な場合は 余程大きな容量に指定しない以上は必要無いかもしれません。

trash.sh -C(大文字のC)とすると、ゴミ箱を全て空にします。

注意として、この様に直接ゴミ箱内に移動されたものは Finderからゴミ箱を開いた場合も表示されますが、 Finderから元に戻す事はできません。 右クリックしても元に戻すの項目はでません。

Finderから直接他へ移動させる事は可能ですが、 コマンドラインからtrash.shを使えば簡単に元に戻せます。

オプション

設定変数は以下の通りです。適時.bashrcなどで設定してください。

変数 説明
TRASHLIST ゴミ箱に移動したファイル/ディレクトリのリスト用ファイル。デフォルトは~/.trashlist
TRASHBOX ゴミ箱親ディレクトリ。デフォルトは~/.Trash(Macの通常のゴミ箱ディレクトリ)。このディレクトリの下にmy_trash_boxというディレクトリを作り、さらに日毎のディレクトリを作りその下に削除された物が移動される。
MAXTRASHBOXSIZE ゴミ箱の容量(MB)。デフォルトは1024。この容量を超えた状態でクリーンアップコマンドを使うとこの容量以下になるように古い順にゴミが削除される。
MAXTRASHSIZE このサイズを超えるファイル/フォルダは直接rmで削除する。

デフォルトの設定にあるように、ゴミ箱の容量は比較的小さめ(1GB) に設定してあります。 さらに~100MBを超える様なファイルの場合には直接rmする様に設定しています。

基本的にはrmを使い、 ちょっとした設定ファイルやスクリプトなど、取り敢えず要らないけど もしかしたら後で見るかも、と思うな物を上で書いたalias delを使って 削除する、と言った形を取っています。

その辺りは好み次第かと。

webで見つけられるその他のゴミ箱実装

Mac, AppleScriptを使った方法

OSX - mv2trashというスクリプト書いた にあるように、MacではAppleScriptの力を借りると 通常のゴミ箱に捨てる作業と同じ様にコマンドラインで 削除することが出来る様になります。

コマンドラインからゴミ箱に捨てるシェルスクリプトにはシェルスクリプトを使って書いているものがありました。

Macの場合は上の trash に削除部分でmvの代わりに使っても良いかな、 とも思ったのですが、2つ目の参照先にもある様にかなり遅くなるので、 頻繁に使うには余り実用的で無い感じがしました。

Mac, rmtrash

mac用の他のものとして、rmtrash という物を良く見かけました。

HomebrewやMacPortsでインストール出来るところから 多少良く出回っている感じがしますが、単に.Trashに 送っているだけです。

もしかしたら古いバージョンを見ただけなのかも知れませんが、 確認した所、上の trash 同様、単にゴミ箱フォルダに移動させてる だけのようなので(sourceを見る限りでも)、 捨てられたファイルはFinderから直接見ると 元に戻す、は使えませんでしたし、rmtrash自体に 戻す機能が無いので元の場所の情報は残ってないことになります。 (Obj-cまで使ってやる必要が良くわかりませんでした…)

trash-cli

trash-cliは pythonベースでゴミ箱を実装したパッケージです。 pythonの環境(python 2.7)を整えればどのOSでも基本的には 使えます。

このパッケージには、ゴミ箱から元に戻したりゴミ箱を 空にしたりするコマンドも入っています。

nrm

自分で試してないですが、 nrmといったものも有りました。

rm tips

消せないファイルを消す方法

手違いで記号から始まる様なファイルを作ってしまったり、 文字化けしてしまう様なファイルを消したい場合、 "などで囲ってみてもどうしても上手く行かない場合があります。

この様なファイルを消す手っ取り早い方法は、必要なファイルや ディレクトリを他のディレクトリに移し、 そのファイルを含むディレクトリ毎消してしまうことです。

さらに安易な方法は、MacならFinder等、GUIファイルマネージャー を開けば大概の場合消去出来ます。

まじめに削除しようとする場合には、 inodeを使って消します。

$ ls -i
45323032 a

とするディレクトリ内にあるファイル/ディレクトリのinode番号が分かるので、 (上の場合はaと言うファイルだけあり、inode番号が45323032) この番号を用いて

find . -inum 45323032 -exec rm -rf {} \;

とすれば目的のファイル/ディレクトリを確実に消せます。

消してしまったファイルの復旧

rmで消してしまった場合でも、ゴミ箱を空にしてしまった場合でも、 これらの消す作業は基本的に情報を保持したブロックとの リンクを切ってその部分を使える様にする(余り正確な記述じゃないかもしれませんが) という作業なので、そのブロック自体には 上書きされるまで情報が残っている可能性があります。

この様な時は、すぐにそのディスク自体への書き込みを禁止にして 復旧作業を行えば情報を復旧出来る事があります。

デジカメなんかで間違えてSDカード内全消去、とかしてしまったとか言う 場面に何度か合いましたが、WindowsにもMacにも 無料のサルベージソフト(または期間限定で無料で使えるソフト) が沢山あるので、適当な物を ダウンロードしてきて毎回比較的簡単に復旧出来ています。

Linux等でext4を使っている場合はextundeleteというユーティリティがあり、 Linuxでうっかりrm -rfしちゃったけど復活出来たよー\(^o^)/が参考になります。

まとめ

rmのプロテクトやゴミ箱、さらに復旧についてまで見てみましたが、 結局のところ、バックアップさえしっかり取っておけばなんとかなるので、 重要なな部分については最低1日1回はバックアップが取れる様な 体制をとっておきたいところです。

今回は特にファイル削除に関することなので、上記のスクリプトも含め、 ご利用は計画的に。

trash は手元のMacとWindowsのcygwin、Red Had系のLinuxで試していますが、 不具合やもっと良く出来そうな部分があれば教えて頂けると嬉しいです。

最後に、rm -rf /をやってみました動画をどうぞ。

Ref:

Sponsored Links
Sponsored Links

« Macのデスクトップ上の情報表示 ANA国際線手荷物がエコノミークラスは2個から1個へ »

}