鍍金池/ 教程/ Linux/ 奇珍異寶
網(wǎng)絡(luò)系統(tǒng)
打印
重定向
使用命令
位置參數(shù)
權(quán)限
文本處理
疑難排解
layout: book-zh title: 自定制 shell 提示符
查找文件
layout: book-zh title: vi 簡(jiǎn)介
shell 環(huán)境
什么是 shell
編譯程序
鍵盤(pán)高級(jí)操作技巧
流程控制:case 分支
流程控制:if 分支結(jié)構(gòu)
layout: book-zh title: 軟件包管理
進(jìn)程
存儲(chǔ)媒介
格式化輸出
編寫(xiě)第一個(gè) Shell 腳本
啟動(dòng)一個(gè)項(xiàng)目
流程控制:while/until 循環(huán)
文件系統(tǒng)中跳轉(zhuǎn)
字符串和數(shù)字
讀取鍵盤(pán)輸入
歸檔和備份
探究操作系統(tǒng)
流程控制:for 循環(huán)
自頂向下設(shè)計(jì)
數(shù)組
操作文件和目錄
奇珍異寶
從 shell 眼中看世界
正則表達(dá)式

奇珍異寶

在我們 bash 學(xué)習(xí)旅程中的最后一站,我們將看一些零星的知識(shí)點(diǎn)。當(dāng)然我們?cè)谥暗恼鹿?jié)中已經(jīng) 涵蓋了很多方面,但是還有許多 bash 特性我們沒(méi)有涉及到。其中大部分特性相當(dāng)晦澀,主要對(duì) 那些把 bash 集成到 Linux 發(fā)行版的程序有用處。然而還有一些特性,雖然不常用, 但是對(duì)某些程序問(wèn)題是很有幫助的。我們將在這里介紹它們。

組命令和子 shell

bash 允許把命令組合在一起??梢酝ㄟ^(guò)兩種方式完成;要么用一個(gè) group 命令,要么用一個(gè)子 shell。 這里是每種方式的語(yǔ)法示例:

組命令:

{ command1; command2; [command3; ...] }

子 shell:

(command1; command2; [command3;...])

這兩種形式的不同之處在于,組命令用花括號(hào)把它的命令包裹起來(lái),而子 shell 用括號(hào)。值得注意的是,鑒于 bash 實(shí)現(xiàn)組命令的方式, 花括號(hào)與命令之間必須有一個(gè)空格,并且最后一個(gè)命令必須用一個(gè)分號(hào)或者一個(gè)換行符終止。

那么組命令和子 shell 命令對(duì)什么有好處呢? 盡管它們有一個(gè)很重要的差異(我們馬上會(huì)接觸到),但它們都是用來(lái)管理重定向的。 讓我們考慮一個(gè)對(duì)多個(gè)命令執(zhí)行重定向的腳本片段。

ls -l > output.txt
echo "Listing of foo.txt" >> output.txt
cat foo.txt >> output.txt

這些代碼相當(dāng)簡(jiǎn)潔明了。三個(gè)命令的輸出都重定向到一個(gè)名為 output.txt 的文件中。 使用一個(gè)組命令,我們可以重新編 寫(xiě)這些代碼,如下所示:

{ ls -l; echo "Listing of foo.txt"; cat foo.txt; } > output.txt

使用一個(gè)子 shell 是相似的:

(ls -l; echo "Listing of foo.txt"; cat foo.txt) > output.txt

使用這樣的技術(shù),我們?yōu)槲覀冏约汗?jié)省了一些打字時(shí)間,但是組命令和子 shell 真正閃光的地方是與管道線相結(jié)合。 當(dāng)構(gòu)建一個(gè)管道線命令的時(shí)候,通常把幾個(gè)命令的輸出結(jié)果合并成一個(gè)流是很有用的。 組命令和子 shell 使這種操作變得很簡(jiǎn)單:

{ ls -l; echo "Listing of foo.txt"; cat foo.txt; } | lpr

這里我們已經(jīng)把我們的三個(gè)命令的輸出結(jié)果合并在一起,并把它們用管道輸送給命令 lpr 的輸入,以便產(chǎn)生一個(gè)打印報(bào)告。

在下面的腳本中,我們將使用組命令,看幾個(gè)與關(guān)聯(lián)數(shù)組結(jié)合使用的編程技巧。這個(gè)腳本,稱為 array-2,當(dāng)給定一個(gè)目錄名,打印出目錄中的文件列表, 伴隨著每個(gè)文件的文件所有者和組所有者。在文件列表的末尾,腳本打印出屬于每個(gè)所有者和組的文件數(shù)目。 這里我們看到的結(jié)果(縮短的,為簡(jiǎn)單起見(jiàn)),是給定腳本的目錄為 /usr/bin 的時(shí)候:

[me@linuxbox ~]$ array-2 /usr/bin
/usr/bin/2to3-2.6                 root        root
/usr/bin/2to3                     root        root
/usr/bin/a2p                      root        root
/usr/bin/abrowser                 root        root
/usr/bin/aconnect                 root        root
/usr/bin/acpi_fakekey             root        root
/usr/bin/acpi_listen              root        root
/usr/bin/add-apt-repository       root        root
.
/usr/bin/zipgrep                  root        root
/usr/bin/zipinfo                  root        root
/usr/bin/zipnote                  root        root
/usr/bin/zip                      root        root
/usr/bin/zipsplit                 root        root
/usr/bin/zjsdecode                root        root
/usr/bin/zsoelim                  root        root

File owners:
daemon  : 1 file(s)
root    : 1394 file(s) File group owners:
crontab : 1 file(s)
daemon  : 1 file(s)
lpadmin : 1 file(s)
mail    : 4 file(s)
mlocate : 1 file(s)
root    : 1380 file(s)
shadow  : 2 file(s)
ssh     : 1 file(s)
tty     : 2 file(s)
utmp    : 2 file(s)

這里是腳本代碼列表(帶有行號(hào)):

1     #!/bin/bash
2
3     # array-2: Use arrays to tally file owners
4
5     declare -A files file_group file_owner groups owners
6
7     if [[ ! -d "$1" ]]; then
8        echo "Usage: array-2 dir" >&2
9        exit 1
10    fi
11
12    for i in "$1"/*; do
13       owner=$(stat -c %U "$i")
14       group=$(stat -c %G "$i")
15        files["$i"]="$i"
16        file_owner["$i"]=$owner
17        file_group["$i"]=$group
18        ((++owners[$owner]))
19        ((++groups[$group]))
20    done
21
22    # List the collected files
23    { for i in "${files[@]}"; do
24    printf "%-40s %-10s %-10s\n" \
25    "$i" ${file_owner["$i"]} ${file_group["$i"]}
26    done } | sort
27    echo
28
29   # List owners
30    echo "File owners:"
31    { for i in "${!owners[@]}"; do
32    printf "%-10s: %5d file(s)\n" "$i" ${owners["$i"]}
33    done } | sort
34    echo
35
36    # List groups
37    echo "File group owners:"
38    { for i in "${!groups[@]}"; do
39    printf "%-10s: %5d file(s)\n" "$i" ${groups["$i"]}
40    done } | sort

讓我們看一下這個(gè)腳本的運(yùn)行機(jī)制:

行5: 關(guān)聯(lián)數(shù)組必須用帶有 -A 選項(xiàng)的 declare 命令創(chuàng)建。在這個(gè)腳本中我們創(chuàng)建了如下五個(gè)數(shù)組:

files 包含了目錄中文件的名字,按文件名索引

file_group 包含了每個(gè)文件的組所有者,按文件名索引

file_owner 包含了每個(gè)文件的所有者,按文件名索引

groups 包含了屬于索引的組的文件數(shù)目

owners 包含了屬于索引的所有者的文件數(shù)目

行7-10:查看是否一個(gè)有效的目錄名作為位置參數(shù)傳遞給程序。如果不是,就會(huì)顯示一條使用信息,并且腳本退出,退出狀態(tài)為1。

行12-20:循環(huán)遍歷目錄中的所有文件。使用 stat 命令,行13和行14抽取文件所有者和組所有者, 并把值賦給它們各自的數(shù)組(行16,17),使用文件名作為數(shù)組索引。同樣地,文件名自身也賦值給 files 數(shù)組。

行18-19:屬于文件所有者和組所有者的文件總數(shù)各自加1。

行22-27:輸出文件列表。為做到這一點(diǎn),使用了 “${array[@]}” 參數(shù)展開(kāi),展開(kāi)成整個(gè)的數(shù)組元素列表, 并且每個(gè)元素被當(dāng)做是一個(gè)單獨(dú)的詞。從而允許文件名包含空格的情況。也要注意到整個(gè)循環(huán)是包裹在花括號(hào)中, 從而形成了一個(gè)組命令。這樣就允許整個(gè)循環(huán)輸出會(huì)被管道輸送給 sort 命令的輸入。這是必要的,因?yàn)?展開(kāi)的數(shù)組元素是無(wú)序的。

行29-40:這兩個(gè)循環(huán)與文件列表循環(huán)相似,除了它們使用 “${!array[@]}” 展開(kāi),展開(kāi)成數(shù)組索引的列表 而不是數(shù)組元素的。

進(jìn)程替換

雖然組命令和子 shell 看起來(lái)相似,并且它們都能用來(lái)在重定向中合并流,但是兩者之間有一個(gè)很重要的不同。 然而,一個(gè)組命令在當(dāng)前 shell 中執(zhí)行它的所有命令,而一個(gè)子 shell(顧名思義)在當(dāng)前 shell 的一個(gè) 子副本中執(zhí)行它的命令。這意味著運(yùn)行環(huán)境被復(fù)制給了一個(gè)新的 shell 實(shí)例。當(dāng)這個(gè)子 shell 退出時(shí),環(huán)境副本會(huì)消失, 所以在子 shell 環(huán)境(包括變量賦值)中的任何更改也會(huì)消失。因此,在大多數(shù)情況下,除非腳本要求一個(gè)子 shell, 組命令比子 shell 更受歡迎。組命令運(yùn)行很快并且占用的內(nèi)存也少。

我們?cè)诘?0章中看到過(guò)一個(gè)子 shell 運(yùn)行環(huán)境問(wèn)題的例子,當(dāng)我們發(fā)現(xiàn)管道線中的一個(gè) read 命令 不按我們所期望的那樣工作的時(shí)候。為了重現(xiàn)問(wèn)題,我們構(gòu)建一個(gè)像這樣的管道線:

echo "foo" | read
echo $REPLY

該 REPLY 變量的內(nèi)容總是為空,是因?yàn)檫@個(gè) read 命令在一個(gè)子 shell 中執(zhí)行,所以它的 REPLY 副本會(huì)被毀掉, 當(dāng)該子 shell 終止的時(shí)候。因?yàn)楣艿谰€中的命令總是在子 shell 中執(zhí)行,任何給變量賦值的命令都會(huì)遭遇這樣的問(wèn)題。 幸運(yùn)地是,shell 提供了一種奇異的展開(kāi)方式,叫做進(jìn)程替換,它可以用來(lái)解決這種麻煩。進(jìn)程替換有兩種表達(dá)方式:

一種適用于產(chǎn)生標(biāo)準(zhǔn)輸出的進(jìn)程:

<(list)

另一種適用于接受標(biāo)準(zhǔn)輸入的進(jìn)程:

>(list)

這里的 list 是一串命令列表:

為了解決我們的 read 命令問(wèn)題,我們可以雇傭進(jìn)程替換,像這樣:

read < <(echo "foo")
echo $REPLY

進(jìn)程替換允許我們把一個(gè)子 shell 的輸出結(jié)果當(dāng)作一個(gè)用于重定向的普通文件。事實(shí)上,因?yàn)樗且环N展開(kāi)形式,我們可以檢驗(yàn)它的真實(shí)值:

[me@linuxbox ~]$ echo <(echo "foo")
/dev/fd/63

通過(guò)使用 echo 命令,查看展開(kāi)結(jié)果,我們看到子 shell 的輸出結(jié)果,由一個(gè)名為 /dev/fd/63 的文件提供。

進(jìn)程替換經(jīng)常被包含 read 命令的循環(huán)用到。這里是一個(gè) read 循環(huán)的例子,處理一個(gè)目錄列表的內(nèi)容,內(nèi)容創(chuàng)建于一個(gè)子 shell:

#!/bin/bash
# pro-sub : demo of process substitution
while read attr links owner group size date time filename; do
    cat <<- EOF
        Filename:     $filename
        Size:         $size
        Owner:        $owner
        Group:        $group
        Modified:     $date $time
        Links:        $links
        Attributes:   $attr
    EOF
done < <(ls -l | tail -n +2)

這個(gè)循環(huán)對(duì)目錄列表的每一個(gè)條目執(zhí)行 read 命令。列表本身產(chǎn)生于該腳本的最后一行代碼。這一行代碼把從進(jìn)程替換得到的輸出 重定向到這個(gè)循環(huán)的標(biāo)準(zhǔn)輸入。這個(gè)包含在管道線中的 tail 命令,是為了消除列表的第一行文本,這行文本是多余的。

當(dāng)腳本執(zhí)行后,腳本產(chǎn)生像這樣的輸出:

[me@linuxbox ~]$ pro_sub | head -n 20
Filename: addresses.ldif
Size: 14540
Owner: me
Group: me
Modified: 2009-04-02 11:12
Links:
1
Attributes: -rw-r--r--
Filename: bin
Size: 4096
Owner: me
Group: me
Modified: 2009-07-10 07:31
Links: 2
Attributes: drwxr-xr-x
Filename: bookmarks.html
Size: 394213
Owner: me
Group: me

陷阱

在第10章中,我們看到過(guò)程序是怎樣響應(yīng)信號(hào)的。我們也可以把這個(gè)功能添加到我們的腳本中。然而到目前為止, 我們所編寫(xiě)過(guò)的腳本還不需要這種功能(因?yàn)樗鼈冞\(yùn)行時(shí)間非常短暫,并且不創(chuàng)建臨時(shí)文件),大且更復(fù)雜的腳本 可能會(huì)受益于一個(gè)信息處理程序。

當(dāng)我們?cè)O(shè)計(jì)一個(gè)大的,復(fù)雜的腳本的時(shí)候,若腳本仍在運(yùn)行時(shí),用戶注銷或關(guān)閉了電腦,這時(shí)候會(huì)發(fā)生什么,考慮到這一點(diǎn)非常重要。 當(dāng)像這樣的事情發(fā)生了,一個(gè)信號(hào)將會(huì)發(fā)送給所有受到影響的進(jìn)程。依次地,代表這些進(jìn)程的程序會(huì)執(zhí)行相應(yīng)的動(dòng)作,來(lái)確保程序 合理有序的終止。比方說(shuō),例如,我們編寫(xiě)了一個(gè)會(huì)在執(zhí)行時(shí)創(chuàng)建臨時(shí)文件的腳本。在一個(gè)好的設(shè)計(jì)流程,我們應(yīng)該讓腳本刪除創(chuàng)建的 臨時(shí)文件,當(dāng)腳本完成它的任務(wù)之后。若腳本接收到一個(gè)信號(hào),表明該程序即將提前終止的信號(hào), 此時(shí)讓腳本刪除創(chuàng)建的臨時(shí)文件,也會(huì)是很精巧的設(shè)計(jì)。

為滿足這樣需求,bash 提供了一種機(jī)制,眾所周知的 trap。陷阱由被恰當(dāng)命令的內(nèi)部命令 trap 實(shí)現(xiàn)。 trap 使用如下語(yǔ)法:

trap argument signal [signal...]

這里的 argument 是一個(gè)字符串,它被讀取并被當(dāng)作一個(gè)命令,signal 是一個(gè)信號(hào)的說(shuō)明,它會(huì)觸發(fā)執(zhí)行所要解釋的命令。

這里是一個(gè)簡(jiǎn)單的例子:

#!/bin/bash
# trap-demo : simple signal handling demo
trap "echo 'I am ignoring you.'" SIGINT SIGTERM
for i in {1..5}; do
    echo "Iteration $i of 5"
    sleep 5
done

這個(gè)腳本定義一個(gè)陷阱,當(dāng)腳本運(yùn)行的時(shí)候,這個(gè)陷阱每當(dāng)接受到一個(gè) SIGINT 或 SIGTERM 信號(hào)時(shí),就會(huì)執(zhí)行一個(gè) echo 命令。 當(dāng)用戶試圖通過(guò)按下 Ctrl-c 組合鍵終止腳本運(yùn)行的時(shí)候,該程序的執(zhí)行結(jié)果看起來(lái)像這樣:

[me@linuxbox ~]$ trap-demo
Iteration 1 of 5
Iteration 2 of 5
I am ignoring you.
Iteration 3 of 5
I am ignoring you.
Iteration 4 of 5
Iteration 5 of 5

正如我們所看到的,每次用戶試圖中斷程序時(shí),會(huì)打印出這條信息。

構(gòu)建一個(gè)字符串形成一個(gè)有用的命令序列是很笨拙的,所以通常的做法是指定一個(gè) shell 函數(shù)作為命令。在這個(gè)例子中, 為每一個(gè)信號(hào)指定了一個(gè)單獨(dú)的 shell 函數(shù)來(lái)處理:

#!/bin/bash
# trap-demo2 : simple signal handling demo
exit_on_signal_SIGINT () {
    echo "Script interrupted." 2>&1
    exit 0
}
exit_on_signal_SIGTERM () {
    echo "Script terminated." 2>&1
    exit 0
}
trap exit_on_signal_SIGINT SIGINT
trap exit_on_signal_SIGTERM SIGTERM
for i in {1..5}; do
    echo "Iteration $i of 5"
    sleep 5
done

這個(gè)腳本的特色是有兩個(gè) trap 命令,每個(gè)命令對(duì)應(yīng)一個(gè)信號(hào)。每個(gè) trap,依次,當(dāng)接受到相應(yīng)的特殊信號(hào)時(shí), 會(huì)執(zhí)行指定的 shell 函數(shù)。注意每個(gè)信號(hào)處理函數(shù)中都包含了一個(gè) exit 命令。沒(méi)有 exit 命令, 信號(hào)處理函數(shù)執(zhí)行完后,該腳本將會(huì)繼續(xù)執(zhí)行。

當(dāng)用戶在這個(gè)腳本執(zhí)行期間,按下 Ctrl-c 組合鍵的時(shí)候,輸出結(jié)果看起來(lái)像這樣:

[me@linuxbox ~]$ trap-demo2
Iteration 1 of 5
Iteration 2 of 5
Script interrupted.

臨時(shí)文件

把信號(hào)處理程序包含在腳本中的一個(gè)原因是刪除臨時(shí)文件,在腳本執(zhí)行期間,腳本可能會(huì)創(chuàng)建臨時(shí)文件來(lái)存放中間結(jié)果。 命名臨時(shí)文件是一種藝術(shù)。傳統(tǒng)上,在類似于 unix 系統(tǒng)中的程序會(huì)在 /tmp 目錄下創(chuàng)建它們的臨時(shí)文件,/tmp 是 一個(gè)服務(wù)于臨時(shí)文件的共享目錄。然而,因?yàn)檫@個(gè)目錄是共享的,這會(huì)引起一定的安全顧慮,尤其對(duì)那些用 超級(jí)用戶特權(quán)運(yùn)行的程序。除了為暴露給系統(tǒng)中所有用戶的文件設(shè)置合適的權(quán)限,這一明顯步驟之外, 給臨時(shí)文件一個(gè)不可預(yù)測(cè)的文件名是很重要的。這就避免了一種為大眾所知的 temp race 攻擊。 一種創(chuàng)建一個(gè)不可預(yù)測(cè)的(但是仍有意義的)臨時(shí)文件名的方法是,做一些像這樣的事情:

tempfile=/tmp/$(basename $0).$$.$RANDOM

這將創(chuàng)建一個(gè)由程序名字,程序進(jìn)程的 ID(PID)文件名,和一個(gè)隨機(jī)整數(shù)組成。注意,然而,該 $RANDOM shell 變量 只能返回一個(gè)范圍在1-32767內(nèi)的整數(shù)值,這在計(jì)算機(jī)術(shù)語(yǔ)中不是一個(gè)很大的范圍,所以一個(gè)單一的該變量實(shí)例是不足以克服一個(gè)堅(jiān)定的攻擊者的。

一個(gè)比較好的方法是使用 mktemp 程序(不要和 mktemp 標(biāo)準(zhǔn)庫(kù)函數(shù)相混淆)來(lái)命名和創(chuàng)建臨時(shí)文件。 這個(gè) mktemp 程序接受一個(gè)用于創(chuàng)建文件名的模板作為參數(shù)。這個(gè)模板應(yīng)該包含一系列的 “X” 字符, 隨后這些字符會(huì)被相應(yīng)數(shù)量的隨機(jī)字母和數(shù)字替換掉。一連串的 “X” 字符越長(zhǎng),則一連串的隨機(jī)字符也就越長(zhǎng)。 這里是一個(gè)例子:

tempfile=$(mktemp /tmp/foobar.$$.XXXXXXXXXX)

這里創(chuàng)建了一個(gè)臨時(shí)文件,并把臨時(shí)文件的名字賦值給變量 tempfile。因?yàn)槟0逯械?“X” 字符會(huì)被隨機(jī)字母和 數(shù)字代替,所以最終的文件名(在這個(gè)例子中,文件名也包含了特殊參數(shù) $$ 的展開(kāi)值,進(jìn)程的 PID)可能像這樣:

/tmp/foobar.6593.UOZuvM6654

對(duì)于那些由普通用戶操作執(zhí)行的腳本,避免使用 /tmp 目錄,而是在用戶家目錄下為臨時(shí)文件創(chuàng)建一個(gè)目錄, 通過(guò)像這樣的一行代碼:

[[ -d $HOME/tmp ]] || mkdir $HOME/tmp

異步執(zhí)行

有時(shí)候需要同時(shí)執(zhí)行多個(gè)任務(wù)。我們已經(jīng)知道現(xiàn)在所有的操作系統(tǒng)若不是多用戶的但至少是多任務(wù)的。 腳本也可以構(gòu)建成多任務(wù)處理的模式。

通常這涉及到啟動(dòng)一個(gè)腳本,依次,啟動(dòng)一個(gè)或多個(gè)子腳本來(lái)執(zhí)行額外的任務(wù),而父腳本繼續(xù)運(yùn)行。然而,當(dāng)一系列腳本 以這種方式運(yùn)行時(shí),要保持父子腳本之間協(xié)調(diào)工作,會(huì)有一些問(wèn)題。也就是說(shuō),若父腳本或子腳本依賴于另一方,并且 一個(gè)腳本必須等待另一個(gè)腳本結(jié)束任務(wù)之后,才能完成它自己的任務(wù),這應(yīng)該怎么辦?

bash 有一個(gè)內(nèi)置命令,能幫助管理諸如此類的異步執(zhí)行的任務(wù)。wait 命令導(dǎo)致一個(gè)父腳本暫停運(yùn)行,直到一個(gè) 特定的進(jìn)程(例如,子腳本)運(yùn)行結(jié)束。

等待

首先我們將演示一下 wait 命令的用法。為此,我們需要兩個(gè)腳本,一個(gè)父腳本:

#!/bin/bash
# async-parent : Asynchronous execution demo (parent)
echo "Parent: starting..."
echo "Parent: launching child script..."
async-child &
pid=$!
echo "Parent: child (PID= $pid) launched."
echo "Parent: continuing..."
sleep 2
echo "Parent: pausing to wait for child to finish..."
wait $pid
echo "Parent: child is finished. Continuing..."
echo "Parent: parent is done. Exiting."

和一個(gè)子腳本:

#!/bin/bash
# async-child : Asynchronous execution demo (child)
echo "Child: child is running..."
sleep 5
echo "Child: child is done. Exiting."

在這個(gè)例子中,我們看到該子腳本是非常簡(jiǎn)單的。真正的操作通過(guò)父腳本完成。在父腳本中,子腳本被啟動(dòng), 并被放置到后臺(tái)運(yùn)行。子腳本的進(jìn)程 ID 記錄在 pid 變量中,這個(gè)變量的值是 $! shell 參數(shù)的值,它總是 包含放到后臺(tái)執(zhí)行的最后一個(gè)任務(wù)的進(jìn)程 ID 號(hào)。

父腳本繼續(xù),然后執(zhí)行一個(gè)以子進(jìn)程 PID 為參數(shù)的 wait 命令。這就導(dǎo)致父腳本暫停運(yùn)行,直到子腳本退出, 意味著父腳本結(jié)束。

當(dāng)執(zhí)行后,父子腳本產(chǎn)生如下輸出:

[me@linuxbox ~]$ async-parent
Parent: starting...
Parent: launching child script...
Parent: child (PID= 6741) launched.
Parent: continuing...
Child: child is running...
Parent: pausing to wait for child to finish...
Child: child is done. Exiting.
Parent: child is finished. Continuing...
Parent: parent is done. Exiting.

命名管道

在大多數(shù)類似也 Unix 的操作系統(tǒng)中,有可能創(chuàng)建一種特殊類型的餓文件,叫做命名管道。命名管道用來(lái)在 兩個(gè)進(jìn)程之間建立連接,也可以像其它類型的文件一樣使用。雖然它們不是那么流行,但是它們值得我們?nèi)チ私狻?/p>

有一種常見(jiàn)的編程架構(gòu),叫做客戶端-服務(wù)器,它可以利用像命名管道這樣的通信方式, 也可以使用其它類型的進(jìn)程間通信方式,比如網(wǎng)絡(luò)連接。

最為廣泛使用的客戶端-服務(wù)器系統(tǒng)類型是,當(dāng)然,一個(gè) web 瀏覽器與一個(gè) web 服務(wù)器之間進(jìn)行通信。 web 瀏覽器作為客戶端,向服務(wù)器發(fā)出請(qǐng)求,服務(wù)器響應(yīng)請(qǐng)求,并把對(duì)應(yīng)的網(wǎng)頁(yè)發(fā)送給瀏覽器。

命令管道的行為類似于文件,但實(shí)際上形成了先入先出(FIFO)的緩沖。和普通(未命令的)管道一樣, 數(shù)據(jù)從一端進(jìn)入,然后從另一端出現(xiàn)。通過(guò)命令管道,有可能像這樣設(shè)置一些東西:

process1 > named_pipe

process2 < named_pipe

表現(xiàn)出來(lái)就像這樣:

process1 | process2

設(shè)置一個(gè)命名管道

首先,我們必須創(chuàng)建一個(gè)命名管道。使用 mkfifo 命令能夠創(chuàng)建命令管道:

[me@linuxbox ~]$ mkfifo pipe1
[me@linuxbox ~]$ ls -l pipe1
prw-r--r-- 1 me
me
0 2009-07-17 06:41 pipe1

這里我們使用 mkfifo 創(chuàng)建了一個(gè)名為 pipe1 的命名管道。使用 ls 命令,我們查看這個(gè)文件, 看到位于屬性字段的第一個(gè)字母是 “p”,表明它是一個(gè)命名管道。

使用命名管道

為了演示命名管道是如何工作的,我們將需要兩個(gè)終端窗口(或用兩個(gè)虛擬控制臺(tái)代替)。 在第一個(gè)終端中,我們輸入一個(gè)簡(jiǎn)單命令,并把命令的輸出重定向到命名管道:

[me@linuxbox ~]$ ls -l > pipe1

我們按下 Enter 按鍵之后,命令將會(huì)掛起。這是因?yàn)樵诠艿赖牧硪欢藳](méi)有任何接受數(shù)據(jù)。當(dāng)這種現(xiàn)象發(fā)生的時(shí)候, 據(jù)說(shuō)是管道阻塞了。一旦我們綁定一個(gè)進(jìn)程到管道的另一端,該進(jìn)程開(kāi)始從管道中讀取輸入的時(shí)候,這種情況會(huì)消失。 使用第二個(gè)終端窗口,我們輸入這個(gè)命令:

[me@linuxbox ~]$ cat < pipe1

然后產(chǎn)自第一個(gè)終端窗口的目錄列表出現(xiàn)在第二個(gè)終端中,并作為來(lái)自 cat 命令的輸出。在第一個(gè)終端 窗口中的 ls 命令一旦它不再阻塞,會(huì)成功地結(jié)束。

總結(jié)

嗯,我們已經(jīng)完成了我們的旅程?,F(xiàn)在剩下的唯一要做的事就是練習(xí),練習(xí),再練習(xí)。 縱然在我們的長(zhǎng)途跋涉中,我們涉及了很多命令,但是就命令行而言,我們只是觸及了它的表面。 仍留有成千上萬(wàn)的命令行程序,需要去發(fā)現(xiàn)和享受。開(kāi)始挖掘 /usr/bin 目錄吧,你將會(huì)看到!

拓展閱讀