歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux綜合 >> Linux資訊 >> 更多Linux

使用自由軟件Rexx 的實現來編寫腳本

    很容易在“小語言”的世界中迷失方向 —— 它們中相當多是出於一個公司、個人或者一個項目的特殊需要而編寫出來的。Rexx 就是這些語言中的一個,已經在 IBM 的操作系統上使用很長時間了,而且令人滿意的最新實現可以用於 Linux 和其他免費軟件操作系統。在相對粗糙的 shell 腳本和笨重的拘泥形式的完全系統語言的夾縫之中,Rexx 有其用武之地。Rexx 值得很多 Linux 程序員和系統管理員將其添加到收集的關鍵工具中。

關於 Rexx

    Rexx 編程語言最初創建於 1979 年,是一個層次非常高的腳本語言,有特別強大的用於文本處理任務的工具。從 Rexx 誕生起,IBM 就將各種版本的 Rexx 加入到它的操作系統中 —— 從主機到中型機再到終端用戶操作系統,比如 OS/2 和 PC-DOS。其他 OS 制造商,比如 Amiga,也將 Rexx 作為永遠可用的系統腳本語言集成進來。此外,許多獨立軟件開發商為很多平台創建了 Rexx 環境。再晚些時候,ANSI 於 1996 年正式為 Rexx 采納了一個標准。

    當前(尤其是在 Linux 或者起源於 BSD 的 OS 上),Rexx 的那些大部分早期實現,主要作為歷史足跡為人們所關心。不過,兩個當前保持的 Rexx 實現可以用於包括 Linux、MacOSX 和 Windows 在內的許多平台:Regina 和NetRexx。Regina 是本地執行文件,以免費軟件源代碼方式,或者為諸多平台預編譯好的方式獲得 —— 您可以像安裝任何其他語言解釋器一樣安裝它。NetRexx 是一個有趣的“混合物”。此語言派生自普通的 Rexx。更類似於 Jython 或者 Jacl,NetRexx 將類似於 Rexx 源代碼編譯為 Java 字節代碼,並(可選)在 JVM 中運行作為結果生成的 .class 文件。

    NetRexx 是一個 IBM 項目,用於為 Java 虛擬機編譯類似於 Rexx 的代碼。在實際能力和編程級別上,Rexx 最接近於 bash 加上 GNU 文件工具(外加 grep 和 sed );或者可能相當於 awk 或 Perl。當然,相對於 Python、Ruby 或者 Java 來說,Rexx 讓人感覺更為快而粗糙。Rexx 的冗長 —— 或者更應該說是,簡潔 —— 類似於 Perl、Python、Ruby 或者 TCL。並且,Rexx 當然是完全 Turing 的,支持模塊和結構化編程,有面向專門任務的庫,例如 GUI 接口、網絡編程和數據庫訪問。但是它最自然的目標在於系統腳本的自動化和本文處理任務。與 shell 腳本一起,Rexx 使得應用程序的控制非常自然且顯而易見;但相對於 bash (或者 tcsh 、 ksh 等等),Rexx 中包含了更豐富的內置控制結構和(文本處理)函數。

    在文體上,Rexx 的 IBM/mainframe 根源體現在它對命令的大小寫敏感;在某種次要程度上也體現在它使用標點符號相對較少 (更多用關鍵字而不是符號)。我傾向於認為這些特性的目的是提高可讀性;但這在很大程度上取決於個人的愛好。

從流和棧開始

    作為一個簡單的小例子,讓我來介紹一個特別簡單的工具的多個版本,它可以列出文件並對其進行編號。Rexx 和 shell 腳本的一個共同點是,它用於和底層操作系統打交道的函數相對很少 —— 幾乎僅限於可以打開、讀和修改文件。對大部分其他事情,您需要依賴於外部實用程序來完成手頭上的工作。實用程序 numbered-1.rexx 只是處理 STDIN:

清單 1. numbered-1.rexx

