使目录免受'rm -rf'保护


9

做完之后,我只是丢失了文件夹B中的文件夹A中的一些数据rm -rf B。在我意识到自己做了什么之前,一切都结束了。现在已吸取了一个教训,我希望对文件夹进行一些防白痴处理,以免下次我做类似的事情并想自杀时避免这种情况。

我能想到的一种方法是编写一个bash函数并将其别名为rm。此功能将在每个子文件夹中查找诸如的隐藏文件.dontdelete。找到后,它将询问我是否真的要继续。我无法使它处于写保护状态,因为有一个过程会不断写入此文件夹。有什么更好的方法吗?


2
你有没有试过别名rmrm -i:> -i提示之前的每条删除或> -I删除超过三个文件或者递归删除前时提示一次。与-i相比,它具有较小的侵入性,同时仍可防止大多数错误。您可以随时使用其他标志来增强它们的可读性。
IBr

2

大约有十二种方法可以做到这一点。您将需要更详细地了解您的环境。
伊格纳西奥·巴斯克斯


另一个想法是将其别名为一个函数,该函数只是将其移至特定文件夹,然后创建一个cronjob,tmpwatch该任务每小时运行一次以从该文件夹中删除文件。
布莱奇利2013年

Answers:


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

还要注意,rm -I(大写i)在别名中可能很有用,因为它的侵入性较小(根据手册页,仅在删除三个以上文件或递归删除时才提示)。
CVn

请注意,该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但是正如人们所期望的那样,它不适用于-foption。我将git用于其他许多目的,但是如果我不小心也rm -rf *使用git怎么办?
Dilawar

使用git,如果只删除一个子目录,则可以轻松地从本地存储库中还原它。如果您删除了整个存储库,则可以再次克隆它-之前已将其推送到其他位置。
michas

1

使用版本控制软件git来封装您的项目。

只要不删除整个项目,就必须有目的地键入rm -rf .*以删除.git目录并丢失回滚所需的所有数据。

这具有额外的好处,您可以将您的内容备份推送到github或bitbucket之类的远程服务器。


1

下面是如何rm -rf dir工作的:

  1. 打开dir,并列出其内容。
  2. 对于每个条目,如果是目录,请对其重复相同的过程;如果不是,请unlink对其进行调用。

如果可以,对于目录列表,请首先返回一个特殊的文件名,如果可以使unlink对该文件执行操作的进程终止,则可以解决此问题。可以使用保险丝文件系统来完成。

例如,您可以改编来自perl Fuse模块loopback.pl示例,该模块仅实现了一个虚拟文件系统,该虚拟文件系统只是像下面这样传递到真实文件系统的通道(另请参见下面的补丁):

  • 列出目录时,如果它包含名为的条目.{{do-not-delete}}.,则在条目列表前添加两个文件:.{{do-not-delete}}!error.{{do-not-delete}}!kill
  • 尝试unlink第一个时,返回EPERM代码,以便rm显示错误消息
  • 尝试unlink执行第二个操作时,该过程被终止。

$ 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',

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.