如何更安全地删除文件:使用自定义`rmtrash`脚本

在 Linux 系统中,rm 命令是一个非常强大的工具,它可以永久删除文件和目录。然而,这种强大的能力也带来了风险,因为一旦文件被删除,就很难恢复。为了防止因误操作而丢失重要数据,我们可以创建一个自定义的回收站来保存被删除的文件。以下是实现这一功能的详细步骤和脚本。

背景

作为 Ubuntu 的用户,我们经常需要在命令行下执行文件删除操作。但是,命令行下的 rm 命令一旦执行,被删除的文件几乎不可能恢复。为了避免这种情况,我们编写了一个简单的脚本,将删除操作改为移动操作,从而实现了一个类似于 Windows 系统中回收站的功能。

脚本介绍

下面提供的脚本 rmtrash 可以作为 rm 命令的替代品。它将用户指定的文件或目录移动到一个特定的“回收站”目录中,而不是直接删除它们。这样,如果需要,用户可以从回收站中恢复这些文件。

脚本功能

  • 移动文件到回收站:而不是直接删除文件,脚本会将它们移动到用户家目录下的 .rmtrash/ 目录。
  • 记录删除操作:脚本会记录所有删除操作的详细信息,包括删除的文件路径和时间,以便于恢复。
  • 恢复文件:可以从回收站中恢复文件到原始位置。
  • 清空回收站:当确认不再需要回收站中的文件时,可以清空回收站。
  • 查看回收站内容:可以列出回收站中的所有文件和目录。
  • 详细日志:可以查看已删除文件的详细日志。

脚本

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
#!/bin/bash

###trash目录define
realrm="/bin/rm"
trash_dir=~/.rmtrash/
trash_log=~/.rmtrash.log
###判断trash目录是否存在,不存在则创建
if [ ! -d $trash_dir ] ;then
mkdir -v $trash_dir
fi

####function define
###usage function
rm_usage () {
cat <<EOF
Usage1: `basename $0` file1 [file2] [dir3] [....] 删除文件或目录, 并将它们移动到 rmtrash 回收站
Usage2: rm file1 [file2] [dir3] [....] 删除文件或目录, 并将它们移动到 rmtrash 回收站
rm is alias to `basename $0`.
options:
-f 移动一个或多个文件到 rmtrash 回收站
-r 移动一个或多个文件到 rmtrash 回收站
-fr 移动一个或多个文件到 rmtrash 回收站
-rf 移动一个或多个文件到 rmtrash 回收站
-R 将所选文件从 rmtrash 回收站恢复到原路径
-l 列出 rmtrash 回收站的内容
-i 显示已删除文件历史的详细日志
-d 根据用户输入的文件名从回收站删除一个或多个文件
-e 清空 rmtrash 回收站
-h 显示此帮助菜单
EOF
}


###rm mv function
rm_mv () {
echo ----------------------------
now=`date +%Y%m%d_%H:%M:%S`
dupfix=.`date +%Y%m%d%H%M%S`
###将用户输入的文件循环mv到trash中
###for file in $file_list ;do
#echo $file
###提取用户输入参数的文件名、目录名,拼出绝对路径
file_name=`basename $file`
file_dir=$(cd `dirname $file`;pwd)
file_fullpath=$file_dir/$file_name
###判断要删除的文件或者目录大小是否超过2G
#echo file_fullpath: $file_fullpath
#if [[ "$file_fullpath" == "/*" ]];then
# echo action deny!
#else
####判断即将删除的文件在trash目录里是否已存在
if [[ `ls $trash_dir|grep ^${file_name}$` ]];then
##已存在,文件名重复,需要rename,想原始名的基础上加后缀
trash_dest_path=$trash_dir$file_name$dupfix
echo trash目录里已存在$file_name,需要rename $file_name$dupfix
else
##不重名,直接按原始文件名保存
trash_dest_path=$trash_dir$file_name
fi
###mv成功记录log,记录删除时的文件、目录的路径等信息到log,以便恢复数据
mv $file_fullpath $trash_dest_path && \
echo $now `whoami` moved from $file_fullpath to $trash_dest_path >> $trash_log && \
echo -e "\033[31m\033[05m $file 从 $file_fullpath 被删除\033[0m"
#cat $trash_log
#fi
###done
}

###rm list function
rm_list () {
echo ----------------------------
echo list trash_dir contents:
ls $trash_dir
}


