官术网_书友最值得收藏!

2.8 綜合應用

至此,你已學習完作為程序設計語言的shell的主要功能。是時候運用你所學的知識來編寫一個實際的示例程序了。

貫穿全書,你將編寫一個CD數據庫應用程序,從而更好地掌握所學的知識。首先從shell腳本開始,但很快你就會用C語言重寫該程序,并給它加上數據庫等新功能。

2.8.1 需求

假設你收集了大量的CD唱片,現在為了方便管理,你將設計和實現一個管理CD唱片的程序。在學習Linux程序設計的過程中,實現這樣一個電子CD唱片目錄看起來是一個比較理想的項目。

開始階段,你至少應該能夠做到把每張CD唱片的基本資料保存起來,如唱片的名稱、音樂類型、藝術家或作曲家的名字等。你可能還想再保存一些簡單的曲目信息。你希望能夠以每張CD唱片為單位進行搜索,而不是以曲目資料為單位。為了讓這個小小的應用程序比較完整,你還希望能夠在這個應用程序中對唱片資料進行輸入、更新和刪除。

2.8.2 設計

3項需求(對數據進行更新、檢索和顯示)應該采用一個簡單的菜單就足夠了。由于所有需要存儲的數據全部都是文本,而且假設你收集的CD唱片不是很多,因此你就沒有必要使用一個復雜的數據庫,使用一些簡單的文本文件即可。將資料保存在文本文件中將使應用程序比較簡單,而且如果你的需求發生了變化,操縱文本文件總是要比操縱其他類型的文件更加容易。在萬不得已的情況下,你甚至還可以使用一個編輯器來手工輸入和刪除數據,而不必非要通過編寫程序來完成。

你需要為數據存儲作出一個重要的設計決策:一個文件夠用嗎?如果夠用,它應該采用什么樣的格式?除曲目信息以外,你想要保存的大部分資料在每張CD唱片上只出現一次(我們暫不考慮某些CD唱片包含多位作曲家或藝術家作品的情況),而且幾乎所有CD唱片都有多個曲目。

你需要對可以存儲在CD唱片上的曲目數量加一個限制嗎?這看起來是一個非常隨意和沒有必要的限制,所以還是立刻放棄這個想法吧!

如果對曲目數量沒有限制,你就有以下3種選擇。

? 只使用一個文件,用一行來保存“標題”信息,再用n行保存該CD唱片上的曲目信息。

? 將每張CD唱片的所有信息都放置在一行上,允許該行一直延續直到沒有曲目信息需要保存為止。

? 把標題信息和曲目信息分開,用不同的文件來分別保存它們。

只有第三種做法能夠讓你靈活地修改文件的格式,如果今后你想把數據庫轉換為關系數據庫格式的話(將在第7章詳細介紹),你就需要修改文件格式,因此我們選擇第三種方法。

下一個決策是要在文件里放入哪些信息。

我們決定對每張CD唱片保存以下信息:

? CD唱片的目錄編號;

? 標題;

? 曲目類型(古典、搖滾、流行、爵士等);

? 作曲家或藝術家。

對曲目,我們只保存兩條信息:

? 曲目編號;

? 曲名。

為了把這兩個文件結合起來,你必須把曲目信息和CD唱片上的其他信息關聯起來。為此,你需要使用CD唱片的目錄編號。因為它對每張CD唱片都是唯一的,所以它在標題文件中只出現一次,在曲目文件中對每首曲目也只出現一次。

讓我們來看一個示例標題文件,如表2-24所示。

表2-24

它所對應的曲目文件,如表2-25所示。

表2-25

這兩個文件通過目錄編號結合在一起。請記住,標題文件中的一個數據項一般都對應曲目文件中的多行數據。

你需要決定的最后一件事情是如何分隔數據項。在關系數據庫里,長度固定的數據字段比較常見,但它并非總是最方便的。另一種常見方法是使用逗號,這個例子就選擇了這個方法(即用逗號分隔變量,或CSV文件)。

在接下來的“實驗”部分,為了不至于讓你迷失方向,我們把將要用到的函數列在下面:

實驗CD唱片應用程序

(1)和以前一樣,這個示例腳本程序的第一行用于確保自己可以作為一個shell腳本程序來執行,接下來是一些版權信息:

(2)首先要做的事情就是,確保設置好腳本程序將要用到的一些全局變量,包括標題文件、曲目文件和一個臨時文件。我們還設置Ctrl+C組合鍵的中斷處理,以確保在用戶中斷腳本程序時刪除臨時文件:

(3)現在開始定義函數。因為腳本程序是從文件的第一行開始執行,所以這樣做可以確保在調用任何一個函數之前都能夠找到它的定義。為了避免在幾個地方反復編寫同樣的代碼,最開始的兩個函數是簡單的工具型函數:

(4)接下來是主菜單函數set_menu_choice。菜單的內容是動態變化的,當用戶選擇了某張CD唱片后,主菜單中會多出幾個選項。

注意,echo-e命令可能不能被移植到某些shell中。

(5)接下來是兩個很短小的函數insert_title和insert_track,它們用于向數據庫文件里添加數據。雖然有的人不喜歡這種長度只有一行的函數,但它們有助于讓其他函數的含義更清晰易解。

緊跟著這兩個函數的是一個比較大的函數add_record_tracks,它會用到上述兩個短小的函數。這個函數使用模式匹配來確保用戶未輸入逗號(因為我們把逗號用做數據字段之間的分隔符),使用算術操作在用戶輸入曲目時遞增當前曲目的編號:

(6)add_records函數用于輸入新CD唱片的標題信息:

(7)find_cd函數的作用是使用grep命令在CD唱片標題文件中查找CD唱片的有關資料。你需要知道查詢字符串在標題文件里出現的次數,但grep命令的返回值只能告訴你該字符串是匹配了0次還是多次。為了解決這一問題,我們把grep命令的輸出保存到一個臨時文件中,文件中的每行對應一次匹配,然后再統計該文件的行數。

單詞統計命令wc在其輸出中使用空格符分隔被統計文件中的行數、單詞數和字符個數。我們使用$(wc -l $temp_file)標記從wc命令的輸出結果中提取出第一個參數,并賦值給變量linesfound。如果要用到wc命令輸出中的其他參數,你可以利用set命令把shell參數變量設置為wc命令的輸出結果。

我們把IFS(內部數據字段分隔符)設置為一個逗號,這樣你就可以讀取以逗號分隔的數據字段了。另一個可選擇的命令是cut。

(8)update_cd函數用于重新輸入CD唱片的資料。注意,你想要搜索(使用grep)的行是以$cdcatnum開頭(通過標志^)并且其后跟著一個逗號,因此你需要把$cdcatnum變量的擴展放在一對花括號{}里,這樣你就可以搜索緊跟在CD目錄編號之后的逗號了。這個函數還在get_confirm返回true的情況下,用花括號將要執行的多個語句組成一個語句塊。

(9)count_cds函數用于快速統計數據庫中CD唱片個數和曲目總數:

(10)remove_records函數用于從數據庫文件中刪除數據項,它通過grep -v命令刪除所有匹配的字符串。注意,你必須使用一個臨時文件來完成這一工作。

如果你使用下面這樣的命令:

$title_file文件就會在grep命令開始執行之前,被>輸出重定向操作設置為空文件,結果導致grep命令將從一個空文件里讀取數據。

(11)list_tracks函數還是使用grep命令來找出你想要的行,它通過cut命令來訪問你想要的字段,然后通過more命令提供按頁輸出。如果你對比一下用C語言重新實現這段大約20行左右的代碼需要多少條語句的話,你就不得不佩服shell是一個功能多么強大的工具了。

(12)現在所有的函數都已定義好了,你可以進入主程序部分了。開頭的幾行先確保需要的文件處于一個已知狀態,然后調用主菜單函數set_menu_choice,再根據它的輸出進行相應的操作。

如果用戶選擇了退出,程序就先刪除臨時文件,再顯示結束信息,最后成功退出(退出碼為0):

2.8.3 應用程序的說明

腳本程序開始處的trap命令用于設置在用戶按下Ctrl+C組合鍵時的中斷處理。根據終端設置的不同,Ctrl+C組合鍵可能引發EXIT或INT信號。

實現菜單選擇還有其他的辦法,特別值得一提的是bash或ksh提供的select結構(但它未被列在X/Open規范中)。它是一個專門用來處理菜單選擇的結構。如果你并不介意腳本程序移植性稍差的話,可以考慮使用它。你還可以利用here文檔來實現為用戶提供多行信息。

你可能已注意到,當添加一個新的CD唱片記錄時,程序并沒有檢查其主鍵。新代碼只是忽略使用同樣主鍵的后續唱片標題,但把它們的曲目添加到第一個使用該主鍵的CD唱片的曲目清單中。如下所示:

我們將把這個問題及其他改進留給讀者,請充分發揮你們的想象力和創造力,因為你可以在GPL條款之下任意修改這些代碼。

主站蜘蛛池模板: 石阡县| 图木舒克市| 宜昌市| 新竹市| 光泽县| 黄浦区| 瓦房店市| 南漳县| 淅川县| 建水县| 白朗县| 子洲县| 大姚县| 盐津县| 六盘水市| 左贡县| 公安县| 宝清县| 屏边| 英超| 曲松县| 闵行区| 宜良县| 柘荣县| 宣武区| 龙州县| 磐安县| 武胜县| 阜阳市| 南汇区| 庆城县| 苏尼特左旗| 衡阳市| 繁昌县| 茌平县| 台湾省| 华阴市| 高青县| 黑水县| 包头市| 满洲里市|