linux c read函数返回值,Linuxc - GNU Readline 库及编程简介
GNU Readline 庫(kù)及編程簡(jiǎn)介
簡(jiǎn)介
用過(guò) Bash 命令行的一定知道,Bash 有幾個(gè)特性:
TAB 鍵可以用來(lái)命令補(bǔ)全
↑ 或 ↓ 鍵可以用來(lái)快速輸入歷史命令
還有一些交互式行編輯快捷鍵:
C-A / C-E 將光標(biāo)移到行首/行尾
C-B / C-F 將光標(biāo)向左/向右移動(dòng)一個(gè)位置
C-D 刪除光標(biāo)下的一個(gè)字符
C-K 刪除光標(biāo)及光標(biāo)到行尾的所有字符
C-U 刪除光標(biāo)到行首的所有字符
...
同樣的操作在很多交互式程序都有類(lèi)似的操作,例如 ftp、gdb 等等,那么你是否想過(guò)這些是如何實(shí)現(xiàn)的呢?如果我們要做一個(gè)命令行下的交互式開(kāi)源軟件,是否希望也能有這些命令補(bǔ)全、搜索歷史命令、行編輯快捷鍵等等這些人性化的交互方式呢?
要想實(shí)現(xiàn)這些,你有兩種途徑:可以自己寫(xiě)程序?qū)崿F(xiàn),或者調(diào)用開(kāi)源的庫(kù) Readline Lib。例如上面介紹的 bash、ftp、gdb 等等軟件都使用了 GNU 的開(kāi)源跨平臺(tái)庫(kù),為其提供交互式的文本編輯功能。當(dāng)然需要注意的是,Readline Library 是 GNU 自由軟件,在 GNU GPL V3 協(xié)議下發(fā)布,因此如果你的程序中需要用到該庫(kù),也必須遵守相關(guān)協(xié)議。
本文首先簡(jiǎn)單介紹一下該庫(kù)的基本使用方法,后面會(huì)稍微詳細(xì)介紹下如何使用 Readline 來(lái)自定義命令補(bǔ)全功能。
Readline 基本操作
輸入讀取
很多命令行交互式程序交互方式都差不多,輸出提示符,等待用戶輸入命令,用戶輸入命令之后按回車(chē),程序開(kāi)始解析命令并執(zhí)行。那么這里面有個(gè)動(dòng)作是讀入用戶的輸入,以前我們也許使用 gets() 這樣的函數(shù)來(lái)實(shí)現(xiàn),當(dāng)我們使用 Readline 庫(kù)時(shí),可以使用 readline() 函數(shù)來(lái)替換它,該函數(shù)在 ANSI C 中定義如下:
char *readline (char *prompt);
該函數(shù)帶有一個(gè)參數(shù) prompt,表示命令提示符,例如 ftp 中就是 "ftp>",用戶在后面可以輸入命令,當(dāng)按下回車(chē)鍵時(shí),程序讀入該行(不包括最后的換行符)存入字符緩沖區(qū)中,readline 的返回值就是該行文本的指針。注意:當(dāng)該行文本不需要使用時(shí),需要釋放該指針指向的空間,防止內(nèi)存泄漏。當(dāng)讀入 EOF時(shí),如果還未讀入其它字符,則返回 (char *) NULL,否則讀入結(jié)束,與讀入換行效果相同。
命令補(bǔ)全
除了能讀入用戶的輸入,我們有時(shí)希望交互更簡(jiǎn)單些,例如命令補(bǔ)全。當(dāng)有很多命令時(shí),如果希望用戶都能準(zhǔn)確記憶命令的拼寫(xiě)是困難的,那么一般做法是按下 TAB 鍵進(jìn)行命令提示及補(bǔ)全,如 ftp 下輸入一個(gè)字符c 之后按下 TAB 鍵,會(huì)列出所有以 c 開(kāi)頭的命令:
ftp> c
case cd cdup chmod close cr
readline 函數(shù)其實(shí)已經(jīng)給用戶默認(rèn)的 TAB 補(bǔ)全的功能:根據(jù)當(dāng)前路徑下文件名來(lái)補(bǔ)全。
如果你不想 Readline 根據(jù)文件名補(bǔ)全,你可以通過(guò) rl_bind_key() 函數(shù)來(lái)改變 TAB 鍵的行為。該函數(shù)的原型為:
int rl_bind_key(int key, int (*function)());
該函數(shù)帶有兩個(gè)參數(shù):key 是你想綁定鍵的 ASCII 碼字符表示,function 是當(dāng) key 鍵按下時(shí)觸發(fā)調(diào)用函數(shù)的地址。如果想按下 TAB 鍵就輸入一個(gè)制表符本身,可以將 TAB 綁定到 rl_insert() 函數(shù),這是 Readline 庫(kù)提供的函數(shù)。如果 key 不是有效的 ASCII 碼值(0~255之間),rl_bind_key() 返回非 0。
這樣,禁止 TAB 的默認(rèn)行為,下面這樣做就可以了:
rl_bind_key('\t', rl_insert);
這個(gè)代碼需要在你程序一開(kāi)始就調(diào)用;你可以寫(xiě)一個(gè)函數(shù)叫 initialize_readline() 來(lái)執(zhí)行這個(gè)動(dòng)作和其它一些必要的初始化,例如安裝用戶自定義補(bǔ)全。
當(dāng)我們希望輸入 TAB 時(shí)不是列出當(dāng)前路徑下的所有文件,而是列出程序內(nèi)置的一些命令,例如上面舉到 ftp 的例子,這種行為稱為自定義補(bǔ)全。 該操作較復(fù)雜,我們留在后面一節(jié)主要介紹。
歷史記錄
基本操作還有一個(gè)——搜索歷史。我們希望輸入過(guò)的命令行,還可以通過(guò) C-p 或者 C-s 來(lái)搜索到,那么就需要將命令行加入到歷史列表中,可以調(diào)用 add_history() 函數(shù)來(lái)完成。但盡量將空行也加入到歷史列表中,因?yàn)榭招姓加脷v史列表的空間而且也毫無(wú)用處。綜上,我們可以寫(xiě)出一個(gè) Readline 版的 gets() 函數(shù) rl_gets():
/* A static variable for holding the line. */
static char *line_read = (char *)NULL;
/* Read a string, and return a pointer to it. Returns NULL on EOF. */
char *
rl_gets ()
{
/* If the buffer has already been allocated, return the memory
to the free pool. */
if (line_read)
{
free (line_read);
line_read = (char *)NULL;
}
/* Get a line from the user. */
line_read = readline ("");
/* If the line has any text in it, save it on the history. */
if (line_read && *line_read)
add_history (line_read);
return (line_read);
}
自定義補(bǔ)全
上面也提到了什么是自定義補(bǔ)全,無(wú)疑這在命令行交互式程序中是非常重要的,直接影響到用戶體驗(yàn)。Readline 庫(kù)提供了兩種比較常用的補(bǔ)全方式——按照文件名補(bǔ)全和按照用戶名補(bǔ)全,分別對(duì)應(yīng) Readline 中已經(jīng)實(shí)現(xiàn)的兩個(gè)函數(shù) rl_filename_completion_function 和 rl_username_completion_function。如果我們既不希望按照文件名和用戶名來(lái)補(bǔ)全,希望按照程序的命令補(bǔ)全,應(yīng)該怎么做呢?也很容易想到,只要實(shí)現(xiàn)自己的補(bǔ)全函數(shù)就好了。
Readline 補(bǔ)全的工作原理如下:
用戶接口函數(shù) rl_complete() 調(diào)用 rl_completion_matches() 來(lái)產(chǎn)生可能的補(bǔ)全列表;
內(nèi)部函數(shù) rl_completion_matches() 使用程序提供的 generator 函數(shù)來(lái)產(chǎn)生補(bǔ)全列表,并返回這些匹配的數(shù)組,在此之前需要將 generator 函數(shù)的地址放到 rl_completion_entry_function 變量中,例如上面提到的按文件名或用戶名補(bǔ)全函數(shù)就是不同的 generators;
generator 函數(shù)在 rl_completion_matches() 中不斷被調(diào)用,每次返回一個(gè)字符串。generator 函數(shù)帶有兩個(gè)參數(shù):text 是需要補(bǔ)全的單詞的部分,state 在函數(shù)第一次調(diào)用時(shí)為 0,接下來(lái)調(diào)用時(shí)非 0。generator 函數(shù)返回 (char *)NULL 通知 rl_completion_matches() 沒(méi)有剩下可能的匹配。
Readline 庫(kù)中有個(gè)變量 rl_attempted_completion_function,改變量類(lèi)型是一個(gè)函數(shù)指針rl_completion_func_t *,我們可以將該變量設(shè)置我們自定義的產(chǎn)生匹配的函數(shù),該按下 TAB 鍵時(shí)會(huì)調(diào)用該函數(shù),函數(shù)具有三個(gè)參數(shù):
text: 該參數(shù)是待補(bǔ)全的單詞的部分,例如在 Bash 提示符后輸入一個(gè) c 字符,按下 TAB,此時(shí) text指向的是 "c" 字符串的指針;在 Bash 提示符后輸入一個(gè) cd /home/gu 字符串,按下 TAB,此時(shí) text指向的是"/home/gu" 字符串的指針;
start: text 字符串在該行輸入中的起始位置,例如對(duì)于上面的例子,第一種情況下是 0,第二種情況下是 3;
end: text 字符串在該行輸入中的結(jié)束位置,例如對(duì)于上面的例子,第一種情況下是 1,第二種情況下是 11。
我們自定義的補(bǔ)全函數(shù)可以根據(jù)傳入的參數(shù)來(lái)設(shè)置我們希望按照什么方式補(bǔ)全,例如對(duì)于 Bash 下的 cd命令,我們希望開(kāi)始是命令補(bǔ)全,當(dāng)命令補(bǔ)全之后,后面接著跟的是文件名補(bǔ)全,這樣可以使用rl_completion_matches() 來(lái)綁定使用哪種 generator,rl_completion_matches() 函數(shù)的原型是:
char ** rl_completion_matches (const char *text, rl_compentry_func_t *entry_func)
帶有兩個(gè)參數(shù):text 就是上面介紹的傳入的待補(bǔ)全的單詞,第二個(gè)參數(shù) entry_func 是上面反復(fù)介紹的generator 函數(shù)的指針。該函數(shù)的返回值是 generator 產(chǎn)生的可能匹配 text 的字符串?dāng)?shù)組指針,該數(shù)組的最后一項(xiàng)是 NULL 指針。
好了,上面說(shuō)了這么多關(guān)于自定義補(bǔ)全的函數(shù)和變量,到底怎么用呢,估計(jì)還是比較模糊,那么看一個(gè)例子估計(jì)就很清楚了,這個(gè)例子是 Readline 官方提供的示例程序,由于比較長(zhǎng),就不在這里貼出來(lái)了,你可以在 http://cnswww.cns.cwru.edu/ph... 找到。
總結(jié)
其實(shí),雖然說(shuō)了很多,但還只是 Readline 庫(kù)的皮毛,這個(gè)庫(kù)的功能遠(yuǎn)遠(yuǎn)比這強(qiáng)大的多,如果想深入了解并且運(yùn)用,你必須要做三件事:
Read The Fucking Manual:閱讀官方的 文檔
Read The Fucking Source Code:閱讀官方提供的例子代碼,如果想了解更深入可以去看 Readline 的源碼
Show Your Code:自己動(dòng)手寫(xiě)幾個(gè)例子試試,如果有機(jī)會(huì)運(yùn)用到你的項(xiàng)目中。
總結(jié)
以上是生活随笔為你收集整理的linux c read函数返回值,Linuxc - GNU Readline 库及编程简介的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 超市买的年糕煮多久能熟 超市买的年糕要煮
- 下一篇: 什么是发粉 发粉是什么