Linux对rm的误操作预防

rm -rf /*    这个要命的操作,让我们轻易不敢用rm这个命令。虽然rm后,数据可以恢复,但是,rm后,rm的目标所在的区,一定不能写入内容了,但是我们无法保证一定不会写入内容。比如紧急状态下,人为的紧张或者其他的日志之类的持续的导入或者数据的同步之类的情况。(当然,那种传说中rpm能敲成rm的也是奇才。)

那么我们不如让rm实现mv aaa /root/.local/share/Trash的效果,让rm的目标转移到trash中,一旦发现rm了错误的目标,可以及时的从trash中还原。实现类似windows下回收站的效果。

然而我功力不够深厚,不知如何alias rm实现输入rm aaa的时候,实现

mv aaa /root/.local/share/Trash

但是,我不知道该如何让rm后面的目标传入到alias定义的 ,mv * /home/trash的*这个位置上。

rm的目标是多变的。我不能把*定义的内容指定一个目标。否则这么定义就没有实用价值了。

于是我找到了IBM提供的一种办法。

构建一个回收站,然后去三个目的:真实的操作。操作的记录。操作的倒回。

首先是真实的操作。

Delete 脚本

创建目录

首先要创建目录来存放被删除的文件,本文在用户根目录$HOME 下建立 trash 目录来存放文件。具体代码如下:

清单 1.创建回收站目录
realrm="/bin/rm"
if [ ! -d ~/trash ]
 then
      mkdir -v ~/trash
      chmod 777 ~/trash
 fi

如上所示,先判断目录是否已建立,如未建立,即第一次运行该脚本,则创建 trash 目录。变量 realrm 存放了 Linux 的 rm 脚本位置,用于在特定条件下调用以直接删除文件或目录。

输出帮助信息

该脚本在用户仅输入脚本名而未输入参数执行时,输出简要帮助信息,代码如下:

清单 2.输出帮助信息
if [ $# -eq 0 ]
  then
      echo "Usage:delete file1 [file2 file3....]"
      echo "If the options contain -f,then the script will exec 'rm' directly"

如代码所示,该脚本的运用格式是 delete 后跟要删除的文件或目录的路径,中间用空格隔开。

直接删除文件

有些用户确认失效并想直接删除的文件,不应放入回收站中,而应直接从硬盘中删除。Delete 脚本提供了-f 选项来执行这项操作:

清单 3.直接删除文件
while getopts "dfiPRrvW" opt
      do
        case $opt in
            f)
               exec $realrm "$@"
                ;;
            *)
               
               # do nothing     
                ;;
        esac
      done

如果用户在命令中加入了-f 选项,则 delete 脚本会直接调用 rm 命令将文件或目录直接删除。如代码中所示,所有的参数包括选项都会传递给 rm 命令。所以只要选项中包括选项-f 就等于调用 rm 命令,可以使用 rm 的所有功能。如:delete �Crfv filename 等于 rm �Crfv filename。

用户交互

需要与用户确认是否将文件放入回收站。相当于 Windows 的弹窗提示,防止用户误操作。

清单 4.用户交互
echo -ne "Are you sure you want to move the files to the trash?[Y/N]:\a"
 read reply
if [ $reply = "y" -o $reply = "Y" ]
  then #####

判断文件类型并直接删除大于 2G 文件

本脚本只对普通文件和目录做操作,其他类型文件不做处理。先对每个参数做循环,判断他们的类型,对于符合的类型再判断他们的大小是否超过 2G,如果是则直接从系统中删除,避免回收站占用太大的硬盘空间。

清单 5.删除大于 2G 的文件
for file in $@
 do
if [ -f "$file" �Co �Cd "$file" ]
then
if [ -f "$file" ] && [ `ls �Cl $file|awk '{print $5}'` -gt 2147483648 ]
   then
      echo "$file size is larger than 2G,will be deleted directly"
        `rm �Crf $file`
elif [ -d "$file" ] && [ `du �Csb $file|awk '{print $1}'` -gt 2147483648 ]
   then
      echo "The directory:$file is larger than 2G,will be deleted directly"
        `rm �Crf $file`

如以上代码所示,该脚本用不同的命令分别判断目录和文件的大小。鉴于目录的大小应该是包含其中的文件以及子目录的总大小,所以运用了'du -sb'命令。两种情况都使用了 awk 来获取特定输出字段的值来作比较。

移动文件到回收站并做记录

该部分是 Delete 脚本的主要部分,主要完成以下几个功能

  • 获取参数的文件名。因为用户指定的参数中可能包含路径,所以要从中获取到文件名,用来生成 mv 操作的参数。该脚本中运用了字符串正则表达式'${file##*/}'来获取。

  • 生成新文件名。在原文件名中加上日期时间后缀以生成新的文件名,这样用户在浏览回收站时,对于每个文件的删除日期即可一目了然。

  • 生成被删文件的绝对路径。为了以后可能对被删文件进行的恢复操作,要从相对路径生成绝对路径并记录。用户输入的参数可能有三种情况:只包含文件名的相对路径,包含点号的相对路径以及绝对路径,脚本中用字符串处理对三种情况进行判断,并进行相应的处理。

  • 调用 logTrashDir 脚本,将回收站中的新文件名、原文件名、删除时间、原文件绝对路径记录到隐藏文件中

  • 将文件通过 mv 命令移动到 Trash 目录下。

    详细代码如下所示:

    清单 6.移动文件到回收站并做记录
    now=`date +%Y%m%d_%H_%M_%S`
    filename="${file##*/}"
    newfilename="${file##*/}_${now}"
    mark1="."
    mark2="/"
    if  [ "$file" = ${file/$mark2} ]
     then
      fullpath="$(pwd)/$file"
    elif [ "$file" != ${file/$mark1} ]
     then
      fullpath="$(pwd)${file/$mark1}"
    else
      fullpath="$file"
    fi	    
    echo "the full path of this file is :$fullpath"
    if mv -f $file ~/trash/$newfilename
     then
      $(/logTrashDir "$newfilename $filename $now $fullpath")
       echo "files: $file is deleted"
     else
      echo "the operation is failed"
     fi