#!/usr/bin/rexx DO i=1 UNTIL lines()==0 PARSE LINEIN line IF line\="" THEN SAY i ") " line END

    無處不在的指令 PARSE 可以從各種不同的源讀入。在這裡,它將 STDIN 的下一行賦給變量 line 。我們也會檢查某一行是否為空,如果是空的話,則不進行顯示和編號。例如,與 ls 組合使用我們可以得到:

清單 2. 將命令通過管道傳輸到 numbered-1

$ ls ./numbered-1.rexx 1) ls-1.rexx 2) ls-2.rexx 3) ls-3.rexx 4) ls-4.rexx 5) ls-5.rexx 6) ls-6.rexx 7) numbered-1.rexx 8) numbered-2.rexx

同樣您可以方便地將任何其他命令通過管道傳輸進來。

    Rexx 的核心概念之一是巧妙地處理多個棧或流。類似 bash 的風格,在 Rexx 中任何沒有被識別為內部指令或函數的內容,都被假定為一個外部實用程序。沒有特定的函數或語法用於調用外部命令。Regina 的實用程序 rxqueue 可以將輸出壓入 Rexx 棧,利用它我們可以寫一個這樣的“有編號的 ls”實用程序:

清單 3. ls-1.rexx

#!/usr/bin/rexx "ls rxqueue" DO i=1 WHILE queued() \= 0 PARSE PULL line SAY i ") " line END

    Rexx 中有一些指令可以顯式地指定要操作的棧;但是其他指令操作是在您用 ADDRESS 指令配置的 環境 中進行。STDIN、STDOUT、STDERR、文件和內存內部數據棧都以統一而優雅的方式處理。上面我們用的是外部 rxqueue 實用程序,不過在 Rexx 內部我們同樣可以重定向實用程序的輸出。例如:

清單 4. ls-2.rexx

#!/usr/bin/rexx ADDRESS SYSTEM ls WITH OUTPUT FIFO '' ERROR NORMAL DO i=1 WHILE queued() \= 0 PARSE PULL line; SAY i ") " line; END

更多的請看:http://www.QQread.com/windows/2003/index.Html

   可能看起來 ADDRESS 命令只是獲得 ls 實用程序的輸出;但實際上它改變了後面外部調用的整體執行環境。這些例子運行於固定大小寫/區分大小寫 的文件系統上;在很多系統下,您將不得不保持“ls”的大小寫去引用它。這個執行相同:

清單 5. ls-5.rexx

#!/usr/bin/rexx ADDRESS SYSTEM WITH OUTPUT FIFO '' ERROR NORMAL ls DO i=1 WHILE queued()\=0; PARSE PULL ln; SAY i") "ln; END

    任意接下來的外部命令,如果他們是在默認的 SYSTEM 環境下運行,將把它們的輸入定向到默認的 FIFO(先入先出,first-in-first-out)。您也可以改為輸出到 LIFO(或者是命名的,或者是默認的)—— 不同之處是,FIFO 向棧的“底部”添加,而 LIFO 向“頂部”添加。指令 PUSH 和 QUEUE 對應的是對棧的 LIFO 和 FIFO 操作。指令 PULL 或者 PARSE PULL 從棧頂取得一個字符串。

    另一個值得說明的有用棧是 Rexx 腳本的命令行參數。例如,我們可能希望在我們的編號實用程序中執行任意的命令,而不是只執行 ls :

清單 6. numbered-1.rexx

#!/usr/bin/rexx PARSE ARG cmd ADDRESS SYSTEM cmd WITH OUTPUT FIFO '' DO i=1 WHILE queued()\=0; PARSE PULL ln; SAY i") "ln; END

腳本執行:

清單 7. 向 numbered-1 傳遞命令

$ ./numbered-2.rexx ps -a -x 1) PID TT STAT TIME COMMAND 2) 1 -- Ss 0:00.00 /sbin/init 3) 2 -- Ss 0:00.19 /sbin/mach_init 4) 51 -- Ss 0:01.95 kextd [...]

  PARSE PULL 可以用於從用戶輸入中得到行。按照參數 cmd 執行的例子,您可以在 Rexx 中寫一個 shell 或者交互式的環境(或許會運行外部的實用程序或者內置命令,正如同 bash )。

