S_lion's Studio

alias与rm的最佳实践

字数统计: 1.5k阅读时长: 5 min
2021/08/01 Share

不论是刚入职的运维萌新,还是工作多年的运维老油条,当在服务器上敲下rm时必须要保证命令的准确,在技术贴吧中也常常有人调侃到不想干了就rm -rf /与删库跑路等等的段子,如何让rm的操作更有保障呢,以下会提供一种解题思路。

alias的用法

Linux alias命令用于设置指令的别名。

语法:

1
alias[别名]=[指令名称]

使用不带参数的alias将列出当前shell环境下所有的已定义的别名。

1
2
3
4
5
6
7
8
9
10
11
[root@slions_pc1 home]# alias
alias cp='cp -i'
alias egrep='egrep --color=auto'
alias fgrep='fgrep --color=auto'
alias grep='grep --color=auto'
alias l.='ls -d .* --color=auto'
alias ll='ls -l --color=auto'
alias ls='ls --color=auto'
alias mv='mv -i'
alias rm='rm -i'
alias which='alias | /usr/bin/which --tty-only --read-alias --show-dot --show-tilde'

另外需要说明的是,当别名和命令同名时,将优先执行别名(否则别名就没有意义了),这可以从which的结果中看出:

1
2
3
[root@slions_pc1 home]# which rm
alias rm='rm -i'
/usr/bin/rm

如果定义的命名名称和原始命令同名(例如定义的别名 ls=’ls -l’ ),此时如果想要明确使用原始命令,可以删除别名或者使用绝对路径或者使用转义符来还原命令。

1
2
3
4
[root@slions_pc1 home]# \ls
slions
[root@slions_pc1 home]# /usr/bin/ls
slions

alias命令是临时定义别名,要定义长久生效的别名就将别名定义语句写入/etc/profile~/.bash_profile~/.bashrc,第一个对所有用户有效,后面两个对对应用户有效。修改后记得使用source来重新调取这些配置文件。

使用unalias可以临时取消别名。

alias与rm

如何让alias与rm配合起来能减少误操作引发的影响呢,整体思路就是rm变为mv操作,后续给备份目录增加个时效性机制,保障磁盘空间的可用性。

alias的bug

1
2
3
4
5
6
7
8
9
10
11
[root@slions_pc1 home]# ls
slions
[root@slions_pc1 home]# touch testfile
[root@slions_pc1 home]# alias rmm='cp $@ /tmp/backup;rm $@'
[root@slions_pc1 home]# rmm testfile
cp: 在"/tmp/backup" 后缺少了要操作的目标文件
Try 'cp --help' for more information.
rm:是否删除普通空文件 "testfile"?y
[root@slions_pc1 home]# ls
slions
[root@slions_pc1 home]# ls /tmp/backup/

该别名的目的是删除文件时先备份到/tmp/backup目录下,然后再删除。按照man bash里的说明,别名rmm只是第一个cp命令的别名,分号后的rm不是别名的一部分,而是紧跟在别名后的下一行命令。当执行别名rmm时,首先读取别名到分号位置处,然后进行别名扩展,执行完别名命令后,再执行分号后的rm命令。

上面的命令没有达到预期效果,问题出在cp的参数”$@”,该变量本表示提供的所有参数,但由于cp命令后使用分号分隔并定义了另一个命令,这使得执行别名命令时,参数无法传递到cp命令上,而只能传递到最后一个命令rm上,也就是说cp后的”$@”是空值。

可以测试下:

1
2
3
4
5
[root@slions_pc1 home]# touch testfile
[root@slions_pc1 home]# alias rmm='echo cp $@ /tmp/backup;echo rm $@'
[root@slions_pc1 home]# rmm testfile
cp /tmp/backup
rm testfile

从上面的结果中看到cp后的”$@”根本就没有进行扩展,而是空值。

那如果别名定义语句中没有使用分号或其他方法定义额外的命令,而是只有一个命令呢?别名一定就能正确工作吗?

测试会发现也有问题:

1
2
3
[root@slions_pc1 home]# alias rmm='echo cp $@ /tmp/backup'
[root@slions_pc1 home]# rmm testfile
cp /tmp/backup testfile

之所以无法正常工作,是因为/tmp/backup也是”$@”的一部分,且是”$@”中最前面的参数。

从上面的分析可以知道,alias是有其缺陷的,它只适合进行简单的命令和参数替换、补全,想要实现复杂的命令替代有点难度。因此man bash中建议尽量使用函数来取代别名。

alias最佳实践

例如,为了让rm安全执行,使用以下方法定义别名:

1
2
3
4
5
6
[root@slions_pc1 home]# alias rmm='move(){ /bin/mv -f $@ /tmp/backup; };move $@'
[root@slions_pc1 home]# rmm testfile
[root@slions_pc1 home]# ls
slions
[root@slions_pc1 home]# ls /tmp/backup/
testfile

因为执行别名时的参数只能传递给最后一个命令即move函数,但”$@”代表的参数可以传递给函数,让函数中的”$@”得到正确的扩展,于是整个别名都能合理且正确地执行。

或者直接定义一个shell function替代rm。例如向/etc/profile.d/rm.sh文件中写入:

1
function rm(){ [ -d /tmp/rmbackup ] || mkdir /tmp/rmbackup;/bin/mv -f $@ /tmp/rmbackup; }
1
2
[root@slions_pc1 home]# chmod +x /etc/profile.d/rm.sh
[root@slions_pc1 home]# source /etc/profile.d/rm.sh

如此,执行rm命令时,便会执行此处定义的rm函数,使得rm变得更安全。但注意,这样的函数默认无法直接在脚本中使用,除非使用 export -f function_name 导出函数,使其可以被子shell继承。

所以,可在/etc/profile.d/rm.sh文件的尾部加上导出语句:

1
2
function rm(){ [ -d /tmp/rmbackup ] || mkdir /tmp/rmbackup;/bin/mv -f $@ /tmp/rmbackup; }
export -f rm

如果function名和命令名相同,则默认优先执行function,除非使用command明确指定。例如上面定义了rm函数,如果想执行rm命令,除了使用/bin/rm,还可以如下操作:

1
command rm testfile

最后,如果是**在shell脚本里涉及到rm命令,那么更建议在每次rm之前先cd到那个目录下,然后再rm相对路径,这样至少能保证不出现符号”/“**。当然,最重要的是习惯和责任心,切勿忙中出错。

CATALOG
  1. 1. alias的用法
  2. 2. alias与rm
    1. 2.1. alias的bug
    2. 2.2. alias最佳实践