「rm -rf」から保護されたディレクトリを作成する


9

実行後、フォルダB内にあるフォルダAの一部のデータが失われましたrm -rf B。私が何をしたかを理解する前に、それはすべて終わりました。レッスンが学習されたので、次回同様のことをして自分を殺したいときに、フォルダの一部をばかばかしくないようにしたいと思います。

私が考えることができる1つの方法は、bash関数を記述して、それをにエイリアスすることrmです。この関数は、などの隠しファイルの各サブフォルダーを調べます.dontdelete。見つかった場合、本当に続行するかどうか尋ねられます。このフォルダに常に書き込みを行う処理があるため、書き込み禁止にできません。それを行うためのより良い方法はありますか?


2
エイリアスしようとしたDID rmへのrm -i:> -iプロンプトの前にすべての除去または> -Iつ以上のファイルを削除、または再帰的に削除するときに前に一度プロンプト。-iよりも煩わしさはほとんどありませんが、ほとんどのミスに対する保護を提供します。他のフラグを使用して、いつでもそれらを作成できます。
IBr 2013年

2
チェックアウトsafe-rm
sr_ 2013年

それを行う方法は十数通りあります。環境についてさらに詳しく説明する必要があります。
Ignacio Vazquez-Abrams 2013年


別のアイデアは、それを特定のフォルダーに移動するだけの関数にエイリアスtmpwatchし、このフォルダーから1時間ごとにファイルを削除するために実行するcronjobを作成することです。
Bratchley 2013年

回答:


14

あなたの質問を調査する際に、私はこのテクニックに出くわしました。これは将来あなたを助けるかもしれません。

次のようにして、ディレクトリ内のファイルを変更することができます。

touch -- -i

が存在rm -fr *するディレクトリでコマンドを実行する-iと、からのインタラクティブプロンプトが表示されますrm

$ ls
file1  file2  file3  file4  file5  -i

$ rm -fr *
rm: remove regular empty file `file1'? n
rm: remove regular empty file `file2'? n
rm: remove regular empty file `file3'? n
rm: remove regular empty file `file4'? n
rm: remove regular empty file `file5'? n

同じことをrm、常に行うエイリアスをそのままにしておくことでも実現できますrm -i。これは迷惑になる可能性があります。ですから、私がよく目にするのは、このエイリアスを設定し、プロンプトが表示されずに本当に削除したいときに無効にすることです。

alias rm='rm -i'

ディレクトリでは、次のように迎えられます。

$ ls
file1  file2  file3  file4  file5

$ rm -r *
rm: remove regular empty file `file1'?

エイリアスを上書きするには:

$ \rm -r *

rm -frしかし、これはまだ止まりません。しかし、それはあなたにいくつかの保護を提供します。

参考文献


1
これは、優雅でエレガントなソリューションであり、少数のディレクトリセットを保護する必要がある場合にのみ機能します。そして、私は古臭いかもしれませんが、私はまだ--force何かを言うと、それは本当にそれを意味するという印象の下に住んでいます。
CVn 2013年

また、rm -I(大文字i)は、少し邪魔にならないため、エイリアスで役立つ場合があります(manページによると、3つを超えるファイルを削除するか再帰的に削除する場合にのみプロンプトが表示されます)。
CVn 2013年

このtouch ./-iトリックはGNUでのみ機能しrm、POSIXLY_CORRECT変数が設定されていない場合にのみ機能することに注意してください(POSIXコマンドは引数の後のオプションを認識しません)。
ステファンChazelas

5

多くの可能性:

  • alias rm='rm -i'-rmが尋ねます-指定しない限り-f...
  • chmod -w dir -そのディレクトリ内のファイルを直接保護します。
  • chattr +i あなたが本当にそれを意味するなら
  • rmの周りに独自のラッパーを書く
  • 等...

ただし、より良い方法は、適切なバックアップを取り、重要なデータを何らかのバージョン管理(などgit)に保持することです。これにより、他にも多くの利点がもたらされます。


1
これについて言及するためにここに来ました。完全に安全にするためにchattr + iをお勧めします。
JZeolla 2013年

私はすでにrmエイリアスを設定してrm -iいますが、予想通り、-fオプションでは機能しません。他の多くの目的でgitを使用していますがrm -rf *、誤ってgitでも使用するとどうなりますか?
ディラワー

gitでは、サブディレクトリだけを削除すると、ローカルリポジトリから簡単に復元できます。リポジトリ全体を削除した場合は、以前に別の場所にプッシュしたことを考えると、単にもう一度クローンを作成できます。
michas 2013年

1

gitプロジェクトをカプセル化するようなバージョン管理ソフトウェアを使用します。

プロジェクト全体を削除しない限り、意図的に入力rm -rf .*して.gitディレクトリを削除し、ロールバックに必要なすべてのデータを失う必要があります。