詞干變量和關聯數組

    在 Rexx 中 —— 有些類似於在 TCL 中 —— 在很大程度上 一切都是字符串。由多行組成的棧和流給您一個簡單的字符串列表或者數組。但是,通常, 根據需要字符串完全可以有像其他數據類型一樣行為。例如,包含有對一個數字(阿拉伯數字、十進制數、指數“e”,等等)適當描述的字符串可以用於數學運算。對於處理報告、日志文件等類似工作,這是您實際上期望的行為。

    不過,Rexx 確實有一個另外的標准數據類型:關聯數組。在 Rexx 中,它們被命名為“詞干變量”,但是其概念非常類似於很多其他語言中的程序庫。詞干變量的語法對 OOP 語言 (如 Java、Python 或者 Rudy) 的用戶來說也會驚人地熟悉:一個句點將“對象”和它們的“屬性”隔開。這不是真正的面向對象,但是其語法確實(偶然地)突出了對象與特別健壯的程序庫的相似程度。 其實有對 Rexx 的 OOP 擴展,但是本文將不會介紹它們。

    不是每一個字符串都是合法的 Rexx 符號 —— 為限制程序庫中的關鍵字 —— 但是相對於大部分語言,Rexx 在符號命名方面非常自由。舉例來說:

清單 8. 在 Rexx 中使用詞干變量

$ cat stems.rexx #!/usr/bin/rexx foo.X_!1.bar = 1 foo.X_!1.23 = 2 foo.fop.fip = 3 foo.fop = 4 SAY foo.X_!1.bar # foo.X_!1.23 # foo.fop.fip # foo.fop # foo.fop.NOPE $ ./stems.rexx 1 # 2 # 3 # 4 # FOO.FOP.NOPE

    在這個例子中突出說明了幾個特性。我們同時為詞干及其復合變量賦值(舉例來說, foo.fop 和 foo.fop.fip )。同時要注意,未定義的符號 foo.fop.nope 盡管沒有賦值,但這樣表示只是它本身的拼寫。這使我們在大部分情況下可以不用去引用。在大部分 Rexx 上下文中,名稱被規格化為大寫的。

    一個有用的技巧是為帶句點的詞干賦值,這個值將成為基於這個詞干的復合名字的默認值。在下一個例子中,我們也會利用這一功能,將一個復合名的順序編號的符號作為輸出環境變量來 ADDRESS 。

清單 9. ls-3.rexx

#!/usr/bin/rexx ls. = UNDEF ADDRESS SYSTEM ls WITH OUTPUT STEM ls. DO i=1 IF ls.i == UNDEF THEN LEAVE SAY i ") " ls.i END

更多的請看:http://www.qqread.com/windows/2003/index.html

   當循環進行到某個沒有被外部 ls 實用程序的輸出所替換的復合變量名時,我們就會檢測到“UNDEF”默認值並退出循環(不過,如果輸出中可能包含那個字符串,會發生錯誤沖突)。

    Rexx 還有一個錯誤處理系統,讓您 SIGNAL 情況並適當地處理它們。不用再檢查默認的復合值,您也可以捕獲對未定義變量的訪問。舉例來說:

清單 10. ls-6.rexx

#!/usr/bin/rexx ADDRESS SYSTEM ls WITH OUTPUT STEM ls. SIGNAL ON NOVALUE NAME quit DO i=1 SAY i ") " ls.i END quit:

為完成我們的 ls 變量,接下來用一個文件作為它的 I/O:

清單 11. ls-4.rexx

#!/usr/bin/rexx ADDRESS SYSTEM ls WITH OUTPUT STREAM files DO i=1 line = linein(files) IF line = "" THEN LEAVE SAY i ") " line END rm files

由於輸出流是一個規則的文件,因此最好在結束時將其刪除。

