【shell】遞歸函數----調用自身的函數


什么是遞歸函數?
一句話,調用自己的函數稱為遞歸函數!
#!/bin/bash
declare -i count

checkoutCount(){
    read -p "Enter an count: " count
    if [ $count -eq 100 ]; then
        echo "Count is 100."
    else
        if [ $count -gt 100 ]; then
            echo "count is greater than 100."
        else
            echo "count is less than 100."
        fi
    fi
    checkoutCount
}
checkoutCount

結果展示:

經典的遞歸函數----江湖俗稱"fork 炸彈"

可能很多人都曾經聽說過 fork 炸彈,它實際上只是一個非常簡單的遞歸程序,程序所做的事情只有一樣:不斷 fork 一個新進程。由於程序是遞歸的,如果沒有任何限制,這會導致這個簡單的程序迅速耗盡系統里面的所有資源。

清單1. bash 中的 fork 炸彈

.(){ .|.& };.

清單2. bash 中的 fork 炸彈的解釋

 1 .()

  2 {

  3  .|.& 

  4 }

  5 ;

  6 .

第 1 行說明下面要定義一個函數,函數名為小數點,沒有可選參數。
第 2 行表示函數體開始。
第 3 行是函數體真正要做的事情,首先它遞歸調用本函數,然后利用管道調用一個新進程(它要做的事情也是遞歸調用本函數),並將其放到后台執行。
第 4 行表示函數體結束。
第 5 行並不會執行什么操作,在命令行中用來分隔兩個命令用。從總體來看,它表明這段程序包含兩個部分,首先定義了一個函數,然后調用這個函數。
第 6 行表示調用本函數。

對 於函數名,大家可能會有所疑惑,小數點也能做函數名使用嗎?畢竟小數點是 shell 的一個內嵌命令,用來在當前 shell 環境中讀取指定文件,並運行其中的命令。實際上的確可以,這取決於 bash 對命令的解釋順序。默認情況下,bash 處於非 POSIX 模式,此時對命令的解釋順序如下:

關鍵字,例如 if、for 等。
別名。別名不能與關鍵字相同,但是可以為關鍵字定義別名,例如 end=fi。
特殊內嵌命令,例如 break、continue 等。POSIX 定義的特殊內嵌命令包括:.(小數點)、:(冒號)、break、continue、eval、exec、exit、export、readonly、 return、set、shift、times、trap 和 unset。bash 又增加了一個特殊的內嵌命令 source。
函數。如果處於非 POSIX 模式,bash 會優先匹配函數,然后再匹配內嵌命令。
非特殊內嵌命令,例如 cd、test 等。
腳本和可執行程序。在 PATH 環境變量指定的目錄中進行搜索,返回第一個匹配項。
由 於默認情況下,bash 處於非 POSIX 模式,因此 fork 炸彈中的小數點會優先當成一個函數進行匹配。(實際上,Jaromil 最初的設計並沒有使用小數點,而是使用的冒號,也能起到完全相同的效果。)要使用 POSIX 模式來運行 bash 腳本,可以使用以下三種方法:

使用 --posix 選項啟動 bash。
在運行 bash 之后,執行 set -o posix 命令。
使用 /bin/sh 。
最 后一種方法比較有趣,盡管 sh 在大部分系統上是一個指向 bash 的符號鏈接,但是它所啟用的卻是 POSIX 模式,所有的行為都完全遵守 POSIX 規范。在清單 3 給出的例子中,我們可以發現,小數點在默認 bash 中被解釋成一個函數,能夠正常執行;但是在 sh 中,小數點卻被當作一個內嵌命令,因此調用函數時會被認為存在語法錯誤,無法正常執行。

清單3. bash 與 sh 對命令匹配順序的區別

[root@localhost ~]# ls -l /bin/bash /bin/sh

-rwxr-xr-x 1 root root 735144 2007-08-31 22:20 /bin/bash

lrwxrwxrwx 1 root root      4 2007-12-18 13:26 /bin/sh -> bash

[root@localhost ~]# echo $SHELL

/bin/bash

[root@localhost ~]# .() { echo hello; } ; .

hello

[root@localhost ~]# sh

sh-3.2# echo $SHELL

/bin/bash

sh-3.2# .() { echo hello; } ; .

sh: `.': not a valid identifier

sh: .: filename argument required

.: usage: . filename [arguments]

sh-3.2#

一旦運行清單 1 給出的 fork 炸彈,會以2的指數次冪的速度不斷產生新進程,這會導致系統資源會被迅速耗光,最終除非重新啟動機器,否則基本上就毫無辦法了。為了防止這會造成太大的損 害,我們可以使用 ulimit 限制每個用戶能夠創建的進程數,如清單 4 所示。

清單4. 限制用戶可以創建的進程數

[root@localhost ~]# ulimit -u 128

[root@localhost ~]# ulimit -a

core file size          (blocks, -c) 0

data seg size           (kbytes, -d) unlimited

max nice                        (-e) 20

file size               (blocks, -f) unlimited

pending signals                 (-i) unlimited

max locked memory       (kbytes, -l) unlimited

max memory size         (kbytes, -m) unlimited

open files                      (-n) 1024

pipe size            (512 bytes, -p) 8

POSIX message queues     (bytes, -q) unlimited

max rt priority                 (-r) unlimited

stack size              (kbytes, -s) 8192

cpu time               (seconds, -t) unlimited

max user processes              (-u) 128

virtual memory          (kbytes, -v) unlimited

file locks                      (-x) unlimited

[root@localhost ~]# .() { .|.& } ; .

[1] 6152

[root@localhost ~]# bash: fork: Resource temporarily unavailable

bash: fork: Resource temporarily unavailable

bash: fork: Resource temporarily unavailable

在清單 4 中,我們將用戶可以創建的最大進程數限制為 128,執行 fork 炸彈會迅速 fork 出大量進程,此后會由於資源不足而無法繼續執行。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM