對(duì)話(huà) UNIX,第 12 部分: 自己動(dòng)手完成項(xiàng)目
Unix® 系統(tǒng)中提供了數(shù)百個(gè)實(shí)用工具應(yīng)用程序或者命令。其中一些命令可以操作文件系統(tǒng),而其他的命令則用于查詢(xún)并控制操作系統(tǒng)本身。大量的命令提供了連接性,并且還有更豐富的命令可用于生成、交換、修改、篩選和分析數(shù)據(jù)。由于 UNIX 具有悠久而豐富的歷史,所以您也許能夠找到恰好合適的工具,以用于手頭的任務(wù)。
此外,當(dāng)一個(gè)實(shí)用工具不能滿(mǎn)足需求時(shí),您可以通過(guò)各種各樣的方式結(jié)合任何數(shù)量的 UNIX 實(shí)用工具,以創(chuàng)建您自己的工具。正如您在前面的部分中所看到的,可以利用管道、重定向和條件,直接在命令行中構(gòu)建即時(shí)可用的工具,并且 Shell 腳本將小型的、易于學(xué)習(xí)的編程語(yǔ)言的強(qiáng)大功能與 UNIX 命令結(jié)合在一起,以構(gòu)建可重用工具。
當(dāng)然,在很多情況下,僅依靠命令行和 Shell 腳本是不夠的。例如,如果您必須部署一個(gè)新的守護(hù)進(jìn)程以提供新的網(wǎng)絡(luò)服務(wù),那么您可以使用一種表達(dá)能力更強(qiáng)的語(yǔ)言,如 C 或者 Python,以便自己編寫(xiě)應(yīng)用程序。并且,因?yàn)?Internet 上有許多應(yīng)用程序是免費(fèi)的(免費(fèi) 意味著無(wú)需支付任何費(fèi)用、得到自由條款的許可,或者兩者都有),所以您還可以下載、編譯并安裝適當(dāng)?shù)摹⒂行У慕鉀Q方案,以滿(mǎn)足您的需求。
UNIX(以及 Linux®)的許多版本都提供一種稱(chēng)為包管理器 的特殊工具,用以在系統(tǒng)中添加、刪除和維護(hù)軟件。包管理器通常可以維護(hù)本地安裝的所有軟件的詳細(xì)目錄,以及一個(gè)或者多個(gè)遠(yuǎn)程存儲(chǔ)庫(kù) 中所有可用軟件的目錄。您可以使用包管理器在存儲(chǔ)庫(kù)中搜索您所需要的軟件。如果存儲(chǔ)庫(kù)中包含您正在尋找的軟件,那么您只需要使用一個(gè)命令或者點(diǎn)擊幾下鼠標(biāo),就可以在您的系統(tǒng)中安裝一個(gè)新的包。
包管理器是非常有價(jià)值的。使用它,您可以刪除全部的包、更新現(xiàn)有的包,以及為任何包自動(dòng)地檢測(cè)并實(shí)現(xiàn)任何先決條件。例如,如果您選擇了操作圖像的軟件,如可靠的 ImageMagick,但是您的系統(tǒng)中缺少處理 JPEG 圖像的庫(kù),那么包管理器將在安裝您所選擇的包之前檢測(cè)并安裝缺少的內(nèi)容。
然而,也可能存在這樣的情況,即您所需要的軟件是可獲得的,但它卻不包含于任何存儲(chǔ)庫(kù)中。由于包管理方式具有顯著的優(yōu)勢(shì),所以大多數(shù)軟件都提供了可以使用包管理器進(jìn)行下載并安裝的形式。然而,因?yàn)?Unix 的版本和風(fēng)格非常之多,所以很難針對(duì)每種特定的變體,以各種包管理器的格式提供每個(gè)應(yīng)用程序。如果您的 UNIX 安裝是主流的,并受到大量擁護(hù)者的喜愛(ài),那么您將更有可能找到預(yù)先構(gòu)建的并且可供使用的軟件。否則,您就需要挽起袖子準(zhǔn)備自己動(dòng)手構(gòu)建軟件了。
是的,年輕的絕地武士(《星球大戰(zhàn)》中的武士),是使用源代碼的時(shí)候了。
如同從沼澤中升起一架 X 翼戰(zhàn)斗機(jī)一樣,從源代碼構(gòu)建軟件乍看起來(lái)可能是令人生畏的,特別當(dāng)您不是軟件開(kāi)發(fā)人員的時(shí)候。事實(shí)上,在大多數(shù)情況下,整個(gè)構(gòu)建過(guò)程僅僅只需要少數(shù)幾條命令,其余的工作都是自動(dòng)完成的。
當(dāng)然,某些程序構(gòu)建起來(lái)是非常復(fù)雜的(或者需要花費(fèi)數(shù)小時(shí)來(lái)進(jìn)行構(gòu)建),并且在構(gòu)建過(guò)程中需要進(jìn)行人工介入。然而,即使這些程序通常是由一些容易構(gòu)建的較小的塊構(gòu)造而得到的,依賴(lài)關(guān)系的數(shù)量和構(gòu)造的順序也會(huì)使構(gòu)建過(guò)程變得復(fù)雜。一些程序還有許多您并不一定希望擁有的特性。例如,您可以構(gòu)建 PHP,以便與新的網(wǎng)際協(xié)議版本 6 (IPv6) Internet 尋址方案進(jìn)行互操作。如果您的網(wǎng)絡(luò)尚未采用 IPv6,則不需要包括這個(gè)特性。對(duì)大量選項(xiàng)進(jìn)行的審查將使構(gòu)建過(guò)程變得更加麻煩。
這個(gè)月,讓我們來(lái)研究如何構(gòu)建一個(gè)典型的 UNIX 軟件應(yīng)用程序。在繼續(xù)學(xué)習(xí)后面的內(nèi)容之前,請(qǐng)確保系統(tǒng)中安裝了 C 編譯器,如 GNU 編譯器套裝(GNU Compiler Collection,GCC),以及常見(jiàn)的 UNIX 軟件開(kāi)發(fā)工具套裝,包括 make、m4、pkg-config 和 awk。此外,請(qǐng)確保在您的 PATH 環(huán)境變量中包含了所有的開(kāi)發(fā)工具。
軟件包中有價(jià)值的內(nèi)容
作為一個(gè)說(shuō)明性的和典型的示例,讓我們配置、構(gòu)建并安裝 SQLite——一個(gè)實(shí)現(xiàn)結(jié)構(gòu)化查詢(xún)語(yǔ)言(Structured Query Language,SQL)數(shù)據(jù)庫(kù)引擎的小型的庫(kù)。SQLite 不需要進(jìn)行任何配置即可使用,并且可以完整地嵌入到任何應(yīng)用程序中,而數(shù)據(jù)庫(kù)則包含在單個(gè)文件中。許多編程語(yǔ)言都可以調(diào)用 SQLite 以實(shí)現(xiàn)數(shù)據(jù)的持久化。SQLite 還包括一種用于管理 SQLite 數(shù)據(jù)庫(kù)的、名為 sqlite3 的命令行實(shí)用工具。
要開(kāi)始學(xué)習(xí)這部分內(nèi)容,首先下載 SQLite(請(qǐng)參見(jiàn)參考資料)。選擇最新的源代碼包,并將其下載到您的計(jì)算機(jī)中。(在撰寫(xiě)本文時(shí),SQLite 的最新版本是版本 3.3.17,于 2007 年4 月 25 日發(fā)布。)這個(gè)示例使用了 http://www.sqlite.org/sqlite-3.3.17.tar.gz 中存儲(chǔ)的文件。
在您獲得了該文件之后,請(qǐng)對(duì)其進(jìn)行解壓縮。.tar.gz 擴(kuò)展反映了該存檔文件是如何構(gòu)造的。在這個(gè)示例中,它是一個(gè)壓縮了的 tar 存檔文件。后面的擴(kuò)展 .gz,表示 gzip(壓縮);前面的擴(kuò)展 .tar,表示 tar(一種存檔格式)。要提取該存檔文件的內(nèi)容,只需要對(duì)其進(jìn)行反向處理即可,也就是首先解壓縮,然后打開(kāi)該存檔文件:
$ gunzip sqlite-3.3.17.tar.gz$ tar xvf sqlite-3.3.17.tar
這兩個(gè)命令在一個(gè)名為 sqlite-3.3.17 的新目錄中創(chuàng)建了原始源代碼的一個(gè)副本。順便說(shuō)明一下,.tar.gz 文件格式是非常常見(jiàn)的(稱(chēng)為 tarball),并且您可以使用 tar 命令直接解壓縮 tarball 文件:
$ tar xzvf sqlite-3.3.17.tar.gz
這一個(gè)命令和前面的兩個(gè)命令是等價(jià)的。
接下來(lái),將目錄更改為 sqlite-3.3.17,并使用 ls,以列出其中的內(nèi)容。您應(yīng)該看到與清單 1 所示類(lèi)似的清單:
清單 1. SQLite 包的清單
$ lsMakefile.in contrib publish.shMakefile.Linux-gcc doc spec.templateREADME ext sqlite.pc.inVERSION install-sh sqlite3.1aclocal.m4 ltmain.sh sqlite3.pc.inaddopcodes.awk main.mk srcart mkdll.shtclinstaller.tclconfig.guessmkopcodec.awk testconfig.sub mkopcodeh.awk toolconfigure mkso.sh wwwconfigure.acnotes
其中的源代碼和 SQLite 補(bǔ)充文件經(jīng)過(guò)了很好組織,并且模擬了大部分的軟件項(xiàng)目分發(fā)源代碼的方式:
README 文件對(duì)該項(xiàng)目進(jìn)行了描述,并且通常用于說(shuō)明如何構(gòu)建該軟件。(README 文件還詳細(xì)地介紹了使用條款,或者許可證、適用情況。許多項(xiàng)目的許可證代碼都符合 GNU 公共許可版本 2 中的條款,即所謂的“copyleft許可證。在許可證與您打算如何使用該軟件之間可能存在一定的沖突,如果您對(duì)此有任何疑問(wèn),最好請(qǐng)教一下合適的法律顧問(wèn)。)
src 目錄中包含了相關(guān)的代碼。
test 目錄中包含了一組測(cè)試,以驗(yàn)證該軟件的操作是否正確。在開(kāi)始構(gòu)建或者進(jìn)行了任何修改之后,請(qǐng)運(yùn)行這些測(cè)試,這樣可以增加對(duì)該軟件的信心。
contrib 目錄中包含核心 SQLite 開(kāi)發(fā)團(tuán)隊(duì)所沒(méi)有提供的附加軟件。對(duì)于像 SQLite 這樣的庫(kù),contrib 中可能包含一些常用語(yǔ)言(如 C、Perl、PHP 和 Python)的編程接口。它可能還包括圖形用戶(hù)界面(GUI)包裝,以及更多的內(nèi)容。
在其他文件中,Makefile.in、configure、configure.ac 和 aclocal.m4 用于生成在您的 Unix 版本中編譯 SQLite 軟件的腳本和規(guī)則。如果這個(gè)軟件足夠簡(jiǎn)單,那么要編譯其代碼,可能只需要一條簡(jiǎn)單的編譯命令即可。但是,因?yàn)榇嬖谌绱酥嗟?UNIX 變種(Mac OS X、Solaris、Linux、IBM® AIX® 和 HP/UX 等等),所以必須對(duì)宿主計(jì)算機(jī)進(jìn)行分析,以確定它的功能及其實(shí)現(xiàn)。例如,郵件閱讀應(yīng)用程序可能會(huì)嘗試確定本地系統(tǒng)是如何存儲(chǔ)郵箱的,并包含對(duì)該格式的支持。
集中精力感受源代碼
下一個(gè)步驟是探查系統(tǒng)并配置該軟件,以便進(jìn)行正確地構(gòu)建。(您可以將這個(gè)步驟想象為裁剪一件衣服:這件衣服大小基本合適,但是需要進(jìn)行一定的修改以使其更加時(shí)尚。)您需要進(jìn)行自定義工作,并為使用 ./configure 本地腳本進(jìn)行編譯做好準(zhǔn)備。在命令行提示處,鍵入:$ ./configure
這個(gè)配置腳本進(jìn)行了幾項(xiàng)測(cè)試,以證明您的系統(tǒng)是合格的。例如,在一臺(tái) Apple MacBook 計(jì)算機(jī)上(其中運(yùn)行著 FreeBSD® UNIX 的一個(gè)變種)運(yùn)行 ./configure,將產(chǎn)生以下結(jié)果(請(qǐng)參見(jiàn)清單 2):
清單 2. 在 Mac OS X 上運(yùn)行 ./configure 的結(jié)果
checking build system type... i386-apple-darwin8.9.1checking host system type... i386-apple-darwin8.9.1checking for gcc... gccchecking for C compiler default output file name... a.outchecking whether the C compiler works... yeschecking whether we are cross compiling... nochecking for suffix of executables...checking for suffix of object files... ochecking whether we are using the GNU C compiler... yeschecking whether gcc accepts -g... yeschecking for gcc option to accept ISO C89... none neededchecking for a sed that does not truncate output... /usr/bin/sedchecking for grep that handles long lines and -e... /usr/bin/grepchecking for egrep... /usr/bin/grep -Echecking for ld used by gcc... /usr/bin/ld...
這里,./configure 可以確定編譯和主機(jī)系統(tǒng)的類(lèi)型(如果您采用交叉編譯的話(huà),它可能不同),證實(shí)是否已經(jīng)安裝了 GNU C 編譯器(GCC),并找到其余構(gòu)建過(guò)程可能需要使用的實(shí)用工具的路徑。您可以瀏覽一下其余的輸出,但是您將看到一個(gè)較長(zhǎng)的診斷信息列表,該列表從成功構(gòu)造 SQLite 的角度分析了您的系統(tǒng)的特點(diǎn)。
注意: ./configure 命令可能 會(huì)失敗,特別是在無(wú)法找到一個(gè)先決條件的情況下(比如一個(gè)系統(tǒng)庫(kù)或者關(guān)鍵的系統(tǒng)實(shí)用工具)。
瀏覽 ./configure 的輸出,尋找其中不正常之處,如命令的專(zhuān)用或者本地版本,它可能并不適合于構(gòu)建通用的應(yīng)用程序,如 SQLite。作為一個(gè)示例,如果您的系統(tǒng)管理員安裝了 GCC 的 alpha 版本,并且 configure 工具首選使用該版本,那么您可以選擇手動(dòng)地改寫(xiě)這個(gè)選擇。要查看您可以改寫(xiě)的選項(xiàng)的列表(該列表通常很長(zhǎng)),可以鍵入 ./configure --help,如清單 3 中所示:
清單 3. 用于 ./configure 腳本的通用選項(xiàng)
$ ./configure --help...By default, `make install' will install all the files in`/usr/local/bin', `/usr/local/lib' etc. You can specifyan installation prefix other than `/usr/local' using `--prefix',for instance `--prefix=$HOME'.For better control, use the options below.Fine tuning of the installation DirectorIEs: --bindir=DIR user executables [EPREFIX/bin] --sbindir=DIR system admin executables [EPREFIX/sbin] --libexecdir=DIR program executables [EPREFIX/libexec]...
./configure --help 的輸出中包括配置系統(tǒng)使用的通用選項(xiàng),以及僅與您正在構(gòu)建的軟件相關(guān)的特定選項(xiàng)。要查看后者(較短)的列表,可以鍵入 ./configure --help=short(請(qǐng)參見(jiàn)清單 4):
清單 4. 要構(gòu)建的軟件包所特定的選項(xiàng)
$ ./configure --help=shortOptional Features: --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no) --enable-FEATURE[=ARG] include FEATURE [ARG=yes] --enable-shared[=PKGS] build shared librarIEs [default=yes] --enable-static[=PKGS] build static libraries [default=yes] --enable-fast-install[=PKGS] optimize for fast installation [default=yes] --disable-libtool-lock avoid locking (might break parallel builds) --enable-threadsafe Support threadsafe operation --enable-cross-thread-connections Allow connection sharing across threads --enable-threads-override-locks Threads can override each others locks --enable-releasemodeSupport libtool link to release mode --enable-tempstore Use an in-ram database for temporary tables (never,no,yes,always) --disable-tcl do not build TCL extension --disable-readline disable readline support [default=detect] --enable-debug enable debugging & verbose explain
返回到 ./configure --help,最頂部的輸出顯示了可執(zhí)行文件的缺省安裝目錄是 /usr/local/bin,庫(kù)文件的缺省安裝目錄是 /usr/local/lib,等等。許多系統(tǒng)使用一個(gè)替代的層次結(jié)構(gòu)來(lái)存儲(chǔ)非核心軟件。
例如,許多系統(tǒng)管理員選擇使用 /opt 而不是 /usr/local 存儲(chǔ)本地添加的或者在本地進(jìn)行了修改的軟件。如果您希望將 SQLite 安裝到與缺省目錄不同的其他目錄中,可以使用 --prefix= 選項(xiàng)指定該目錄。一種可行的方法(也是一種常見(jiàn)的方法,如果只有您一個(gè)人使用這個(gè)軟件包,或者如果您沒(méi)有 root 訪問(wèn)權(quán)限以便在全局的范圍內(nèi)安裝該軟件)是將該軟件安裝到您的 home 目錄中的層次結(jié)構(gòu)中:$ ./configure --prefix=$HOME/sw
使用這個(gè)命令,構(gòu)建過(guò)程的安裝部分將在 $HOME/sw 中(比如 $HOME/sw/bin、$HOME/sw/lib、$HOME/sw/etc、$HOME/sw/man,以及其他所需的目錄中)重新創(chuàng)建該軟件的層次結(jié)構(gòu)。為了簡(jiǎn)單起見(jiàn),這個(gè)示例在缺省目標(biāo)處安裝其代碼。
編譯代碼
./configure 的結(jié)果是一個(gè)與您的 Unix 版本兼容的 Makefile。名為 make 的開(kāi)發(fā)實(shí)用工具將使用這個(gè) Makefile,以執(zhí)行編譯所需的步驟,并將代碼鏈接到一個(gè)可執(zhí)行文件。您可以打開(kāi)這個(gè) Makefile 對(duì)其進(jìn)行檢查,但不要對(duì)它進(jìn)行編輯,因?yàn)槿绻俅芜\(yùn)行 ./configure,它將列出您所做的任何修改。
這個(gè) Makefile 中包含需要編譯的源文件的列表,并且它還包括啟用或者禁用并選擇 SQLite 包中的某些代碼片段的常數(shù)。例如,如果 configure 工具檢測(cè)到了系統(tǒng)中合適的芯片,那么它可能會(huì)啟用 64位處理器特定的代碼。這個(gè) Makefile 還說(shuō)明了源文件之間的依賴(lài)關(guān)系,因此在一個(gè)非常重要的頭文件 (.h) 中進(jìn)行的一項(xiàng)更改,可能會(huì)導(dǎo)致重新編譯所有的 C 源代碼。
您的下一個(gè)步驟是運(yùn)行 make,以構(gòu)建該軟件(請(qǐng)參見(jiàn)清單 5):
清單 5. 運(yùn)行 make
$ makesed -e s/--VERS--/3.3.17/ ./src/sqlite.h.in | sed -e s/--VERSION-NUMBER--/3003017/ >sqlite3.hgcc -g -O2 -o lemon ./tool/lemon.ccp ./tool/lempar.c .cp ./src/parse.y ../lemon parse.ymv parse.h parse.h.tempawk -f ./addopcodes.awk parse.h.temp >parse.hcat parse.h ./src/vdbe.c | awk -f ./mkopcodeh.awk >opcodes.h./libtool --mode=compile --tag=CC gcc -g -O2 -I. -I./src -DNDEBUG -I/System/Lib rary/Frameworks/Tcl.framework/Versions/8.4/Headers -DTHREADSAFE=0 -DSQLITE_THREA D_OVERRIDE_LOCK=-1 -DSQLITE_OMIT_LOAD_EXTENSION=1 -c ./src/alter.cmkdir .libsgcc -g -O2 -I. -I./src -DNDEBUG -I/System/Library/Frameworks/Tcl.framework/Vers ions/8.4/Headers -DTHREADSAFE=0 -DSQLITE_THREAD_OVERRIDE_LOCK=-1 -DSQLITE_OMIT_L OAD_EXTENSION=1 -c ./src/alter.c -fno-common -DPIC -o .libs/alter.o...ranlib .libs/libtclsqlite3.acreating libtclsqlite3.la
注意: 在上面的輸出中,添加了一些空白行,以便更好地突出顯示 make 發(fā)起的每個(gè)步驟。
make 實(shí)用工具檢查文件(頭文件、源代碼、數(shù)據(jù)文件和目標(biāo)文件)的修改日期,并編譯合適的 C 源文件。最初,make 將重新編譯所有內(nèi)容,因?yàn)椴淮嬖谌魏文繕?biāo)文件或者編譯目標(biāo)。正如您可以看到的,用于編譯目標(biāo)的規(guī)則還包括一些中間步驟,其中使用了一些相關(guān)的工具,如 sed 和 awk,以產(chǎn)生在后續(xù)的步驟中將要使用的頭文件。
執(zhí)行 make 命令所得到的結(jié)果是一個(gè)完成的庫(kù)和 sqlite3 實(shí)用工具。
最好對(duì)您剛編譯的軟件進(jìn)行測(cè)試,盡管這在每個(gè)包中并不是強(qiáng)制的,也沒(méi)有提供相應(yīng)的內(nèi)容。即便成功地構(gòu)建 了您的軟件,也不一定就表示該軟件能夠正確地運(yùn)行。
要測(cè)試您的軟件,可以再次運(yùn)行 make,并使用 test 選項(xiàng)(請(qǐng)參見(jiàn)清單 6):
清單 6. 對(duì)軟件進(jìn)行測(cè)試
$ make test...alter-1.1... Okalter-1.2... Okalter-1.3... Okalter-1.3.1... Okalter-1.4... Ok...Thread-specific data deallocated properly0 errors out of 28093 testsFailures on these tests:
成功了!該軟件構(gòu)建成功,并且工作正常。如果其中一個(gè)或者多個(gè)測(cè)試用例失敗了,那么底部的總結(jié)(這里,它是空白的)將向您報(bào)告哪一項(xiàng)測(cè)試或者哪幾項(xiàng)測(cè)試需要進(jìn)一步研究。
完成后的產(chǎn)品
如果您的軟件工作正常,那么最后一個(gè)步驟是將它安裝到您的系統(tǒng)中。同樣,使用 make,并指定 install 目標(biāo)。要將軟件添加到 /usr/local,通常需要由 sudo 所提供的超級(jí)用戶(hù)(root)權(quán)限(請(qǐng)參見(jiàn)清單 7):
清單 7. 在您的本地系統(tǒng)中安裝軟件
$ sudo make installtclsh ./tclinstaller.tcl 3.3/usr/bin/install -c -d /usr/local/lib./libtool --mode=install /usr/bin/install-c libsqlite3.la /usr/local/lib /usr/bin/install-c .libs/libsqlite3.0.8.6.dylib /usr/local/lib/libsqlite3.0.8.6 .dylib.../usr/bin/install -c .libs/libsqlite3.lai /usr/local/lib/libsqlite3.la/usr/bin/install -c .libs/libsqlite3.a /usr/local/lib/libsqlite3.achmod 644 /usr/local/lib/libsqlite3.aranlib /usr/local/lib/libsqlite3.a.../usr/bin/install -c -d /usr/local/bin./libtool --mode=install /usr/bin/install -c sqlite3 /usr/local/bin/usr/bin/install -c .libs/sqlite3 /usr/local/bin/sqlite3/usr/bin/install -c -d /usr/local/include/usr/bin/install -c -m 0644 sqlite3.h /usr/local/include/usr/bin/install -c -m 0644 ./src/sqlite3ext.h /usr/local/include/usr/bin/install -c -d /usr/local/lib/pkgconfig;/usr/bin/install -c -m 0644 sqlite3.pc /usr/local/lib/pkgconfig;
相關(guān)文章:
1. 對(duì)話(huà) UNIX,第 5 部分: 操縱數(shù)據(jù)與文件2. 對(duì)話(huà) UNIX: 關(guān)于 inode3. 對(duì)話(huà) UNIX,第 7 部分: 命令行慣用語(yǔ)4. 對(duì)話(huà) UNIX,第 8 部分: UNIX 進(jìn)程5. 對(duì)話(huà) UNIX,第 13 部分: 另外十種命令行組合6. 對(duì)話(huà) UNIX: 第 10 部分,定制您的 Shell7. 對(duì)話(huà) UNIX,第 3 部分: 在命令行中完成所有的工作8. 對(duì)話(huà) UNIX: 更多 shell 腳本技術(shù)9. 對(duì)話(huà) UNIX,第 4 部分: UNIX 所有權(quán)和權(quán)限管理10. 對(duì)話(huà) UNIX: Squirrel--可移植的 shell 和腳本語(yǔ)言
