shell: bash中的冒号

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

前言

先来shell命令吧.

  1. :(){ :|:& };:

2015.4.21 更新

感谢 nnkken 言简意明的告诉我真正的原因在于无限递归fork子进程。

而这几天我刚好在看 《UNIX环境高级编程》的第九章, 正好第9小节讲的有shell执行程序的原理,于是可以有理有据的更新一下了。

当然,可能写的还有很多不足或错误之处,可以留言告诉我,然后我完善一下。

shell中的冒号

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

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

变量默认值

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

  1. ${VAR:=DEFAULT}

例如下面的测试代码

  1. tiankonguse@tiankonguse:~$ base="hello>"
  2.  
  3. tiankonguse@tiankonguse:~$ echo ${first:="word>"}
  4. word
  5. tiankonguse@tiankonguse:~$ echo $first
  6. word
  7.  
  8. tiankonguse@tiankonguse:~$ second="tiankonguse>"
  9. tiankonguse@tiankonguse:~$ echo $second
  10. tiankonguse
  11.  
  12. tiankonguse@tiankonguse:~$ echo ${second:="word>"}
  13. tiankonguse

空语句

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

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

  1. if [ $today == "2011-08-29>" ]; then
  2. :
  3. else
  4. echo $today;
  5. fi

清空文件

  1. : > test

shell 函数

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

  1. :(){ :|:& };:

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

分析

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

: 是函数调用.

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

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

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

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

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

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

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

  1. while(1){
  2. ;
  3. }
  4.  
  5. void dfs(){
  6. dfs();
  7. }

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

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

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

shell: bash中的冒号
shell: bash中的冒号

我这边测试时这个样子

  1. tiankonguse:bin $ ps -f | ./cat1 | ./cat2 &
  2. [1] 26964
  3.  
  4. tiankonguse:bin $
  5. UID PID PPID C STIME TTY TIME CMD
  6. tiankonguse 15822 15821 0 09:12 pts/0 00:00:00 -bash
  7. tiankonguse 26962 15822 0 09:34 pts/0 00:00:00 ps -f
  8. tiankonguse 26963 15822 0 09:34 pts/0 00:00:00 ./cat1
  9. tiankonguse 26964 15822 0 09:34 pts/0 00:00:00 ./cat2
  10.  
  11. [1]+ Done ps -f | ./cat1 | ./cat2

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

开启子进程会消耗FD的。

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

  1. while(1) {
  2. fork();
  3. }

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

bash 内的特殊字符

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

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

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

测试

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

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

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

参考资料

原文:http://www.kuqin.com/shuoit/20150424/345862.html

You might also like:

Sidebar