引 言
嵌入文檔(Here Documents)技術是Unix/Linux平台中腳本語言BASH提供的一個特徵,是用於在當前腳本內部處理重定向的一種手段。由於Unix從設計之初就是遵循「使用小而簡單的工具進行無縫的集成」的理念,完成一項工作是需要大量的文本處理工具、流處理工具進行合作,通過標準輸入、輸出進行連接,將一個工具的處理結果轉向另一個工具進行加工,直到得到最終的處理結果。因此,I/O重定向(redirection)是SHELL腳本的一個重要語言特徵。但是,重定向技術也使整個處理過程顯得凌亂、瑣碎,對於初學者往往把握不好重定向的使用時機,甚至濫用重定向,而使得整個腳本里存在大量的中間文件,極大的損害了腳本語言的「優雅」程度。這是SHELL腳本學習過程中的一個常見問題。
為解決這個問題,SHELL腳本在設計上儘可能的減少中間文件,增加了「Here Documents」這一特徵。Here Documents提供了在腳本內部保存原始文件,以及在腳本內部提供重定向的機制,是精簡腳本的有效方式,也是反映腳本編程水平的重要特徵。在各種資料中「Here Documents」有不同的翻譯名稱,例如「嵌入文檔」、「內部文檔」、「現場文檔」等等。為了兼顧字面上的相近和使用上的形象,本文使用「嵌入文檔」這一名稱,並通過若干實例展示這一功能的使用方法。
一、問題的提出
事情是這樣的,在8月初,筆者負責某信息學競賽考試伺服器的準備和維護工作(http://gait.buaa.edu.cn:8765),在考試結束需要將伺服器上的考試成績數據導出來,發送給競賽主辦方。伺服器上安裝的競賽考試系統是在Linux下開發的專用系統,其中保存的成績數據都是採用自定義格式的文本文件,並且由於系統開發並不是非常完善,沒有提供很好的導出成績的功能。而主辦方當然希望看到一個直觀的Excel文件的總成績表。因此,需要緊急的手工處理一下,將伺服器上的文本文件轉換成Excel表出來。在這種情況下,當然首選的是SHELL腳本文件。
在考試系統裡面,成績的統計結果已經可以方便的在一個統計工具的WEB頁面里查看。這個統計工具當初設計的還是非常實用的,可以根據考試人員的需求,在頁面上查詢考試人員的成績、分數,等等。但是這個工具當時實現時存在一些問題,它只是列出了成績信息,而沒有把考試人員的一些基本信息列出來,包括考試人員的聯繫方式等註冊屬性。但是這個信息還是要給主辦方的,而且要求都顯示在同一個Excel文件中。改程序已經來不及了,因此這次只能手工處理配置文件了。
根據歷次的經驗,對於這種批量文本的轉換處理,使用SHELL腳本的效率還是非常高的。因此立即著手進行。
二、需求和目標
首先要解決的是弄清考試系統里保存的數據文件都是什麼格式。通過與系統的開發人員相切磋,從系統中找出了與本次處理相關的文件格式。
2.1 考生基本信息文件
在當前目錄下有所有考生的基本註冊信息文件,每個考生保存為一個文件。在系統中,考生有唯一標識是一個ID,是一個32位整數,使用這個整數保存成該ID的"%8X.REG"形式的文件名(當時使用這種格式主要是為了顯示的直觀和規整)。
例如,我本人的ID是4,那麼我的存儲文件名為「00000004.REG」。
信息文件的內容如下所示:
--------------------------------------------------------
考生基本信息文件(00000004.REG)
--------------------------------------------------------
1 loginid:jgj
2 pwd:kkaACXiDLyk=
3 repwd:kkaACXiDLyk=
4 name:靳國傑
5 sex:男
6 id:130229197911092212
7 birthday:19791109
8 school:北航
9 class:BY0306
10 phone:01082317614
11 mobile:13691250730
12 address:北航逸夫館
13 code:100083
14 mail:jin@cse.buaa.edu.cn
--------------------------------------------------------
關於文件格式的說明:
1. 上面這個文件中共包括14行,每一行形如「註冊屬性名稱:屬性值」,即冒號左邊是欄位名稱,冒號右邊是值。該文件一共14行,存儲了每個人員的14項註冊信息。這些信息都是要給主辦方的。
2. 通過名稱可以直觀的看出,註冊屬性包括登錄名、密碼、確認密碼(與密碼相同)、姓名、性別、身份證號、出生日期、學校、班級、電話、手機、地址、郵政編碼、郵箱。
在本次考試中一共註冊了650人,因此,註冊文件共有650個,文件名是從00000001.REG到0000028A.REG。
2.2 匯總文件
為了給競賽主辦方的人員一個清晰的材料,需要將這650個文件匯總,形成一份總表TOTAL.CSV,每人一行,15個欄位各佔一列。為方便起見,同一行中各欄位以「~」分隔,從而能夠方便的導入到Excel中,形成給主辦方的最終結果。匯總文件TOTAL.CSV如下表所示:
--------------------------------------------------------
匯總文件(TOTAL.CSV)
--------------------------------------------------------
ID~登錄名~密碼~確認密碼~姓名~性別~……
00000001~bootluck~QkxldMs0JllDRj5pIczVIw==~QkxldMs0JllDRj5pIczVIw==~Sun~男~……
00000002~xinghe~RA9S0UZEIHVDRj5pIczVIw==~RA9S0UZEIHVDRj5pIczVIw==~xinghe~男~……
00000003~alice607~9sCyToqCsQd5ouKr/39oaw==~9sCyToqCsQd5ouKr/39oaw==~alice607~女~……
00000004~jgj~kkaACXiDLyk=~kkaACXiDLyk=~靳國傑~男~……
……
……
……
……
……
……
0000028A~holybear~a1aACXiDLyk=~a1aACXiDLyk=~李聖雄~男~……
--------------------------------------------------------
關於文件格式的說明:
1. 上面這個文件中共包括651行,第一行是所有註冊欄位(以「~」分隔)。之後的每一行對應一個考生的所有註冊信息。
2. 通過名稱可以直觀的看出,註冊屬性包括登錄名、密碼、確認密碼(與密碼相同)、姓名、性別、身份證號、出生日期、學校、班級、電話、手機、地址、郵政編碼、郵箱。
3.3 匯總要求
因此,在這個工作中,需要對現有的數據文件進行如下的匯總過程,以形如最終提交的匯總文件。演演算法如下:
① 對每個考生信息文件,抽取冒號右邊的信息,得到所有註冊欄位名;抽取冒號右邊的信息,得到所有屬性值;
② 對抽出的14個欄位,以適當的分隔符分隔(比如「~」),拼成一行,總為匯總文件的首行;
③ 對每一個考生的屬性值,以「~」分隔,形成一條考生信息記錄,作為匯總文件的一行;
④ 通過編寫的Shell腳本,對所有650個考生信息文件執行上述過程,最終形成一個有651行的CSV(逗號分隔欄位)文件。
形成的CSV文件導入到Excel中就是要發給主辦方的文件。由於CSV導入Excel的操作比較簡單,這裡就不再贅述,下面重點講解Shell腳本的編寫過程。
三、技術基礎
我們採用Red Hat Linux和Unix下面廣泛使用的BASH來編寫腳本。首先講解有關Here Documents的語法基礎和基本使用方法。
3.1 語法結構
在命令行中執行man bash,可以找到BASH手冊中有關Here Documents的語法定義,其一般形式如下:
Here Documents
This type of redirection instructs the shell to read input from the
current source until a line containing only word (with no trailing
blanks) is seen. All of the lines read up to that point are then used
as the standard input for a command.
The format of here-documents is:
<<[-]word
here-document
delimiter
No parameter expansion, command substitution, arithmetic expansion, or
pathname expansion is performed on word. If any characters in word are
quoted, the delimiter is the result of quote removal on word, and the
lines in the here-document are not expanded. If word is unquoted, all
lines of the here-document are subjected to parameter expansion, com-
mand substitution, and arithmetic expansion. In the latter case, the
character sequence \
[火星人 ] Unix/Linux Shell編程實戰:使用嵌入文檔Here Documents已經有547次圍觀