文本處理函數

    通過前面簡短的例子,讀者將會對 Rexx 這門編程語言有一些感覺。當然,您也可以定義您自己的過程和函數 —— 在單獨的模塊文件中,如果您希望的話 —— 然後以 CALL 指令或者以帶括號的參數方式來調用它們,正如本文中一些使用標准函數的例子。

    或許,作為一門文本處理語言,Rexx 的最強大之處在於它所具備的實用的內置字符串處理函數。可能有超過一半的標准 Rexx 函數用於處理字符串,其他的大部分被用來以極其顯而易見的方式處理位向量。此外,甚至位向量也經常作為由 1 和 0 構成的向量來處理(或者讀入):

清單 12. bits.rexx

#!/usr/bin/rexx SAY b2c('01100001') b2c('01100010') /* --> a b */ SAY bitor(b2c('01100001'), b2c('01100010')) /* --> c */ SAY bitor('a','b') /* --> c */ EXIT /* Function in ARexx, but not ANSI Rexx */ b2c: PROCEDURE ARG bits return x2c(b2x(bits))

    Rexx 的文本處理函數的一個令人喜愛的特性是,它可以自然地處理以空格隔開的 詞 組成的行。對文本的報表和日志文件來說,輕松忽略掉沒用的空格是非常有必要的 —— ‘awk’有類似的功能,但是 Python 的 string.split() 在描述同樣的操作時更“繁雜”。實際上,在 Rexx 中“數組”就是由空格隔開的字符串。 PULL 指令將以一個通用的模板模式從一行中得到變量,並支持最小程度的詞拆分:

清單 13. pushpull.rexx

#!/usr/bin/rexx PUSH "a b c d e f" PULL x y " C " z /* pull x and y before the C, remainder into z */ SAY x # y # z /* --> A # B # D E F */

   可以對那些可能是或者不是通過模板得到的字符串進行進一步完美的拆分。 Wordpos() 、 word() 、 wordindex() 或者 words() 、 subword() 這些函數讓您可以訪問字符串中的“詞”,就像是它們構成一個列表一樣,舉例來說:

清單 14. 處理字符串中的詞

seuss = "The cat in the hat came back" thehat = wordpos('the hat', seuss) SAY "'came'" is wordlength(seuss, thehat+2) letters long /* --> 'came' IS 4 LETTERS LONG */

    當然,您也可以得到足夠的面向字符的函數集。可以同樣容易地使用 reverse() 、 right() 、 justify() 、 center() 、 pos() 或者 substr() (還有其他的)函數來處理字符位置。

    使用另外一些內置的函數,您可以以一種靈活的、面向報表的方法處理日期和數字。更確切地說,可以以各種格式對任意(可配置)精度的數字進行讀和寫。類似的,使用標准函數調用可以對日期進行讀寫和各種格式間的轉換(例如,一周中的第幾天、一個世紀中的第幾天、歐洲時間相對於美國時間,等等)。處理日期和數字的靈活性,在編寫系統腳本和處理日志文件時,其必要性可能比不上處理來自數據庫應用的半結構化輸出報表。但是當您需要這個功能時,使用充分測試過的內置函數要比編寫您自己的特制的轉換器和格式化工具要穩妥得多。

結束語

    由於來自 IBM “大型機”環境比重多於 Unix 系統,Rexx 對很多 Linux 程序員和系統管理員是鮮為人知的。但是在 Linux 中仍然有一個重要的領域,在這裡,相對於“過輕量級”的 bash 或 ksh shell,或者“過重量級”的解釋性編程語言例如 Python、Perl、Ruby、TCL,或者可能還有 Scheme,Rexx 是更好的腳本解決方案。作為可以快速且簡單易讀的腳本,在對外部進程的輸入和輸出進行文本處理方面,Rexx 無懈可擊,而且學習和安裝也不困難。

原文鏈接:http://www-128.ibm.com/developerworks/cn/linux/l-rexx.html




Copyright © Linux教程網 All Rights Reserved