转载时请注明出处和作者联系方式文章出处:http://blog.csdn.net/jack0106 作者联系方式:冯牮 fengjian0106@yahoo.com.cn
前两天有个同事在命令行下面执行rm的时候,不小心删除了很多系统文件,搞得系统崩溃了,我们想过恢复ext4文件系统,但是没有成功,最终还是重装电脑。我之前也犯过同样的错误,所以再总结一下
1. 最简单的办法,将rm替换为mv,方法如下:
在~/.bashrc 文件里追加下面一段,然后刷新该文件 source ~/.bashrc 即可。使用rm命令的时候就会把文件移动到指定的文件夹~/.trash下
mkdir -p ~/.trash alias rm=trash trash() { mv $@ ~/.trash/ }
这种方法虽然简单,但是还不够灵活,而且这个rm命令,不能处理命令行选项。
2. 第一种方法的改进,可以参考此帖http://iregex.org/blog/safer-rm-command.html
不足的地方----仍然是不能处理rm的命令行选项。
3. 提供一个和rm兼容的工具,选项参数和rm保持一致。(这个代码我还没有找到,我也没有自己去写,因为,后面还有更好的方法)
不足的地方----本来这种方法已经没有不足了,但是,gnome图形环境下,是有回收站的,我们还可以更好的和回收站整合
4. 提供一个和rm兼容的工具,选项参数和rm保持一致,并且,文件会被删除到gnome的回收站中(gnome回收站的路径是~/.local/share/Trash 其中还包含2个或多个子路径,回收站里面不仅保存文件,还保存执行删除操作时的一起额外信息,比如原始路径和删除的时间),这样的话,在图形环境下,也可以察看被删除的文件,并且可以恢复。
这个工具已经有了,不需要我们自己造轮子,在ubuntu下执行sudo apt-get install trash-cli,然后就会得到trash这个命令行工具,看一下它的帮助信息,和容易就可以使用了
trash --help
Usage: trash [OPTION]... FILE...
Put files in trash
Options:
--version show program's version number and exit
-h, --help show this help message and exit
-d, --directory ignored (for GNU rm compatibility)
-f, --force ignored (for GNU rm compatibility)
-i, --interactive ignored (for GNU rm compatibility)
-r, -R, --recursive ignored (for GNU rm compatibility)
-v, --verbose explain what is being done
5. 如果要重新造轮子,怎么造?
这就要用到glib中的gio模块,在gio中,文件都是用GFile来表示的,GFile有一个函数,就是把文件删除到回收站中,函数如下:
gboolean g_file_trash (GFile *file, GCancellable *cancellable, GError **error);
要自己造轮子,就要用到这个函数,还可以进一步深入,这个函数的内部,是如何实现的?这个函数的内部,调用到了glocalfile.c中的另外一个函数,如下:
static gboolean g_local_file_trash (GFile *file, GCancellable *cancellable, GError **error) { GLocalFile *local = G_LOCAL_FILE (file); GStatBuf file_stat, home_stat; const char *homedir; char *trashdir, *topdir, *infodir, *filesdir; char *basename, *trashname, *trashfile, *infoname, *infofile; char *original_name, *original_name_escaped; int i; char *data; gboolean is_homedir_trash; char delete_time[32]; int fd; GStatBuf trash_stat, global_stat; char *dirname, *globaldir; GVfsClass *class; GVfs *vfs; if (g_lstat (local->filename, &file_stat) != 0) { int errsv = errno; g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errsv), _("Error trashing file: %s"), g_strerror (errsv)); return FALSE; } homedir = g_get_home_dir (); g_stat (homedir, &home_stat); is_homedir_trash = FALSE; trashdir = NULL; if (file_stat.st_dev == home_stat.st_dev) { is_homedir_trash = TRUE; errno = 0; trashdir = g_build_filename (g_get_user_data_dir (), "Trash", NULL); if (g_mkdir_with_parents (trashdir, 0700) < 0) { char *display_name; int errsv = errno; display_name = g_filename_display_name (trashdir); g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errsv), _("Unable to create trash dir %s: %s"), display_name, g_strerror (errsv)); g_free (display_name); g_free (trashdir); return FALSE; } topdir = g_strdup (g_get_user_data_dir ()); } else { uid_t uid; char uid_str[32]; uid = geteuid (); g_snprintf (uid_str, sizeof (uid_str), "%lu", (unsigned long)uid); topdir = find_topdir_for (local->filename); if (topdir == NULL) { g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, _("Unable to find toplevel directory for trash")); return FALSE; } /* Try looking for global trash dir $topdir/.Trash/$uid */ globaldir = g_build_filename (topdir, ".Trash", NULL); if (g_lstat (globaldir, &global_stat) == 0 && S_ISDIR (global_stat.st_mode) && (global_stat.st_mode & S_ISVTX) != 0) { trashdir = g_build_filename (globaldir, uid_str, NULL); if (g_lstat (trashdir, &trash_stat) == 0) { if (!S_ISDIR (trash_stat.st_mode) || trash_stat.st_uid != uid) { /* Not a directory or not owned by user, ignore */ g_free (trashdir); trashdir = NULL; } } else if (g_mkdir (trashdir, 0700) == -1) { g_free (trashdir); trashdir = NULL; } } g_free (globaldir); if (trashdir == NULL) { gboolean tried_create; /* No global trash dir, or it failed the tests, fall back to $topdir/.Trash-$uid */ dirname = g_strdup_printf (".Trash-%s", uid_str); trashdir = g_build_filename (topdir, dirname, NULL); g_free (dirname); tried_create = FALSE; retry: if (g_lstat (trashdir, &trash_stat) == 0) { if (!S_ISDIR (trash_stat.st_mode) || trash_stat.st_uid != uid) { /* Remove the failed directory */ if (tried_create) g_remove (trashdir); /* Not a directory or not owned by user, ignore */ g_free (trashdir); trashdir = NULL; } } else { if (!tried_create && g_mkdir (trashdir, 0700) != -1) { /* Ensure that the created dir has the right uid etc. This might fail on e.g. a FAT dir */ tried_create = TRUE; goto retry; } else { g_free (trashdir); trashdir = NULL; } } } if (trashdir == NULL) { g_free (topdir); g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, _("Unable to find or create trash directory")); return FALSE; } } /* Trashdir points to the trash dir with the "info" and "files" subdirectories */ infodir = g_build_filename (trashdir, "info", NULL); filesdir = g_build_filename (trashdir, "files", NULL); g_free (trashdir); /* Make sure we have the subdirectories */ if ((g_mkdir (infodir, 0700) == -1 && errno != EEXIST) || (g_mkdir (filesdir, 0700) == -1 && errno != EEXIST)) { g_free (topdir); g_free (infodir); g_free (filesdir); g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, _("Unable to find or create trash directory")); return FALSE; } basename = g_path_get_basename (local->filename); i = 1; trashname = NULL; infofile = NULL; do { g_free (trashname); g_free (infofile); trashname = get_unique_filename (basename, i++); infoname = g_strconcat (trashname, ".trashinfo", NULL); infofile = g_build_filename (infodir, infoname, NULL); g_free (infoname); fd = open (infofile, O_CREAT | O_EXCL, 0666); } while (fd == -1 && errno == EEXIST); g_free (basename); g_free (infodir); if (fd == -1) { int errsv = errno; g_free (filesdir); g_free (topdir); g_free (trashname); g_free (infofile); g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errsv), _("Unable to create trashing info file: %s"), g_strerror (errsv)); return FALSE; } close (fd); /* TODO: Maybe we should verify that you can delete the file from the trash before moving it? OTOH, that is hard, as it needs a recursive scan */ trashfile = g_build_filename (filesdir, trashname, NULL); g_free (filesdir); if (g_rename (local->filename, trashfile) == -1) { int errsv = errno; g_free (topdir); g_free (trashname); g_free (infofile); g_free (trashfile); if (errsv == EXDEV) /* The trash dir was actually on another fs anyway!? This can happen when the same device is mounted multiple times, or with bind mounts of the same fs. */ g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, _("Unable to trash file: %s"), g_strerror (errsv)); else g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errsv), _("Unable to trash file: %s"), g_strerror (errsv)); return FALSE; } vfs = g_vfs_get_default (); class = G_VFS_GET_CLASS (vfs); if (class->local_file_moved) class->local_file_moved (vfs, local->filename, trashfile); g_free (trashfile); /* TODO: Do we need to update mtime/atime here after the move? */ /* Use absolute names for homedir */ if (is_homedir_trash) original_name = g_strdup (local->filename); else original_name = try_make_relative (local->filename, topdir); original_name_escaped = escape_trash_name (original_name); g_free (original_name); g_free (topdir); { time_t t; struct tm now; t = time (NULL); localtime_r (&t, &now); delete_time[0] = 0; strftime(delete_time, sizeof (delete_time), "%Y-%m-%dT%H:%M:%S", &now); } data = g_strdup_printf ("[Trash Info]/nPath=%s/nDeletionDate=%s/n", original_name_escaped, delete_time); g_file_set_contents (infofile, data, -1, NULL); g_free (infofile); g_free (data); g_free (original_name_escaped); g_free (trashname); return TRUE; }
可以用c编码,也可以用python编码,其实前面提到的trash工具,就是用python编写的。