###rm restore function
rm_restore () {
echo ----------------------------
echo -en "请选择要恢复的文件名(多个文件中间空格分隔,取消ctl+c):"
read reply
for file in $reply ;do
###判断原始位置的是否有同名文件存在
originalpath=`cat $trash_log|grep /$file$|awk '{print $5}'`
if [[ `ls $originalpath` ]];then
echo -en "originalpath:$originalpath 已经存在。是否继续覆盖(y/n):"
read ack
if [[ $ack == y ]];then
echo restore:
elif [[ $ack == n ]];then
echo bye && exit
else
echo 输入非法 && exit
fi
fi
###
mv $trash_dir$file $originalpath && \
###linux和mac下sed的用法有细微差别,故需通过操作系统类型进行选择对应的sed格式
if [[ $os_type == Darwin ]];then
sed -i .bak "/\/$file$/d" $trash_log
echo os_type=Darwin
elif [[ $os_type == Linux ]];then
sed -i.bak "/\/$file$/d" $trash_log
echo os_type=Linux
fi && \
echo -e "\033[32m\033[05m$file 从 $originalpath 恢复成功\033[0m"
done
}

### rm show delete log function
rm_infolog () {
echo ----------------------------
echo detailed deleted file log:
cat $trash_log
}


###rm empty trash function
rm_empty () {
echo ----------------------------
echo -en "空回收站,回收站中的所有备份将被删除,是否继续(y/n):"
read ack
if [[ $ack == y ]];then
echo begin to empty trash:
elif [[ $ack == n ]];then
echo bye && exit
else
echo 输入非法 && exit
fi
/bin/rm -fr ${trash_dir} && \
echo >$trash_log && \
echo -e "\033[31m\033[05m 垃圾桶已经被清空了\033[0m"
}

###rm delete function
rm_delete () {
echo ----------------------------
echo -en "请选择trash中要删除的文件名(多个文件中间空格分隔,取消ctl+c):"
read reply
for file in $reply ;do
###if file exist then delete it from trash
if [[ `ls ${trash_dir}$file` ]];then
/bin/rm -fr ${trash_dir}$file && \
###linux和mac下sed的用法有细微差别,故需通过操作系统类型进行选择对应的sed格式
if [[ $os_type == Darwin ]];then
sed -i .bak "/\/$file$/d" $trash_log
echo os_type=Darwin
elif [[ $os_type == Linux ]];then
sed -i.bak "/\/$file$/d" $trash_log
echo os_type=Linux
fi && \
echo -e "\033[32m\033[05m$file 从 ${trash_dir}$file 被删除\033[0m"
else
echo $file is not exist in $trash_dir
fi
done
}


###跨分区的问题

#####主程序开始
###参数个数为0,输出help
if [ $# -eq 0 ] ;then rm_usage ;fi
###根据用户输入选项执行相应动作
###通过非显示的方式(加入fr选项,但在case里不做匹配操作,遇到含-fr/-rf/-f/-r时直接删除)支持很多用户的使用习惯rm -fr file,rm -rf file
while getopts lRiedhfr option ;do
case "$option" in
l) rm_list;;
R) rm_list
rm_restore;;
i) rm_infolog;;
h) rm_usage;;
e) rm_empty;;
d) rm_list
rm_delete;;
\?)rm_usage
exit 1;;
esac
done
shift $((OPTIND-1))

###将文件名的参数依次传递给rm_mv函数
while [ $# -ne 0 ];do
file=$1
echo file=$file
rm_mv
shift
done

安装脚本

  1. 将上面的脚本保存为一个文件,例如 rmtrash

  2. 将该脚本移动到 /usr/local/bin/ 目录下,使其全局可执行:

    1
    2
    sudo mv rmtrash /usr/local/bin/
    sudo chmod +x /usr/local/bin/rmtrash
  3. 使用别名替换原 rm 命令:

    1
    alias rm='rmtrash'

    将上述别名添加到你的 .bashrc.bash_profile 文件中,以便在每次登录时自动设置。

使用脚本

现在,你可以像使用 rm 命令一样使用 rmtrash。例如,删除一个文件:

1
rm file.txt

这将把 file.txt 移动到回收站,而不是直接删除它。

恢复文件

如果你想从回收站恢复文件,可以使用 -R 选项:

1
rm -R file.txt

这将提示你选择要恢复的文件。

清空回收站

当你确定回收站中的文件不再需要时,可以使用 -e 选项清空回收站:

1
rm -e