bash中的冒号

作者: | 更新日期:

前段时间有人在群上发了一个shell命令,问谁敢运行,还说最好在虚拟里运行.于是我研究了一下.

本文首发于公众号:天空的代码世界,微信号:tiankonguse

前言

先来shell命令吧.

:(){ :|:& };:

** 2015.4.21 更新 **

感谢 nnkken 言简意明的告诉我真正的原因在于无限递归fork子进程。
而这几天我刚好在看《UNIX环境高级编程》 的第九章, 正好第9小节讲的有shell执行程序的原理,于是可以有理有据的更新一下了。
当然,可能写的还有很多不足或错误之处,可以留言告诉我,然后我完善一下。

shell中的冒号

看到这个shell代码,我第一个想法是难道是冒号的特殊用法?

于是查询了一下冒号的用法.

变量默认值

当变量VAR没有声明或者为NULL时,将VAR设置为默认值DEFAULT。

${VAR:=DEFAULT}

例如下面的测试代码

tiankonguse@tiankonguse:~$ base="hello"

tiankonguse@tiankonguse:~$ echo ${first:="word"}
word
tiankonguse@tiankonguse:~$ echo $first
word

tiankonguse@tiankonguse:~$ second="tiankonguse"
tiankonguse@tiankonguse:~$ echo $second
tiankonguse

tiankonguse@tiankonguse:~$ echo ${second:="word"}
tiankonguse

空语句

学过 python 的人都知道 python 中有 pass 这个语法.

bash 中的冒号也有这个作用.

if [ $today == "2011-08-29" ]; then  
    :  
else  
    echo $today;
fi  

清空文件

: > test

shell 函数

看了那几个冒号的作用, 发现那段代码都不符合.

:(){ :|:& };:

然后自己冷静下来分析一下, 发现这不就是一个函数的递归定义然后调用吗?

分析

:(){ :|:& } 是函数定义.

: 是函数调用.

多条 bash 语句写在同一行时,就需要使逗号分隔了.

在函数定义中, :|:& 是函数体.

然后 :|:& 又可以看做 调用函数 : 后把输出作为函数 : 的输入,并后台运行.

这样的话相当于程序死循环和无限递归了,自然系统就会有问题了.

~~ 我们还可以简写为 :(){ :; };: 吧. ~~

上面我说死循环和无限递归的时候, 并没有意识到这样做对机器有什么影响。

死循环和无限递归就像下面的例子,对系统影响不大的。

while(1){
    ;
}

void dfs(){
    dfs();
}

为什么影响不大呢?
如果是这样的话, 程序一直在运行,但是只消耗CPU资源而已。

但是我们使用管道后就不一样了。

比如这本书《UNIX环境高级编程》上面的这个图

img-shell-pie

我这边测试时这个样子

tiankonguse:bin $ ps -f | ./cat1 | ./cat2 &
[1] 26964

tiankonguse:bin $ 
    UID        PID  PPID  C STIME TTY          TIME CMD
tiankonguse  15822 15821  0 09:12 pts/0    00:00:00 -bash
tiankonguse  26962 15822  0 09:34 pts/0    00:00:00 ps -f
tiankonguse  26963 15822  0 09:34 pts/0    00:00:00 ./cat1
tiankonguse  26964 15822  0 09:34 pts/0    00:00:00 ./cat2

[1]+  Done                    ps -f | ./cat1 | ./cat2

我们使用管道时,它会fork子进程。

开启子进程会消耗FD的。

然后结果就像 nnkken 说的那样,结果是下面的样子。

while(1) { 
    fork(); 
}

系统死了的原因是FD使用完了。

bash 内的特殊字符

之前我曾总结过, bash 内特殊字符有这些. 特殊字符和引用

当然, 那个文档是去年4月份整理的,那时候才学疏浅的我不知道有没有总结错误.

我们就假设没错把, 那样的话, 里面没有看到冒号,所以我们可以使用冒号当做函数名字了.

测试

tiankonguse@tiankonguse:~$ :(){ echo "tiankonguse $FUNCNAME"; };:;
tiankonguse :

类似的字符还有很多, 比如

tiankonguse@tiankonguse:~$ .(){ echo "tiankonguse $FUNCNAME"; };.;
tiankonguse .

参考资料

本文首发于公众号:天空的代码世界,微信号:tiankonguse
如果你想留言,可以在微信里面关注公众号进行留言。

关注公众号,接收最新消息

tiankonguse +
穿越