(顺手记个tar:

tar -C /home -czf /home/aaa.tar.gz /root/Desktop/aaa.txt 

   #打包后发现,连带root一起被打包了。在home下出现了一个aaa.tar.gz。里面是/root/Desktop/aaa.txt,而不是单独的aaa.txt。)

做一下操作的记录:

logTrashDir 脚本

该脚本较简单,仅是一个简单的文件写入操作,之所以单独作为一个脚本,是为了以后扩展的方便,具体代码如下:

清单 7.logTrashDir 代码
if [ ! -f ~/trash/.log ]
  then
     touch ~/trash/.log
     chmod 700~/trash/.log
fi
   echo $1 $2 $3 $4>> ~/trash/.log

该脚本先建立.log 隐藏文件,然后往里添加删除文件的记录

倒回的办法:

restoreTrash 脚本

该脚本主要完成以下功能:

  • 从.log 文件中找到用户想要恢复的文件对应的记录。此处依然使用 awk,通过正表达式匹配找到包含被删除文件名的一行

  • 从记录中找到记录原文件名的字段,以给用户提示

  • 将回收站中的文件移动到原来的位置,在这里运用了 mv �Cb 移动文件,之所以加入-b 选项是为了防止原位置有同名文件的情况。

  • 将.log 文件中与被恢复文件相对应的记录删除

清单 8.获取相应记录
originalPath=$(awk /$filename/'{print $4}' "$HOME/trash/.log")
清单 9.查找原文件名及现文件名字段
filenameNow=$(awk /$filename/'{print $1}' ~/trash/.log)
filenamebefore=$(awk /$filename/'{print $2}' ~/trash/.log)
echo "you are about to restore $filenameNow,original name is $filenamebefore"
echo "original path is $originalPath"
清单 10.恢复文件到原来位置并删除相应记录
echo "Are you sure to do that?[Y/N]"
  read reply
  if [ $reply = "y" ] || [ $reply = "Y" ]
   then
$(mv -b "$HOME/trash/$filename" "$originalPath")
$(sed -i /$filename/'d' "$HOME/trash/.log")
  else
    echo "no files restored"
  fi

自动定期清理 trash 目录

因为 delete 操作并不是真正删除文件,而是移动操作,经过一段时间的积累,trash 目录可能会占用大量的硬盘空间,造成资源浪费,所以定期自动清理 trash 目录下的文件是必须得。本文的清理规则是:在回收站中存在 7 天以上的文件及目录将会被自动从硬盘中删除。运用的工具是 Linux 自带的 crontab。

Crontab 是 Linux 用来定期执行程序的命令。当安装完成操作系统之后,默认便会启动此任务调度命令。Crontab 命令会定期检查是否有要执行的工作,如果有要执行的工作便会自动执行该工作。而 Linux 任务调度的工作主要分为以下两类:

1、系统执行的工作:系统周期性所要执行的工作,如备份系统数据、清理缓存

2、个人执行的工作:某个用户定期要做的工作,例如每隔 10 分钟检查邮件服务器是否有新信,这些工作可由每个用户自行设置。

首先编写 crontab 执行时要调用的脚本 cleanTrashCan.如清单 10 所示,该脚本主要完成两项功能:

  • 判断回收站中的文件存放时间是否已超过 7 天,如果超过则从回收站中删除。

  • 将删除文件在.log 文件中相应的记录删除,保持其中数据的有效性,提高查找效率。

清单 11.删除存在回收站超过 7 天的文件并删除.log 中相应记录
arrayA=($(find ~/trash/* -mtime +7 | awk '{print $1}'))
   for file in ${arrayA[@]}
    do
      $(rm -rf "${file}")
      filename="${file##*/}"
      echo $filename
      $(sed -i /$filename/'d' "$HOME/trash/.log")
    done

脚本编写完成后通过 chmod 命令赋予其执行权限,然后运过 crontab �Ce 命令添加一条新的任务调度:

 10 18 * * * /bin/ cleanTrashCan

该语句的含义为,在每天的下午 6 点 10 分执行 cleanTrashCan 脚本

通过这条任务调度,trash 的大小会得到有效的控制,不会持续增大以致影响用户的正常操作。

实际应用

首先要将 delete 脚本,logTrashDir 脚本,restoreTrash 脚本和 cleanTrashCan 放到/bin 目录下,然后用 chmod +x delete restoreTrash logTrashDir cleanTrashCan 命令赋予这三个脚本可执行权限。

运用 delete 脚本删除文件,delete aaa,delete ./aaa 或者 delete /usr/aaa

如果用户在七天之内发现该文件还有使用价值,则可以使用 restoreTrash 命令将被删除文件恢复到原路径下:restoreTrash ~/trash/aaa_20150923_06_28_57

查看/usr 目录,可以发现 useless 文件已经被恢复至此。

另外,看完这个文章以后,我的感觉就是,shell学了半天都还给老师了。

看来有只好一点点补习了。只可惜现在的工作并不是专职的Linux运维,要不我的Linux水准一定能得到质的提升。

单靠一点兴趣在工作之余支撑着,实在是有些艰难。

牛的私人博客。不过看他文章,应该是本科生,还没毕业的样子。

外,有大牛整理了完整的代码。我也是膜拜一下。做个记录。

下面出现大量代码刷屏,请睁大眼睛。直接粘到本地txt再看。大牛写的太好了。有点晃眼。

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

#Save as /bin/delete

#!/bin/bash

realrm="/bin/rm"

if[!-d~/trash]

then

mkdir-v~/trash

chmod777~/trash

fi

if[$# -eq 0 ]

then

echo"Usage: delete file1 [file2 file3....]"

echo"If the options contain -f, then the script will exec 'rm' directly"

fi

whilegetopts"dfiPRrvw"opt

do

case$opt in

f)

exec$realrm"$@"

;;

*)

# do nothing

;;

esac

    done

echo-ne"Are you sure you want to move the files to the trash?[Y/N]:\a"

read reply

if[$reply="y"-o$reply="Y"]

then#####

forfile in$@

do

if[-f"$file"-o-d"$file"]

then

if[-f"$file"]&&[`ls-l$file|awk' {print $5}'`-gt2147483648]

then

echo"$file size is larger than 2G, will be deleted directly"

`rm-rf$file`

elif[-d"$file"]&&[`du-sb$file|awk'{print $1}'`-gt2147483648]

then

echo"The directory:$file is larger than 2G, will be deleted directly"

`rm-rf$file`

fi

fi

done

fi

now=`date+%Y%m%d_%H_%M_%S`

filename="${file##*/}"

newfilename="${file##*/}_${now}"

mark1="."

mark2="/"

if["$file"=${file/$mark2}]

then

fullpath="$(pwd)/$file"

elif["$file"!=${file/$mark1}]

then

fullpath="$(pwd)${file/$mark1}"

else

fullpath="$file"

fi

echo"the full path of this file is : $fullpath"

ifmv-f$file~/trash/$newfilename

then

$(/bin/logTrashDir"$newfilename $filename $now $fullpath")

echo"files: $file is deleted"

else

echo"the operation is failed"

fi

1

2

3

4

5

6

7

8

#Save as /bin/logTrashDir

#!/bin/bash

if[!-f~/trash/.log]

then

touch~/trash/.log

chmod700~/trash/.log

fi

echo$1$2$3$4>>/home/razrlele/trash/.log

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

#Save as /bin/restoreTrash

#!/bin/bash

originalPath=$(awk/$filename/'{print $4}'"$HOME/trash/.log")

filenameNow=$(awk/$filename/'{print $1}'"$HOME/trash/.log")

filenamebefore=$(awk/$filename/'{print $2}'"$HOME/trash/.log")

echo"you are about to restore $filenameNow,original name is $filenamebefore"

echo"original path is $originalPath"

echo"Are you sure to do that?[Y/N]"

read reply

if[$reply="y"]||[$reply="Y"]

then

$(mv-b"$HOME/trash/$filenameNow"$originalPath)

$(sed-i/$filenameNow/'d'"$HOME/trash/.log")

else

echo"no files restored"

fi

1

2

3

4

5

6

7

8

9

10

#Save as /bin/cleanTrashCan

#!/bin/bash

arrayA=($(find~/trash/* -mtime +7 | awk '{print $1}'))

    for file in ${arrayA[@]}

    do

        $(rm -rf "${file}")

        filename="${file##*/}"

        echo $filename

        $(sed -i /$filename/'d' "$HOME/trash/.log")

done

1

chmod+xdelete restoreTrash logTrashDir cleanTrashCan

1

/etc/systemd/system/cleanTrash.service

1

2

3

4

5

6

[Unit]

Description=Clean Trash Can

[Service]

Type=simple

ExecStart=/bin/cleanTrashCan

1

/etc/systemd/system/cleanTrash.timer

1

2

3

4

5

6

7

8

9

[Unit]

Description=Runs cleanTrashCan every week

[Timer]

OnCalendar=Sun,18:00

Unit=cleanTrash.service

[Install]

WantedBy=multi-user.target

2

systemctl start cleanTrash.timer

systemctl enable cleanTrash.timer

顺便发现了几个好玩的。

[root@localhost Desktop]#  echo abc | openssl base64

YWJjCg==

[root@localhost Desktop]# echo YWJjCg== | base64 -d

abc

[root@localhost Desktop]# 

对于md5这个没做试验,要睡觉了。拜拜。

你可能感兴趣的:(windows,linux,回收站,local,alias)