これには、githubやbitbucketなどのリモートサーバーにコンテンツのバックアップをプッシュできるという追加の利点があります。


1

仕組みrm -rf dirは次のとおりです。

  1. が開きdir、コンテンツが表示されます。
  2. 各エントリについて、それがディレクトリの場合は同じプロセスを繰り返し、そうでない場合は呼び出しますunlink

ディレクトリリストの場合は、最初に特別なファイル名を返すことができunlink、そのファイルに対してを実行しているプロセスを停止させることができれば、問題は解決します。これは、fuseファイルシステムを使用して行うことができます。

たとえば、perl Fuseモジュールのloopback.plを、その下の実際のファイルシステムへのパススルーであるダミーファイルシステムを実装するだけにすることができます(以下のパッチも参照)。

  • ディレクトリをリストするとき、それは名前のエントリが含まれている場合、.{{do-not-delete}}.2つのファイルを持つエントリのリストを先頭に追加:.{{do-not-delete}}!error.{{do-not-delete}}!kill
  • unlink最初のものにしようとすると、エラーメッセージEPERMrm表示するようにコードを返します
  • unlink2つ目を試みると、プロセスが強制終了されます。

$ ls -Ff dir/test
./  .{{do-not-delete}}.  foo/  ../  bar
$ ./rm-rf-killer dir
$ ls -Ff dir/test
.{{do-not-delete}}!error  .{{do-not-delete}}!kill  ./  .{{do-not-delete}}.   foo/  ../  bar
$ rm -rf dir/test
rm: cannot remove `dir/test/.{{do-not-delete}}!error': Operation not permitted
zsh: terminated  rm -rf dir/test
$ ls -Ff dir/test
.{{do-not-delete}}!error  .{{do-not-delete}}!kill  ./  .{{do-not-delete}}.   foo/  ../  bar

ここでは、概念の証明としてそのloopback.pl例の上に適用するパッチを示します。

--- loopback.pl 2013-06-03 22:35:00.577316063 +0100
+++ rm-rf-killer    2013-06-03 22:33:41.523328427 +0100
@@ -7,2 +7,4 @@
 my $has_threads = 0;
+my $flag = ".{{do-not-delete}}";
+
 eval {
@@ -42,3 +44,4 @@

-use blib;
+#use blib;
+use File::Basename;
 use Fuse;
@@ -49,3 +52,3 @@

-my %extraopts = ( 'threaded' => 0, 'debug' => 0 );
+my %extraopts = ( 'threaded' => 0, 'debug' => 0, 'mountopts' => 'nonempty' );
 my($use_real_statfs, $pidfile);
@@ -64,3 +67,7 @@

-sub fixup { return "/tmp/fusetest-" . $ENV{LOGNAME} . shift }
+sub fixup {
+    my $f = shift;
+    $f =~ s#(/\Q$flag\E)!(error|kill)$#$1.#s;
+    return ".$f";
+}

@@ -78,3 +85,9 @@
 }
-    my (@files) = readdir(DIRHANDLE);
+    my @files;
+    
+    while (my $f = readdir(DIRHANDLE)) {
+        unshift @files, "$flag!error", "$flag!kill"
+            if ($f eq "$flag.");
+        push @files, $f;
+    }
 closedir(DIRHANDLE);
@@ -121,3 +134,12 @@
 sub x_readlink { return readlink(fixup(shift));         }
-sub x_unlink   { return unlink(fixup(shift)) ? 0 : -$!; }
+sub x_unlink   {
+    my $f = shift;
+    if (basename($f) eq "$flag!error") {return -EPERM()}
+    if (basename($f) eq "$flag!kill") {
+        my $caller_pid = Fuse::fuse_get_context()->{"pid"};
+        kill("TERM", $caller_pid);
+        return -EPERM();
+    }
+    return unlink(".$f") ? 0 : -$!;
+}

@@ -203,3 +225,2 @@
 sub daemonize {
-    chdir("/") || die "can't chdir to /: $!";
 open(STDIN, "< /dev/null") || die "can't read /dev/null: $!";
@@ -236,2 +257,3 @@

+chdir($mountpoint) or die("chdir: $!");
 daemonize();
@@ -239,3 +261,3 @@
 Fuse::main(
-    'mountpoint'    => $mountpoint,
+    'mountpoint'    => '.',
 'getattr'       => 'main::x_getattr',

-1

この作業を簡単にするスクリプトを作成しました。シェルスクリプトは、rootパスワードを要求せずに、フォルダーの特定のリストの読み取り/書き込み権限とchattrフラグをインタラクティブに変更します。以下のリンクからzipファイルをダウンロードできます。

https://drive.google.com/file/d/0B_3UYBZy2FVsMVpBSWdSWnFBYk0/edit?usp=sharing

セットアップを簡単にするためのインストールスクリプトも含めました。手順はzipファイルに含まれています。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.