<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    每日一得

    不求多得,只求一得 about java,hibernate,spring,design,database,Ror,ruby,快速開發
    最近關心的內容:SSH,seam,flex,敏捷,TDD
    本站的官方站點是:顛覆軟件

      BlogJava :: 首頁 :: 新隨筆 :: 聯系 :: 聚合  :: 管理 ::
      220 隨筆 :: 9 文章 :: 421 評論 :: 0 Trackbacks

    #

    key words: oracle優化 優化oracle? 表設計優化 powerdesigner


    前言

    絕 大多數的Oracle數據庫性能問題都是由于數據庫設計不合理造成的,只有少部分問題根植于Database Buffer、Share Pool、Redo Log Buffer等內存模塊配置不合理,I/O爭用,CPU爭用等DBA職責范圍上。所以除非是面對一個業已完成不可變更的系統,否則我們不應過多地將關注點 投向內存、I/O、CPU等性能調整項目上,而應關注數據庫表本身的設計是否合理,庫表設計的合理性才是程序性能的真正執牛耳者。

    合理的數據庫設計需要考慮以下的方面:

    ·業務數據以何種方式表達。如一個員工有多個Email,你可以在T_EMPLOYEE表中建立多個Email字段如email_1、email_2、 email_3,也可以創建一個T_EMAIL子表來存儲,甚至可以用逗號分隔開多個Email地址存放在一個字段中。

    ·數據以何種方式物理存儲。如大表的分區,表空間的合理設計等。

    ·如何建立合理的數據表索引。表索引幾乎是提高數據表查詢性能最有效的方法,Oracle擁有類型豐富的數據表索引類型,如何取舍選擇顯得特別重要。

    本 文我們將目光主要聚焦于數據表的索引上,同時也將提及其他兩點的內容。通過對一個簡單的庫表設計實例的分析引出設計中的不足,并逐一改正。考慮到手工編寫 庫表的SQL腳本原始且低效,我們將用目前最流行的庫表設計工具PowerDesigner 10來講述表設計的過程,所以在本文中你還會了解到一些相關的PowerDesigner的使用技巧。

    一個簡單的例子

    某個開發人員著手設計一個訂單的系統,這個系統中有兩個主要的業務表,分別是訂單基本信息表和訂單條目表,這兩張表具有主從關系的表,其中T_ORDER是訂單主表,而T_ORDER_ITEM是訂單條目表。數據庫設計人員的設計成果如圖 1所示:

    合理設計優化Oracle庫表設計的若干方法

    圖 1 訂單主從表


    ORDER_ID 是訂單號,為T_ORDER的主鍵,通過名為SEQ_ORDER_ID的序列產生鍵值,而ITEM_ID是T_ORDER_ITEM表的主鍵,通過名為 SEQ_ORDER_ITEM的序列產生鍵值,T_ORDER_ITEM通過ORDER_ID外鍵關聯到T_ORDER表。

    需求文檔指出訂單記錄將通過以下兩種方式來查詢數據:

    ·CLIENT + ORDER_DATE+IS_SHPPED:根據"客戶+訂貨日期+是否發貨"條件查詢訂單及訂單條目。

    ·ORDER_DATE+IS_SHIPPED:根據"訂貨日期+是否發貨"條件查詢訂單及訂單條目。

    數 據庫設計人員根據這個要求,在T_ORDER表的CLIENT、 ORDER_DATE及IS_SHPPED三字段上建立了一個復合索引IDX_ORDER_COMPOSITE;在T_ORDER_ITEM為外鍵 ORDER_ID建立IDX_ORDER_ITEM_ORDER_ID索引。

    讓我們看一下該份設計的最終SQL腳本:

    /*訂單表*/

    create table T_ORDER (

    ORDER_ID NUMBER(10) not null,

    ADDRESS VARCHAR2(100),

    CLIENT VARCHAR2(60),

    ORDER_DATE CHAR(8),

    IS_SHIPPED CHAR(1),

    constraint PK_T_ORDER primary key (ORDER_ID)

    );

    create index IDX_CLIENT on T_ORDER (

     CLIENT ASC,

     ORDER_DATE ASC,

     IS_SHIPPED ASC);

    /*訂單條目子表*/

    create table T_ORDER_ITEM (

     ITEM_ID NUMBER(10) not null,

     ORDER_ID NUMBER(10),

     ITEM VARCHAR2(20),

     COUNT NUMBER(10),

     constraint PK_T_ORDER_ITEM primary key (ITEM_ID)

    );

    create index IDX_ORDER_ITEM_ORDER_ID on T_ORDER_ITEM (

     ORDER_ID ASC);

     alter table T_ORDER_ITEM add constraint FK_T_ORDER__REFERENCE_T_ORDER foreign key (ORDER_ID) references T_ORDER (ORDER_ID);



    我們承認在ER關系上,這份設計并不存在的缺陷,但卻存在以下有待優化的地方:

    ·沒有將表數據和索引數據存儲到不同的表空間中,而不加區別地將它們存儲到同一表空間里。這樣,不但會造成I/O競爭,也為數據庫的維護工作帶來不便。

    ·ORACLE會自動為表的主鍵列創建一個普通B-Tree索引,但由于這兩張表的主鍵值都通過序列提供,具有嚴格的順序性(升序或降序),此時手工為其指定一個反鍵索引(reverse key index)將更加合理。

    · 在子表T_ORDER_ITEM外鍵列ORDER_ID上建立的IDX_ORDER_ITEM_ORDER_ID的普通B-Tree索引非常適合設置為壓 縮型索引,即建立一個壓縮型的B-Tree索引。因為一份訂單會對應多個訂單條目,這就意味著T_ORDER_ITEM表存在許多同值的 ORDER_ID列值,通過將其索引指定為壓縮型的B-Tree索引,不但可以減少IDX_ORDER_ITEM_ORDER_ID所需的存儲空間,還將 提高表操作的性能。

    ·企圖僅通過建立一個包含3字段IDX_ORDER_COMPOSITE復合索引滿足如前所述的兩種查詢條件方式的索引是有問題的,事實上使用ORDER_DATE+IS_SHIPPED復合條件的查詢將利用不到IDX_ORDER_COMPOSITE索引。

    ?優化設計

    1、將表數據和索引數據分開表空間存儲

    1.1 表數據和索引為何需要使用獨立的表空間

    Oracle 強烈建立,任何一個應用程序的庫表至少需要創建兩個表空間,其中之一用于存儲表數據,而另一個用于存儲表索引數據。因為如果將表數據和索引數據放在一起, 表數據的I/O操作和索引的I/O操作將產生影響系統性能的I/O競爭,降低系統的響應效率。將表數據和索引數據存放在不同的表空間中(如一個為 APP_DATA,另一個為APP_IDX),并在物理層面將這兩個表空間的數據文件放在不同的物理磁盤上,就可以避免這種競爭了。

    擁有獨立的表空間,就意味著可以獨立地為表數據和索引數據提供獨立的物理存儲參數,而不會發生相互影響,畢竟表數據和索引數據擁有不同的特性,而這些特性又直接影響了物理存儲參數的設定。

    此外,表數據和索引數據獨立存儲,還會帶來數據管理和維護上的方面。如你在遷移一個業務數據庫時,為了降低數據大小,可以只遷出表數據的表空間,在目標數據庫中通過重建索引的方式就可以生成索引數據了。

    1.2 表數據和索引使用不同表空間的SQL語法

    指定表數據及索引數據存儲表空間語句最簡單的形式如下。

    將表數據存儲在APP_DATA表空間里:

    create table T_ORDER ( ORDER_ID NUMBER(10) not null, …)tablespace APP_DATA;

    將索引數據存儲在APP_IDX表空間里:

    create index IDX_ORDER_ITEM_ORDER_ID on T_ORDER_ITEM ( ORDER_ID ASC)tablespace APP_IDX;

    1.3 PowerDesigner中如何操作

    1) 首先,必須創建兩個表空間。通過Model->Tablespace...在List of Tablespaces中創建兩個表空間:

    合理設計優化Oracle庫表設計的若干方法(2)

    圖 2 創建表空間



    2) 為每張表指定表數據存儲的表空間。在設計區中雙擊表,打開Table Properties設計窗口,切換到options 頁,按圖 3所示指定表數據的存儲表空間。

    合理設計優化Oracle庫表設計的若干方法(2)

    圖 3 指定表數據的存儲表空間



    3) 為每個索引指定索引數據的存儲表空間。在Table Properties中切換到Indexes頁,在這里列出了表的所有索引,雙擊需設置表空間的索引,在彈出的Index Properties窗口中切換到Options頁,按如下方式指定索引的存儲表空間。



    合理設計優化Oracle庫表設計的若干方法(2)

    圖 4 指定索引數據的存儲表空間



    將表空間的問題延展一下:一個應用系統庫表的表空間可以進行更精細的劃分。

    首先,如果表中存在LOB類型的字段,有為其指定一個特定的表空間,因為LOB類型的數據在物理存儲結構的管理上和一般數據的策略有很大的不同,將其放在一個獨立的表空間中,就可方便地設置其物理存儲參數了。

    其 次,需要考慮庫表數據的DML操作特性:根據DML(INSERT,UPDATE,DELETE)操作頻繁程度,將幾乎不發生任何DML操作的數據放在獨 立的表空間中,因為極少DML操作的表可設置符合其特性的物理參數:如PCTFREE可置為0,其BUFFER_POOL指定為KEEP,以便將數據緩存 在KEEP數據緩存區中等等,不一而足。

    此外,還可以考慮按業務需要將不同的業務模塊分開存放,這主要是考慮到備份問題。假設我們有一部分業務數據重要性很強,而其他的業務數據重要性相對較弱,這樣就可以將兩者分開存儲,以便設置不同的備份策略。

    當然,無節制的細化表空間也將帶來管理上和部署上的復雜,根據業務需求合理地規劃表空間以達到管理和性能上的最佳往往需要更多的權衡。

    ?2、顯式為主鍵列建立反向鍵索引

    2.1 反向鍵索引的原理和用途

    我 們知道Oracle會自動為表的主鍵列建立索引,這個默認的索引是普通的B-Tree索引。對于主鍵值是按順序(遞增或遞減)加入的情況,默認的B- Tree索引并不理想。這是因為如果索引列的值具有嚴格順序時,隨著數據行的插入,索引樹的層級增長很快。搜索索引發生的I/O讀寫次數和索引樹的層級數 成正比,也就是說,一棵具有5個層級的B-Tree索引,在最終讀取到索引數據時最多可能發生多達5次I/O操作。因而,減少索引的層級數是索引性能調整 的一個重要方法。

    如果索引列的數據以嚴格的有序的方式插入,那么B-Tree索引樹將變成一棵不對稱的"歪樹",如圖 5所示:

    合理設計優化Oracle庫表設計的若干方法(3)

    圖 5不對稱的B-Tree索引


    而如果索引列的數據以隨機值的方式插入,我們將得到一棵趨向對稱的索引樹,如圖 6所示:

    合理設計優化Oracle庫表設計的若干方法(3)

    圖 6對稱的B-Tree索引


    比較圖 5和圖 6,在圖 5中搜索到A塊需要進行5次I/O操作,而圖 6僅需要3次I/O操作。

    既 然索引列數據從序列中獲取,其有序性無法規避,但在建立索引時,Oracle允許對索引列的值進行反向,即預先對列值進行比特位的反向,如 1000,10001,10011,10111,1100經過反向后的值將是0001,1001,1101,0011。顯然經過位反向處理的有序數據變得 比較隨機了,這樣所得到的索引樹就比較對稱,從而提高表的查詢性能。

    但反向鍵索引也有它局限性:如果在WHERE語句中,需要對索引列的 值進行范圍性的搜索,如BETWEEN、<、>等,其反向鍵索引無法使用,此時,Oracle將執行全表掃描;只有對反向鍵索引列進行 <> 和 = 的比較操作時,其反向鍵索引才會得到使用。

    2.2 反向鍵索引的SQL語句

    回到我們上面的例子,由于T_ORDER和T_ORDER_ITEM的主鍵值來源于序列,主鍵值是有嚴格順序的,所以我們應該摒棄默認的Oracle所提供的索引,而采取顯式為主鍵指定一個反向鍵索引的方式。

    ORDER_ID為T_ORDER表的主鍵,主鍵名為PK_ORDER,我們為ORDER_ID列上建立一個反向鍵索引IDX_ORDER_ID,并使PK_ORDER_ID使用這個索引,其SQL語句如下:

    create table T_ORDER (

     ORDER_ID NUMBER(10) not null,

     CLIENT VARCHAR2(60),

     ADDRESS VARCHAR2(100),

     ORDER_DATE CHAR(8));

    create unique index IDX_ORDER_ID on T_ORDER ( ORDER_ID ASC) reverse;alter table T_ORDER add constraint PK_ORDER primary key (ORDER_ID) using index IDX_ORDER_ID;


    要保證創建IDX_ORDER_ID的SQL語句在創建PK_ORDER主鍵的SQL語句之前,因為主鍵需要引用到這個反向鍵索引。

    由于主鍵列的數據是唯一的,所以為IDX_ORDER_ID加上unique限定,使其成為唯一型的索引。

    2.3 PowerdDesigner如何操作

    1) 首先,需要為ORDER_ID列建立一個反向鍵索引。打開T_ORDER的Table Properties的窗口,切換到Indexes頁,新建一個名為IDX_ORDER_ID的索引。填寫完索引的名稱后,雙擊這個索引,彈出Index Properties窗口,在這個窗口的Columns中選擇ORDER_ID列。然后,切換到Options頁,按圖 7的方式將其設置為反向鍵索引。

    合理設計優化Oracle庫表設計的若干方法(3)

    圖 7 設置反向鍵索引
    ?

    2) 顯式指定主鍵PK_ORDER使用這個索引。在Table Properties窗口中切換到Keys頁,默認情況下,PowerDesigner為T_ORDER所指定的主鍵名為Key1,我們將其更名為 PK_ORDER,雙擊這個主鍵,彈出Key Properties窗口,切換到Options頁,按圖 8的方式為PK_ORDER指定IDX_ORDER_ID。

    合理設計優化Oracle庫表設計的若干方法(4)

    圖 8 為主鍵指定特定的索引


    不可否認PowerDesigner確實是目前業界最強大易用的數據庫設計工具,但很遺憾,當我們為表主鍵指定一個索引時,其產生的語句在順序上有問題:即創建主鍵的語句位于創建索引語句之前:

    create table T_ORDER (…);alter table T_ORDER add constraint PK_T_ORDER primary key (ORDER_ID) using index IDX_ORDER_ID;create unique index IDX_ORDER_ID on T_ORDER ( ORDER_ID ASC) reverse;


    我們可以通過對PowerDesigner生成SQL語句的設置進行調整,先生成創建表和索引的SQL語句,再創建為表添加主鍵和外鍵的SQL語句來達到曲線救國的目的,請看下一步。

    3)通過菜單Database->Generate Database...調出Database Configuration窗口,切換到Keys&Indexes頁,按圖 9設置:



    合理設計優化Oracle庫表設計的若干方法(4)

    圖 9 設置生成鍵和索引SQL的選項


    這里,我們將Primary Keys和Foreign keys的選項都取消,而將Indexes勾選,以達到只生成表的索引SQL語句的目的。

    點擊"確定"后,生成創建數據庫表及其索引的SQL語句,運行該SQL創建數據庫后,再按圖 10設置生成為表添加主鍵和外鍵的SQL語句:

    合理設計優化Oracle庫表設計的若干方法(4)

    圖 10 生成創建表主鍵和外鍵的SQL語句



    除此設置外,還必須切換到Tables & Views頁下,取消所有選項,避免重新生成創建表的語句。

    ?3、將子表的外鍵列的索引改為壓縮型

    3.1 壓縮型索引的原理和用途

    在前面的例子中,由于一條訂單會對應多條訂單條目,所以T_ORDER_ITEM的ORDER_ID字段總會出現重復的值,如:

    ITEM_ID ORDER_ID ITEM COUNT

    1 100 101 1

    2 100 104 2

    3 100 201 3

    4 200 301 2

    5 200 401 1

    6 200 205 3


    在ORDER_ID列上創建一個普通未壓縮的B-Tree索引,則索引數據的物理上的存儲形式如下:

    合理設計優化Oracle庫表設計的若干方法(5)

    圖 11 未進行壓縮的索引存儲


    ORDER_ID的重復值在索引塊中重復出現,這樣不但增加了存儲空間的需求,而且因為查詢時需要讀取更多的索引數據塊,所以查詢性能也會降低=。讓我們來看一下經過壓縮后索引數據的存儲方式:

    合理設計優化Oracle庫表設計的若干方法(5)

    圖 12 進行壓縮的索引存儲


    壓縮型的索引消除了重復的索引值,將相同索引列值所關聯的ROWID存儲在一起。這樣,不但節省了存儲空間,查詢效率也提高了,真可謂兩全齊美了。

    對象T_ORDER和T_ORDER_ITEM這樣的主從表進行查詢時,一般情況下,我們都必須通過外鍵查詢出子表所有關聯的記錄,所以在子表的外鍵上建立壓縮型的索引是非常適合的。

    3.2 壓縮型索引的SQL語句

    創建壓縮型索引的SQL語句非常簡單,在T_ORDER_ITEM的ORDER_ID上創建壓縮型索引的SQL如下所示:

    create index IDX_ORDER_ITEM_ORDER_ID on T_ORDER_ITEM ( ORDER_ID ASC) compress;


    需要在創建索引的語句后附上compress關鍵字就可以了。

    3.3 PowerDesigner如何創建壓縮型索引

    1) 打開T_ORDER_ITEM表的Table Properties的窗口,切換到Indexes頁,為ORDER_ID列創建一個名為IDX_ORDER_ITEM_ORDER_ID的索引。

    2) 雙擊IDX_ORDER_ITEM_ORDER_ID彈出Index Properties窗口,切換到Options頁,按圖 13將索引設置為壓縮型:

    合理設計優化Oracle庫表設計的若干方法(5)

    圖 13 將索引指定為壓縮型


    4、建立滿足需求的復合鍵索引

    設計人員希望通過T_ORDER表上的IDX_ORDER_COMPOSITE復合索引滿足以下兩種組合條件的查詢:

    ·CLIENT + ORDER_DATE + IS_SHIPPED

    ·ORDER_DATE + IS_SHIPPED

    為方便闡述,我們特地將IDX_ORDER_COMPOSITE的創建SQL語句再次列出:

    create index IDX_ORDER_COMPOSITE on T_ORDER ( CLIENT ASC, ORDER_DATE ASC, IS_SHIPPED ASC);


    事實上,在CLIENT + ORDER_DATE + IS_SHIPPED 三列上所執行的復合條件查詢會應用到這個索引,而在ORDER_DATE + IS_SHIPPED列上所執行的復合查詢不會使用這個索引,因而將導致一個全表掃描的操作。

    可以用許多工具來了解查詢語句的執行計劃,通過SET AUTOTRACE ON來查詢以上兩個復合查詢的執行計劃:

    打開SQL/Plus,輸入以下的語句:

    SQL> set autotrace on

    SQL> select * from t_order where CLIENT = '1' and ORDER_DATE='1' and IS_SHIPPED='1';


    分析得到的執行計劃為:

    SELECT STATEMENT Optimizer=CHOOSETABLE ACCESS (BY INDEX ROWID) OF 'T_ORDER' INDEX (RANGE SCAN) OF 'IDX_ORDER_COMPOSITE' (NON-UNIQUE)


    可見Oracle先利用IDX_ORDER_COMPOSITE得到滿足條件的記錄ROWID,再通過ROWID返回記錄。

    而下面查詢語句:

    SQL> select * from t_order where ORDER_DATE='1' and IS_SHIPPED='1'


    的執行計劃則為:

    SELECT STATEMENT Optimizer=CHOOSE TABLE ACCESS (FULL) OF 'T_ORDER'


    很明顯,Oracle在T_ORDER表上執行了一個全表掃描的操作,沒有用到IDX_ORDER_COMPOSITE索引。

    對復合列索引,我們得出這個結論:

    假設在COL_1,COL_2,…,COL_n這些列上建立了一個復合索引:

    create index IDX _COMPOSITE on TABLE1

    {

    COL_1,

    COL_2,

    …,

    COL_n

    }


    則只有WHERE語句上包含COL_1(復合索引的第一個字段)的查詢才會使用這個復合索引,而未包含COL_1的查詢則不會使用這個復合索引。

    回到我們的例子,如何建立滿足CLIENT + ORDER_DATE + IS_SHIPPED和ORDER_DATE + IS_SHIPPED兩種查詢的索引呢?

    考 慮到IS_SHIPPED列基數很小,只有兩個可能的值:0,1。在這種情況下,有兩種方案:第一,分別為CLIENT + ORDER_DATE + IS_SHIPPED和ORDER_DATE + IS_SHIPPED建立一個復合索引;第二,分別在CLIENT和ORDER_DATE列上建立一個索引,而IS_SHIPEED列不建立索引。

    第一種方案的查詢效率最快,但因為CLIENT和ORDER_DATE在索引中會重復出現兩次,占用較大的存儲空間。第二種方案CLIENT和ORDER_DATE不會在索引存儲出現兩次,較為節省空間,查詢效率比之于第一種方案會稍低一些,但影響不大。

    我們采用第二種方案為CLIENT和ORDER_DATE分別創建索引IDX_CLIENT和IDX_ORDER_DATE,組合查詢條件為CLIENT + ORDER_DATE + IS_SHIPPED時的執行計劃為:

    SELECT STATEMENT Optimizer=CHOOSE TABLE ACCESS (BY INDEX ROWID) OF 'T_ORDER' AND-EQUAL INDEX (RANGE SCAN) OF 'IDX_CLIENT' (NON-UNIQUE) INDEX (RANGE SCAN) OF 'IDX_ORDER_DATE' (NON-UNIQUE)


    而組合條件為ORDER_DATE + IS_SHIPPED時的執行計劃為:

    SELECT STATEMENT Optimizer=CHOOSE TABLE ACCESS (BY INDEX ROWID) OF 'T_ORDER' INDEX (RANGE SCAN) OF 'IDX_ORDER_DATE' (NON-UNIQUE)


    通過這樣的改造,我們得到了一個滿足兩種組合查詢的執行計劃。

    總結

    貫穿本文的訂單主從表實例結構上很簡單,但是其粗糙的設計包含了許多問題,這也是許多對Oracle物理存儲結構沒有很好理解的數據庫設計師容易忽視的地方。

    在一般情況下,這樣的設計并不會導致嚴重系統的性能問題,但是精益求精是每一位優秀軟件設計師的品質,此外,對于設計師,一定要清楚這樣一條規律:對于等質的性能提升,在編碼層面往往需要比設計層面付出更多的艱辛。

    在Oracle中提高數據庫的性能需要考慮的問題,注意的誤區還很多,本文涵蓋是一些最常見的問題。下面,我們將提高數據庫操作性能方法及一些誤區作個小結:

    ·對于大表,可以考慮創建分區表,分區表有范圍分區、散列分區、列表分區和散列分區幾種,通過它可以達到化大表為小表的目的。

    ·考慮適量的數據冗余,如一個業務表有一個審批狀態,審批需要經過多步,每一步對應審批表的一條記錄,最后審批的那條記錄決定了業務的狀態。我們大可在業務表中存放一個審批狀態的標志,以取消每次需要通過關聯審批表獲取業務審批狀態的復雜的關聯表查詢。

    · 不要做太多的關聯表查詢,一些幾乎不發生數據變動的表碼表,如性別,學歷,婚姻狀態等表碼表,可以考慮在應用程序啟動時一次性地下載到應用程序的內存中緩 存起來,在從數據庫獲取結果集后,再由程序利用這些緩存的表碼表數據來翻譯這些表碼字段,而不要在數據庫中通過表間的關聯查詢方式來翻譯這些字段。

    · 常看到一些令我瞠目的設計:在需要進行頻繁DML(INSERT,UPDATE,DELETE)操作的表的某些基數低的字段(如性別,婚姻狀態)上創建位 圖索引。位圖索引是好東西,但它是有使用范圍的,在OLTP系統中,需要進行頻繁DML操作的表中不應該出現位圖索引,位圖索引只適用于幾乎不進行DML 操作,只進行查詢的DSS系統中。此外,聚簇和索引組織表也都更適合DSS系統,而非OLTP系統。
    (完)

    posted @ 2006-07-19 18:16 Alex 閱讀(377) | 評論 (0)編輯 收藏

    key words: Oracle監控? shell腳本

    前言

    這篇文章介紹了DBA每天在監控Oracle數 據庫方面的職責,講述了如何通過shell腳本來完成這些重復的監控工作。本文首先回顧了一些DBA常用的Unix命令,以及解釋了如何通過Unix Cron來定時執行DBA腳本。同時文章還介紹了8個重要的腳本來監控Oracle數據庫:

    檢查實例的可用性

    檢查監聽器的可用性

    檢查alert日志文件中的錯誤信息

    在存放log文件的地方滿以前清空舊的log文件

    分析table和index以獲得更好的性能

    檢查表空間的使用情況

    找出無效的對象

    監控用戶和事務

    DBA需要的Unix基本知識

    基本的UNIX命令

    以下是一些常用的Unix命令:

    ps--顯示進程 grep--搜索文件中的某種文本模式 mailx--讀取或者發送mail cat--連接文件或者顯示它們 cut--選擇顯示的列 awk--模式匹配語言 df--顯示剩余的磁盤空間

    以下是DBA如何使用這些命令的一些例子:

    顯示服務器上的可用實例:

    $ ps -ef | grep smon

    oracle 21832 1 0 Feb 24 ? 19:05 ora_smon_oradb1

    oracle 898 1 0 Feb 15 ? 0:00 ora_smon_oradb2

    dliu 25199 19038 0 10:48:57 pts/6 0:00 grep smon

    oracle 27798 1 0 05:43:54 ? 0:00 ora_smon_oradb3

    oracle 28781 1 0 Mar 03 ? 0:01 ora_smon_oradb4、
    顯示服務器上的可用監聽器:

    $ ps -ef | grep listener | grep -v grep

    (譯者注:grep命令應該加上-i參數,即grep -i listener,

    該參數的作用是忽略大小寫,因為有些時候listener是大寫的,這時就會看不到結果)

    oracle 23879 1 0 Feb 24 ? 33:36 /8.1.7/bin/tnslsnr listener_db1 -inherit

    oracle 27939 1 0 05:44:02 ? 0:00 /8.1.7/bin/tnslsnr listener_db2 -inherit

    oracle 23536 1 0 Feb 12 ? 4:19 /8.1.7/bin/tnslsnr listener_db3 -inherit

    oracle 28891 1 0 Mar 03 ? 0:01 /8.1.7/bin/tnslsnr listener_db4 -inherit
    查看Oracle存檔目錄的文件系統使用情況

    $ df -k | grep oraarch

    /dev/vx/dsk/proddg/oraarch 71123968 4754872 65850768 7% /u09/oraarch
    統計alter.log文件中的行數:

    $ cat alert.log | wc -l

    2984

    列出alert.log文件中的全部Oracle錯誤信息:

    $ grep ORA- alert.log

    ORA-00600: internal error code, arguments: [kcrrrfswda.1], [], [], [], [], []

    ORA-00600: internal error code, arguments: [1881], [25860496], [25857716], []

    CRONTAB基本

    一個crontab文件中包含有六個字段:

    分鐘 0-59

    小時 0-23

    月中的第幾天 1-31

    月份 1 - 12

    星期幾 0 - 6, with 0 = Sunday

    Unix命令或者Shell腳本

    要編輯一個crontab文件,輸入:Crontab -e

    要查看一個crontab文件,輸入:

    Crontab -l

    0 4 * * 5 /dba/admin/analyze_table.ksh

    30 3 * * 3,6 /dba/admin/hotbackup.ksh /dev/null 2>&1

    在上面的例子中,第一行顯示了一個分析表的腳本在每個星期5的4:00am運行。第二行顯示了一個執行熱備份的腳本在每個周三和周六的3:00a.m.運行。

    監控數據庫的常用Shell腳本

    以下提供的8個shell腳本覆蓋了DBA每日監控工作的90%,你可能還需要修改UNIX的環境變量。

    檢查Oracle實例的可用性

    oratab文件中列出了服務器上的所有數據庫

    $ cat /var/opt/oracle/oratab


    ############################################################


    ## /var/opt/oracle/oratab##


    ############################################################

    oradb1:/u01/app/oracle/product/8.1.7:Y

    oradb2:/u01/app/oracle/product/8.1.7:Y

    oradb3:/u01/app/oracle/product/8.1.7:N

    oradb4:/u01/app/oracle/product/8.1.7:Y
    以下的腳本檢查oratab文件中列出的所有數據庫,并且找出該數據庫的狀態(啟動還是關閉)

    ##############################################################


    ## ckinstance.ksh ## ###################################################################

    ORATAB=/var/opt/oracle/oratab

    echo `date`

    echo Oracle Database(s) Status `hostname` :

    db=`egrep -i :Y|:N $ORATAB | cut -d: -f1 | grep -v # | grep -v *`

    pslist=`ps -ef | grep pmon`

    for i in $db ; do

    echo $pslist | grep ora_pmon_$i > /dev/null 2>$1

    if (( $? )); then

    echo Oracle Instance - $i: Down

    else

    echo Oracle Instance - $i: Up

    fi

    done

    使用以下的命令來確認該腳本是可以執行的:

    $ chmod 744 ckinstance.ksh

    $ ls -l ckinstance.ksh

    -rwxr--r-- 1 oracle dba 657 Mar 5 22:59 ckinstance.ksh*
    以下是實例可用性的報表:

    $ ckinstance.ksh

    Mon Mar 4 10:44:12 PST 2002

    Oracle Database(s) Status for DBHOST server:

    Oracle Instance - oradb1: Up

    Oracle Instance - oradb2: Up

    Oracle Instance - oradb3: Down

    Oracle Instance - oradb4: Up

    檢查Oracle監聽器的可用性

    以下有一個類似的腳本檢查Oracle監聽器。如果監聽器停了,該腳本將會重新啟動監聽器:

    #######################################################################

    ## cklsnr.sh ##

    #######################################################################

    #!/bin/ksh

    DBALIST=primary.dba@company.com,another.dba@company.com;export DBALIST

    cd /var/opt/oracle

    rm -f lsnr.exist

    ps -ef | grep mylsnr | grep -v grep > lsnr.exist

    if [ -s lsnr.exist ]

    then

    echo

    else

    echo Alert | mailx -s Listener ‘mylsnr‘ on `hostname` is down $DBALIST

    TNS_ADMIN=/var/opt/oracle; export TNS_ADMIN

    ORACLE_SID=db1; export ORACLE_SID

    ORAENV_ASK=NO; export ORAENV_ASK

    PATH=$PATH:/bin:/usr/local/bin; export PATH

    . oraenv

    LD_LIBRARY_PATH=${ORACLE_HOME}/lib;export LD_LIBRARY_PATH

    lsnrctl start mylsnr

    fi

    檢查Alert日志(ORA-XXXXX)

    每個腳本所使用的一些環境變量可以放到一個profile中:

    ####################################################################### 

    ## oracle.profile ##

    #######################################################################

    EDITOR=vi;export EDITOR ORACLE_BASE=/u01/app/oracle; export

    ORACLE_BASE ORACLE_HOME=$ORACLE_BASE/product/8.1.7; export

    ORACLE_HOME LD_LIBRARY_PATH=$ORACLE_HOME/lib; export

    LD_LIBRARY_PATH TNS_ADMIN=/var/opt/oracle;export

    TNS_ADMIN NLS_LANG=american; export

    NLS_LANG NLS_DATE_FORMAT=‘Mon DD YYYY HH24:MI:SS‘; export

    NLS_DATE_FORMAT ORATAB=/var/opt/oracle/oratab;export

    ORATAB PATH=$PATH:$ORACLE_HOME:$ORACLE_HOME/bin:/usr/ccs/bin:/bin:/usr/bin:/usr/sbin:/

    sbin:/usr/openwin/bin:/opt/bin:.; export

    PATH DBALIST=primary.dba@company.com,another.dba@company.com;export

    DBALIST
    以下的腳本首先調用oracle.profile來設置全部的環境變量。如果發現任何的Oracle錯誤,該腳本還會給DBA發送一個警告的email。

    ####################################################################

    ## ckalertlog.sh ##

    ####################################################################

    #!/bin/ksh

    .. /etc/oracle.profile

    for SID in `cat $ORACLE_HOME/sidlist`

    do

    cd $ORACLE_BASE/admin/$SID/bdump

    if [ -f alert_${SID}.log ]

    then

    mv alert_${SID}.log alert_work.log

    touch alert_${SID}.log

    cat alert_work.log >> alert_${SID}.hist

    grep ORA- alert_work.log > alert.err

    fi

    if [ `cat alert.err|wc -l` -gt 0 ]

    then

    mailx -s ${SID} ORACLE ALERT ERRORS $DBALIST < alert.err

    fi

    rm -f alert.err

    rm -f alert_work.log

    done

    清除舊的歸檔文件

    以下的腳本將會在log文件達到90%容量的時候清空舊的歸檔文件:

    $ df -k | grep arch

    Filesystem kbytes used avail capacity Mounted on

    /dev/vx/dsk/proddg/archive 71123968 30210248 40594232 43% /u08/archive

    #######################################################################

    ## clean_arch.ksh ##

    #######################################################################

    #!/bin/ksh

    df -k | grep arch > dfk.result

    archive_filesystem=`awk -F ‘{ print $6 }‘ dfk.result`

    archive_capacity=`awk -F ‘{ print $5 }‘ dfk.result`

    if [[ $archive_capacity > 90% ]]

    then

    echo Filesystem ${archive_filesystem} is ${archive_capacity} filled

    # try one of the following option depend on your need

    find $archive_filesystem -type f -mtime +2 -exec rm -r {} ;

    tar

    rman

    fi

    分析表和索引(以得到更好的性能)

    以下我將展示如果傳送參數到一個腳本中:

    #################################################################### 

    ## analyze_table.sh ##

    ####################################################################

    #!/bin/ksh

    # input parameter: 1: password # 2: SID

    if (($#<1)) then echo "Please enter oracle user password as the first parameter !" exit 0

    fi

    if (($#<2)) then echo "Please enter instance name as the second parameter!" exit 0

    fi

    要傳入參數以執行該腳本,輸入:

    $ analyze_table.sh manager oradb1 
    腳本的第一部分產生了一個analyze.sql文件,里面包含了分析表用的語句。腳本的第二部分分析全部的表:

    #####################################################################

    ## analyze_table.sh ##

    #####################################################################

    sqlplus -s <
    oracle/$1@$2

    set heading off

    set feed off

    set pagesize 200

    set linesize 100

    spool analyze_table.sql

    select ANALYZE TABLE || owner || . || segment_name ||

    ESTIMATE STATISTICS SAMPLE 10 PERCENT;

    from dba_segments

    where segment_type = TABLE

    and owner not in (SYS, SYSTEM);

    spool off

    exit

    !

    sqlplus -s <
    oracle/$1@$2

    @./analyze_table.sql

    exit

    !
    以下是analyze.sql的一個例子:

    $ cat analyze.sql

    ANALYZE TABLE HIRWIN.JANUSAGE_SUMMARY ESTIMATE STATISTICS SAMPLE 10 PERCENT;

    ANALYZE TABLE HIRWIN.JANUSER_PROFILE ESTIMATE STATISTICS SAMPLE 10 PERCENT;

    ANALYZE TABLE APPSSYS.HIST_SYSTEM_ACTIVITY ESTIMATE STATISTICS SAMPLE 10 PERCENT;

    ANALYZE TABLE HTOMEH.QUEST_IM_VERSION ESTIMATE STATISTICS SAMPLE 10 PERCENT;

    ANALYZE TABLE JSTENZEL.HIST_SYS_ACT_0615 ESTIMATE STATISTICS SAMPLE 10 PERCENT;

    ANALYZE TABLE JSTENZEL.HISTORY_SYSTEM_0614 ESTIMATE STATISTICS SAMPLE 10 PERCENT;

    ANALYZE TABLE JSTENZEL.CALC_SUMMARY3 ESTIMATE STATISTICS SAMPLE 10 PERCENT;

    ANALYZE TABLE IMON.QUEST_IM_LOCK_TREE ESTIMATE STATISTICS SAMPLE 10 PERCENT;

    ANALYZE TABLE APPSSYS.HIST_USAGE_SUMMARY ESTIMATE STATISTICS SAMPLE 10 PERCENT;

    ANALYZE TABLE PATROL.P$LOCKCONFLICTTX ESTIMATE STATISTICS SAMPLE 10 PERCENT;

    檢查表空間的使用

    以下的腳本檢測表空間的使用。如果表空間只剩下10%,它將會發送一個警告email。

    #####################################################################

    ## ck_tbsp.sh ##

    #####################################################################

    #!/bin/ksh

    sqlplus -s <
    oracle/$1@$2

    set feed off

    set linesize 100

    set pagesize 200

    spool tablespace.alert

    SELECT F.TABLESPACE_NAME,

    TO_CHAR ((T.TOTAL_SPACE - F.FREE_SPACE),999,999) "USED (MB)",

    TO_CHAR (F.FREE_SPACE, 999,999) "FREE (MB)",

    TO_CHAR (T.TOTAL_SPACE, 999,999) "TOTAL (MB)",

    TO_CHAR ((ROUND ((F.FREE_SPACE/T.TOTAL_SPACE)*100)),999)|| % PER_FREE

    FROM (

    SELECT TABLESPACE_NAME,

    ROUND (SUM (BLOCKS*(SELECT VALUE/1024

    FROM V\$PARAMETER

    WHERE NAME = db_block_size)/1024)

    ) FREE_SPACE

    FROM DBA_FREE_SPACE

    GROUP BY TABLESPACE_NAME

    ) F,

    (

    SELECT TABLESPACE_NAME,

    ROUND (SUM (BYTES/1048576)) TOTAL_SPACE

    FROM DBA_DATA_FILES

    GROUP BY TABLESPACE_NAME

    ) T

    WHERE F.TABLESPACE_NAME = T.TABLESPACE_NAME

    AND (ROUND ((F.FREE_SPACE/T.TOTAL_SPACE)*100)) < 10;

    spool off

    exit

    !

    if [ `cat tablespace.alert|wc -l` -gt 0 ]

    then

    cat tablespace.alert -l tablespace.alert > tablespace.tmp

    mailx -s "TABLESPACE ALERT for ${2}" $DBALIST < tablespace.tmp

    fi

    警告email輸出的例子如下:

    TABLESPACE_NAME USED (MB) FREE (MB) TOTAL (MB) PER_FREE 

    ------------------- --------- ----------- ------------------- ------------------

    SYSTEM 2,047 203 2,250 9 %

    STBS01 302 25 327 8 %

    STBS02 241 11 252 4 %

    STBS03 233 19 252 8 %
    查找出無效的數據庫對象

    以下查找出無效的數據庫對象:

    #####################################################################
    ## invalid_object_alert.sh ##
    ##################################################################### 
    #!/bin/ksh . /etc/oracle.profile 

    sqlplus -s <
    oracle/$1@$2

    set feed off

    set heading off column object_name format a30

    spool invalid_object.alert

    SELECT OWNER, OBJECT_NAME, OBJECT_TYPE,
    STATUS FROM DBA_OBJECTS WHERE STATUS = 
    INVALID ORDER BY OWNER, OBJECT_TYPE, OBJECT_NAME;

    spool off

    exit ! if [ `cat invalid_object.alert|wc -l` -gt 0 ] then

    mailx -s "INVALID OBJECTS for ${2}" $DBALIST < invalid_object.alert

    fi$ cat invalid_object.alert

    OWNER OBJECT_NAME OBJECT_TYPE STATUS

    --------------------------------------------
    HTOMEH DBMS_SHARED_POOL PACKAGE BODY INVALID

    HTOMEH X_$KCBFWAIT VIEW INVALID

    IMON IW_MON PACKAGE INVALID

    IMON IW_MON PACKAGE BODY INVALID

    IMON IW_ARCHIVED_LOG VIEW INVALID

    IMON IW_FILESTAT VIEW INVALID

    IMON IW_SQL_FULL_TEXT VIEW INVALID

    IMON IW_SYSTEM_EVENT1 VIEW INVALID

    IMON IW_SYSTEM_EVENT_CAT VIEW INVALIDLBAILEY CHECK_TABLESPACE_USAGE PROCEDURE INVALID

    PATROL P$AUTO_EXTEND_TBSP VIEW INVALID

    SYS DBMS_CRYPTO_TOOLKIT PACKAGE INVALID

    SYS DBMS_CRYPTO_TOOLKIT PACKAGE BODY INVALID

    SYS UPGRADE_SYSTEM_TYPES_TO_816 PROCEDURE INVALID

    SYS AQ$_DEQUEUE_HISTORY_T TYPE INVALID

    SYS HS_CLASS_CAPS VIEW INVALID SYS HS_CLASS_DD VIEW INVALID
    監視用戶和事務(死鎖等)

    以下的腳本在死鎖發生的時候發送一個警告e-mail:

    ###################################################################

    ## deadlock_alert.sh ##

    ##################################################################
    ##!/bin/ksh

    .. /etc/oracle.profile

    sqlplus -s <
    oracle/$1@$2

    set feed off

    set heading off

    spool deadlock.alert

    SELECT SID, DECODE(BLOCK, 0, NO, YES ) BLOCKER,

    DECODE(REQUEST, 0, NO,YES ) WAITER

    FROM V$LOCK

    WHERE REQUEST > 0 OR BLOCK > 0

    ORDER BY block DESC;

    spool off

    exit

    !

    if [ `cat deadlock.alert|wc -l` -gt 0 ]

    then

    mailx -s "DEADLOCK ALERT for ${2}" $DBALIST < deadlock.alert

    fi


    結論

    0,20,40 7-17 * * 1-5 /dba/scripts/ckinstance.sh > /dev/null 2>&1

    0,20,40 7-17 * * 1-5 /dba/scripts/cklsnr.sh > /dev/null 2>&1

    0,20,40 7-17 * * 1-5 /dba/scripts/ckalertlog.sh > /dev/null 2>&1

    30 * * * 0-6 /dba/scripts/clean_arch.sh > /dev/null 2>&1

    * 5 * * 1,3 /dba/scripts/analyze_table.sh > /dev/null 2>&1

    * 5 * * 0-6 /dba/scripts/ck_tbsp.sh > /dev/null 2>&1

    * 5 * * 0-6 /dba/scripts/invalid_object_alert.sh > /dev/null 2>&1

    0,20,40 7-17 * * 1-5 /dba/scripts/deadlock_alert.sh > /dev/null 2>&1

    通過以上的腳本,可大大減輕你的工作。你可以使用這些是來做更重要的工作,例如性能調整。

    posted @ 2006-07-18 16:08 Alex 閱讀(674) | 評論 (0)編輯 收藏


    									2003-05 余楓

    ORACLE客戶端連服務器的注意事項:

    1. 通過SQL*NET協議,ORACLE客戶端連服務器時一般需要配置sqlnet.ora和tnsnames.ora。
    它們默認的目錄在$ORACLE_HOME/network/admin 目錄下

    也可以設置環境變量TNS_ADMIN指向你想用的sqlnet.ora和tnsnames.ora目錄
    例如:
    TNS_ADMIN=/home/oracle/config/9.0.1;export TNS_ADMIN

    sqlnet.ora文件決定找數據庫服務器別名的方式

    默認的參數有
    NAMES.DEFAULT_DOMAIN = WORLD
    NAMES.DIRECTORY_PATH = (TNSNAMES, ONAMES, HOSTNAME)

    如果你的ORACLE客戶端和服務器默認的域名不一樣,需要用#號注釋第一行
    #NAMES.DEFAULT_DOMAIN = WORLD
    使它不起作用。

    NAMES.DIRECTORY_PATH指定找服務器別名的順序 (本地的tnsnames.ora文件, 命名服務器, 主機名方式)

    服務器的sqlnet.ora里可以設置檢查客戶端是否alive的時間間隔
    sqlnet.expire_time = 10

    tnsnames.ora文件里寫數據庫服務器別名的詳細內容,有以下幾種寫法:

    # 一般的寫法
    APPDB =
    (DESCRIPTION =
    (ADDRESS_LIST =
    (ADDRESS = (PROTOCOL = TCP)(HOST = 192.168.0.35)(PORT = 1521))
    )
    (CONNECT_DATA =
    (SERVICE_NAME = appdb)
    )
    )

    # 明確標明用dedicated方式連接數據庫
    APPD=
    (DESCRIPTION=
    (ADDRESS=(PROTOCOL=TCP)(HOST=192.168.0.35)(PORT=1521))
    (CONNECT_DATA=
    (SERVICE_NAME=appdb)
    (SERVER=DEDICATED)))

    # 對多個listener端口做均衡負載方式連接數據庫
    APPS =
    (DESCRIPTION =
    (ADDRESS_LIST =
    (ADDRESS = (PROTOCOL = TCP)(HOST = 192.168.0.35)(PORT = 1521))
    (ADDRESS = (PROTOCOL = TCP)(HOST = 192.168.0.35)(PORT = 1856))
    )
    (CONNECT_DATA =
    (SERVICE_NAME = appdb)
    )
    )

    # 注意:如果數據庫服務器用MTS,客戶端程序需要用database link時最好明確指明客戶端用dedicated直連方式,
    # 不然會遇到很多跟分布式環境有關的ORACLE BUG。
    # 一般情況下數據庫服務器用直接的連接會好一些,除非你的實時數據庫連接數接近1000。

    2. /etc/hosts (UNIX)
    或者windows\hosts(WIN98) winnt\system32\drivers\etc\hosts (WIN2000)
    客戶端需要寫入數據庫服務器IP地址和主機名的對應關系。

    127.0.0.1 localhost
    192.168.0.35 oracledb oracledb
    192.168.0.45 tomcat tomcat
    202.84.10.193 bj_db bj_db

    有些時候我們配置好第一步后,tnsping 數據庫服務器別名顯示是成功的,
    但是sqlplus username/password@servicename不通,jdbc thin link 也不通的時候,
    一定不要忘了在客戶端做這一步,原因可能是DNS服務器里沒有設置這個服務器IP地址和主機名的對應關系。

    如果同時有私有IP和Internet上公有IP,私有IP寫在前面,公有IP寫在后面。

    編輯前最好留一個備份,增加一行時也最好用復制粘貼,避免編輯hosts時空格或者tab字符錯誤。

    3. UNIX下ORACLE多數據庫的環境,OS客戶端需要配置下面兩個環境變量

    ORACLE_SID=appdb;export ORACLE_SID
    TWO_TASK=appdb;export TWO_TASK

    來指定默認的目標數據庫。
    posted @ 2006-07-17 22:31 Alex 閱讀(274) | 評論 (0)編輯 收藏

    Windows

    • 下載并安裝 Tor & Privoxy
      http://www.vidalia-project.net/dist/vidalia-bundle-0.1.1.21-0.0.5.exe
      安裝完之后,在localhost:8118有http代理(privoxy提供),localhost:9050有socks5代理(tor提供)
      如果這時候可以配置Firefox使用8118和9050這兩個端口,那就所有的Internet訪問都將通過tor訪問,會很慢。所以這種方式不推薦,要自己編輯一下代理配置腳本
      Proxy settings in Firefox
    • 編輯proxy.pac
      可以在某個地方創建代理配置腳本, 比如說c:\proxy.pac, 下面是我用的內容, 可以上一些比較常用的網站, 比如wikipedia和google網頁快照. 如果查詢google也經常有問題, 可以把第一條 nosite.google.com 改成 .google.com 記得google.com前面有個點, 這樣所有訪問google的請求都會通過代理,不過做好心理準備,會很慢
      function FindProxyForURL(url, host)
      {
      url = url.toLowerCase();
      host = host.toLowerCase();

      if(dnsDomainIs(host,"nosite.google.com")) return "PROXY localhost:8118";
      else if(dnsDomainIs(host,".blogspot.com")) return "PROXY localhost:8118";
      else if(dnsDomainIs(host,".wordpress.com")) return "PROXY localhost:8118";
      else if(dnsDomainIs(host,"wikipedia.org")) return "PROXY localhost:8118";
      else if(shExpMatch(url,"*q=cache:*")) return "PROXY localhost:8118";
      else return "DIRECT";
      }
    • 配置IE使用代理配置腳本
      上面那個Firefox配置界面中,選擇Aotumatic proxy configuration URL, 填入
      file://c:/proxy.pac

      IE的配置類似.每次重新修改proxy.pac,都應該到上面的界面Reload代理配置腳本(比較討厭,好在這個文件修改次數不會很多)

    Debian Linux

    這里主要講Debian下的安裝配置,其他版本的Linux可以參考tor的官方說明

    • 安裝 tor privoxy
    •  apt-get install tor privoxy
    • 配置privoxy
    • Debian下面的privoxy需要額外的配置,因為privoxy需要使用tor提供的9050 socks5端口.具體是配置/etc/privoxy/config,加一行

      forward-socks4a / localhost:9050 .

      不要漏掉最后的點.然后重起一下privoxy

      /etc/init.d/tor restart
    • 如果使用Firefox,剩下的配置可以參考Windows下面的配置方法

    參考

    http://www.linuxsir.org/bbs/showthread.php?t=232436
    http://tor.eff.org/docs/tor-doc-win32.html.en

    Technorati Tags: , ,

    posted @ 2006-07-17 11:01 Alex 閱讀(3136) | 評論 (0)編輯 收藏

         摘要: 作者:John Smiley 學習在 RHEL 2.1、RHEL 3 或 SLES 8 上從頭安裝 Oracle 數據庫 10g 的基礎知識(僅用于評估) ...  閱讀全文
    posted @ 2006-07-13 13:57 Alex 閱讀(646) | 評論 (0)編輯 收藏

    key words: Oracle閃回 flash

      1、Oracle 9i的閃回查詢功能

       在Oracle 9i之前,如果用戶錯誤操作數據后,除了不完全恢復外,沒有好的解決辦法。Oracle 9i中提供閃回查詢,由一個新的包DBMS_FLASH來實現。用戶使用閃回查詢可以及時取得誤操作DML(Delete、Update、Insert) 前某一時間點數據庫的映像視圖,用戶可以利用系統時間或系統改變號(SCN:System Change Number)來指定這個只讀視圖,并可以針對錯誤進行相應的恢復措施。閃回查詢功能完全依賴于自動回滾段管理(AUM),對于Drop等誤操作不能恢 復。閃回特性可應用在以下方面:

      (1)自我維護過程中的修復:當一些重要的記錄被意外刪除,用戶可以向后移動到一個時間點,查看丟失的行并把它們重新插入現在的表內恢復。

      (2)恢復Email和聲音Email:當用戶意外刪除了Email或者聲音信息時,可以通過移回到固定時間點來恢復刪除。

      (3)賬號平衡狀況:可以查看以前的歷史數據。如銀行外幣管理中用于記錄特定時間的匯率。在以前,匯率變更被記錄在一個歷史表中,現在就可以通過閃回功能進行查詢。

      (4)用于趨勢分析的決策支持系統:決策支持系統和聯機分析應用必須執行一個長時間的事務。使用閃回查詢,這些應用可以對歷史數據執行分析和建模。例如,特定產品如礦泉水隨季節變化需求情況的變化。

      2、回滾段概述

      回滾段用于存放數據修改之前的位置和值,回滾段的頭部包含正在使用的該回滾段事務的信息。回滾段的作用如下:

      (1)事務回滾:當事務修改表中數據的時候,該數據修改前的值(即前影像)會存放在回滾段中,當用戶回滾事務時,Oracle將會利用回滾段中的數據前影像來將修改的數據恢復到原來的值。

      (2)事務恢復:當事務正在處理的時候,例程失敗,回滾段的信息保存在重做日志文件中,Oracle將在下次打開數據庫時利用回滾來恢復未提交的數據。

      (3)讀一致性:當一個會話正在修改數據時,其它的會話將看不到該會話未提交的修改。而且,當一個語句正在執行時,該語句將看不到從該語句開始執行后的未提交的修改(語句級讀一致性)。

      3、Oracle中Delete和Commit操作的流程分析

      (1)刪除(Delete)流程

      ·Oracle讀Block(數據塊)到Buffer Cache(緩沖區)(如果該Block在Buffer中不存在);

      ·在Redo Log Buffer(重做日志緩沖區)中記錄Delete操作的細節;

      ·在相應回滾段段頭的事物表中創建一個Undo(回滾)條目;

      ·把將要刪除的記錄創建前鏡像,存放到Undo Block(回滾塊)中;

      ·在Buffer Cache中的相應數據塊上刪除記錄,并且標記相應的數據塊為Dirty(臟)。

      (2)提交(Commit)流程

      ·Oracle產生一個SCN;

      ·在回滾段事物表中標記該事物狀態為Commited;

      ·LGWR(日志讀寫進程) Flush Log Buffer到日志文件;

      ·如果此時數據塊仍然在Buffer Cache中,那么SCN將被記錄到Block Header上,這被稱為快速提交;

      ·如果Dirty Block已經被寫回到磁盤,那么下一個訪問這個Block的進程將會自回滾段中獲取該事物的狀態,確認該事物被提交。然后這個進程獲得提交SCN并寫回到Block Header上,這被稱為延遲塊清除。

      4、Oracle 9i中閃回查詢操作實例

      進行閃回查詢必須設置自動回滾段管理,在init.ora設置參數UNDO_MANAGEMENT=AUTO,參數UNDO_RETENTION=n,決定了能往前閃回的最大時間,值越大就需要越多Undo空間。

      例:Oracle 9i的Flashback Query操作。

      (1)創建閃回查詢用戶

    SQL> create user flashtest identified by flashtest;
    SQL> grant connect, resource to flashtest;
    SQL> grant execute on dbms_flashback to flashtest;
    SQL> connect flashtest/flashtest;

      (2)創建測試表,插入測試記錄

    SQL> create table test(id number(3));
    SQL> insert into test values (1);
    SQL> insert into test values(2);
    SQL> commit;
    SQL> create table rec_date(date_scn);

      注意:在執行步驟3或者步驟4之前,等待5分鐘。

      (3)刪除記錄

    SQL> execute dbms_flashback.disable;
    SQL> insert into rec_date select sysdate from dual;
    SQL> commit;
    SQL> delete from test where id=1;
    SQL> commit;

      通過以上的操作,我們插入了兩條記錄,并刪除了其中一條記錄。在以下的操作中,我們將通過flashback query找到刪除的記錄

      (4)閃回查詢

    SQL> DECLARE
    Restore_scn date;
    BEGIN
    Select date_scn into restore_scn from rec_date;
    Dbms_flashback.enable_at_time (restore_scn);
    END;
    SQL> select * from test;
    ID
    1
    2


      可以看出,雖然刪除記錄并提交,但是通過閃回操作,仍能查詢到刪除前 的兩條記錄。需要注意Oracle每5分鐘記錄一次SCN,并將SCN和對應時間的映射進行紀錄。如果原來插入的記錄到做閃回操作的時間在5分鐘之內,用 基于時間的閃回查詢可能得不到記錄,因為基于時間點的查詢實際上是轉化為最近的一次SCN,然后從這個SCN開始進行恢復。因此,如果需要精確的查詢可以 采用基于SCN的閃回查詢,可精確閃回到需要恢復的時間。可以通過DBMS_FLASHBACK.GET_SYSTEM_CHANGE_NUMBER語句 獲取SCN。

      Oracle 10g的閃回查詢概述

      與Oracle 9i相比Oracle 10g的
    Flash back有了非常大的改進,從普通的Flashback Query發展到了多種形式,主要表現在如下幾方面新特性:

      1、Flashback Database

      Oracle Flashback Database特性允許通過SQL語句Flashback Database語句,讓
    數據庫 前 滾到當前的前一個時間點或者SCN,而不需要做時間點的恢復。閃回數據庫可以迅速將數據庫回到誤操作或人為錯誤的前一個時間點,如Word中的"撤消"操 作,可以不利用備份就快速的實現基于時間點的恢復。Oracle通過創建新的Flashback Logs(閃回日志),記錄數據庫的閃回操作。如果希望能閃回數據庫,需要設置如下參數:DB_RECOVER_FILE_DEST日志的存放位置, DB_RECOVER_FILE_DEST_SIZE恢復區的大小。在創建數據庫的時候,Oracle將自動創建恢復區,但默認是關閉的,需要執行 alter database flashback on命令。

      例:執行Flashback Database命令格式。

    SQL>flashback database to time to_timestamp(xxx);
    SQL>flashback database to scn xxx

      2、Flashback Table

      Oracle Flashback Table特性允許利用Flashback Table語句,確保閃回到表的前一個時間點。與Oracle 9i中的Flashback Query相似,利用回滾段信息來恢復一個或一些表到以前的一個時間點(一個快照)。要注意的是,Flashback Table不等于Flashback Query,Flashback Query僅僅是查詢以前的一個快照點而已,并不改變當前表的狀態,而Flashback Table將改變當前表及附屬對象一起回到以前的時間點。

      語法:

    flashback table tablename to timestamp xxx或
    flashback table tablename to scn xxx

      注意:如果需要閃回一個表,需要以下條件:

      ·需要有flashback any table的系統權限或者是該表的flashback對象權限;

      ·需要有該表的select,insert,delete,alter權限;

      ·必須保證該表row movement。

      例:執行將test表閃回到2005年5月7日下午3點。

    SQL>flashback table test to timestamp to_timestamp(’2005-05-07 15:00:00’,’yyyy-mm-dd hh24:mi:ss’);

      3、Flashback Drop

      Oracle Flashback Drop特性提供一個類似回收站的功能,用來恢復不小心被刪除的表。當刪除表時,Oracle 10g并不立刻釋放被刪除的表所占用的空間,而是將這個被刪除的表進行自動重命名(為了避免同類對象名稱的重復)并放進回收站中。所謂的回收站類似于
    Windows 系統中的回收站,是一個虛擬的容器,用于存放所有被刪除的對象,在回收站中被刪除的對象將占用創建時的同樣的空間。如果這個被刪除的表需要進行恢復,就可利用Flashback Drop功能。

      例:進行一個刪除表后恢復的簡單測試。

      (1)顯示回收站信息

    SQL>show recyclebin;

      可以看到,回收站中是沒有任何結果的,表示沒有任何表在回收站中。

      (2)創建一個表,并刪除,再次顯示回收站信息

    SQL>create table test_drop(name varchar2(10));
    SQL>drop table test_drop;
    SQL>show recyclebin;
    ORIGINAL NAME RECYCLEBIN NAME OBJECT TYPE DROP TIME
    TEST_DROP BIN$b+XkkO1RS5K10uKo9BfmuA==$0 TABLE 2005-05-07:14:30:47

      (3)對被刪除的表進行恢復

    SQL>flashback table test_drop to before drop;或
    SQL>flashback table "BIN$b+XkkO1RS5K10uKo9BfmuA==$0" to before drop;

      (4)管理回收站

      清除回收站中的單個表:purge table test_drop

      清除整個回收站:purge recyclebin

      清除不同的對象回收站:purge user_recyclebin或purge dba_recyclebin

      (5)確認刪除一個表

    SQL>drop table test_drop purge;

      如果刪除一個表且不放到回收站中不能進行恢復,在drop語句中可以利用purge選項。

      4、Flash Version Query

       Oracle Flashback Version Query特性,利用保存的回滾信息,可以看到特定的表在時間段內的任何修改,如電影的回放一樣,可以了解表在該期間的任何變化。Flashback version query一樣依賴于AUM,提供了一個查看行改變的功能,能找到所有已經提交了的行的記錄,分析出過去時間都執行了什么操作。Flashback version query采用VERSIONS BETWEEN語句來進行查詢,常用的方法:

      ·VERSIONS_SCN - 系統改變號

      ·VERSIONS_TIMESTAMP - 時間

       例如:在test表中,時間1插入一條記錄,時間2刪除了這條記錄,對于時間3執行select * from test當然查詢不到這條記錄,只能看到該表最后的提交記錄。這時如果利用Flash Table或者是Flash Query,只能看到過去的某一時間點的一個快照,而利用Flashback Version Query,能夠把時間1、時間2的操作給記錄下來,并詳細的查詢出對表進行的任何操作。

    SQL>select versions_starttime,versions_endtime, versions_xid,versions_operation,id
    from test versions
    between timestamp minvalue and maxvalue
    order by versions_starttime;

      在上述查詢中,列 versions_starttime、versions_endtime、versions_xid、versions_operation是偽列,還 有一些偽列,如versions_startscn和versions_endscn顯示了該時刻的系統更改號。列versions_xid顯示了更改該 行的事務標識符。

      當然,除了分析以上所有的變更之外,可以根據需要指定時間段,如顯示在2005-05-07時間在15:30到16:30之間test表的所有變更。

    SQL>select id from test
    versions between timestamp to_date(’2005-05-07 15:30:00’,’yyyy-mm-dd hh24:mi:ss’) and to_date(’2005-05-07 16:30:00’,’yyyy-mm-dd hh24:mi:ss’)

      5、Flashback Transaction Query

      Oracle Flashback Transaction Query特性確保檢查數據庫的任何改變在一個事務級別,可以利用此功能進行診斷
    問題 、 性能分析和審計事務。它其實是Flashback Version Query查詢的一個擴充,Flashback Version Query說明了可以審計一段時間內表的所有改變,但是也僅僅是能發現問題,對于錯誤的事務,沒有好的處理辦法。而Flashback Transaction Query提供了從FLASHBACK_TRANSACTION_QUERY視圖中獲得事務的歷史以及Undo_sql(回滾事務對應的sql語句),也 就是說審計一個事務到底做了什么,甚至可以回滾一個已經提交的事務。

      例:Flashback Transaction Query的操作實例。

      (1)在test表中刪除記錄,獲得事務的標識XID,然后提交。

    SQL>delete from test where id=2;
    SQL>select xid from v$transaction;
    XID
    ----------------
    04001200AE010000
    SQL>commit;

      在測試中方便起見,在事務沒有提交的時候,獲得事務的XID為04001F0035000000。實際情況下,不可能去跟蹤每個事務,想要獲得已提交事務的XID,就必須通過上面的Flashback Version Query。

      (2)進行Flashback Transaction Query

    SQL>select * from FLASHBACK_TRANSACTION_QUERY
    where xid=’04001F0035000000’;
    UNDO_SQL
    insert into "FLASHTEST"."TEST"("ID") values (’2’);

      注意:這個刪除語句對應的是1個Insert語句,如果想回滾這個事務,執行這個Insert語句即可。

       可以看到,Flashback Transaction Query主要用于審計一個事務,并可以回滾一個已經提交的事務。如果確定出錯的事務是最后一個事務,我們利用Flashback Table或者Flashback Query就可以解決問題。但是,如果執行了一個錯誤的事務之后,又執行了一系列正確的事務,那么上面的方法就無能為力,利用Flashback Transaction Query可以查看或回滾這個錯誤的事務。

      結束語

      通過上面的描述,可以看出閃回功能使用戶恢復偶然的錯誤刪除更加容易,增強了系統的可用性與讀一致性。
    posted @ 2006-07-13 10:26 Alex 閱讀(1355) | 評論 (0)編輯 收藏

    start the oem service:
    emctl?start?dbconsole

    then type following addrese in browser:

    http://yoursrv:1561/em


    it's convient.

    It's B/S model ,and it's C/S model at Oracle9i.


    posted @ 2006-07-11 16:18 Alex 閱讀(268) | 評論 (0)編輯 收藏

    key words: cookie 保留歷史記錄 登陸記錄

    很多時候用session覺得挺方便的,今天突然發現自己竟然幾乎沒用過cookie,呵呵,有點意思。正好碰到一個登陸頁面,需要用戶選擇站點類型,覺得每次都讓用戶選擇有點不合理,畢竟一個用戶常用的就一個,所以決定用cookie記錄下這個站點,下次登陸的時候可以直接顯示.

    效果如下:
    站點cookie

    ?/**
    ?????*?從cookie里讀取指定Name?對應的值
    ?????*?如果沒有返回空?null
    ?????*?
    @param?cookieName
    ?????*?
    @param?request
    ???? *
    @param decode :編碼
    ?????*?@return??String
    ?????
    */
    ????
    public?static?String?getCookieValue(String?cookieName,?HttpServletRequest?request,String?decode)?{
    ????????
    if(null?==?cookieName?||?cookieName.trim().length()?==0)?return?"";
    ????????Cookie?cookies[]?
    =?request.getCookies();
    ????????Cookie?sCookie?
    =?null;
    ????????String?sname?
    =?null;
    ????????String?svalue?
    =?null;
    ????????
    if?(null?!=?cookies?&&?cookies.length?>?0)?{
    ????????????
    for?(int?i?=?0;?i?<?cookies.length;?i++)?{
    ????????????????sCookie?
    =?cookies[i];
    ????????????????sname?
    =?sCookie.getName();
    ????????????????
    if?(cookieName.equals(sname))?{
    ????????????????????
    try?{
    ????????????????????????svalue?
    =?java.net.URLDecoder.decode(sCookie.getValue(),decode);
    ????????????????????}?
    catch?(UnsupportedEncodingException?e)?{
    ????????????????????????e.printStackTrace();??
    ????????????????????}
    ????????????????????
    break;
    ????????????????}
    ????????????}
    ????????}
    ????????
    return?svalue?;
    ????}

    ????
    /**
    ?????*?設置cookie
    ?????*?
    @param?cookieName
    ?????*?
    @param?cookieValue
    ?????*?
    @param?maxAge
    ?????*?
    @param?response
    ???? * @paramencode :編碼
    ?????*/
    ????
    public?static?void?setCookieValue(String?cookieName,String?cookieValue,
    int?maxAge,HttpServletResponse?response,String?encode)?{
    ????????
    if(null?==?cookieName?||?cookieName.trim().length()?==0)?return?;
    ????????Cookie?cookie?
    =?null;
    ????????
    try?{
    ????????????cookie?
    =?new?Cookie(cookieName,?java.net.URLEncoder.encode(cookieValue,encode));
    ????????}?
    catch?(UnsupportedEncodingException?e)?{
    ????????????e.printStackTrace();
    ????????}
    ????????cookie.setMaxAge(maxAge);
    ????????response.addCookie(cookie);
    ????}

    posted @ 2006-07-05 17:39 Alex 閱讀(568) | 評論 (1)編輯 收藏

    key words: 切割字符串 切割中文字符 DecimalFormat 格式化字符 科學計數法

    一.切割字符串的前幾個字符
    在首頁,有時候會因為table列表里的某個內容比較長而使得頁面撐得很難看,一般做法就是截取前幾個字符
    /**
    ?????*?截取前幾個字符串
    ?????*?
    @param?src??被截取的字符
    ?????*?
    @param?num??截取的長度
    ?????*?
    @param?append?附加的字符
    ?????*?
    @return?String
    ?????
    */
    ????
    public?static?String?splitStr(String?src,?int?num,?String?append)?{
    ????????
    if?(null?==?src?||?num?<?0)?return?"";
    ????????
    if?(src.length()?<?num)?return?src;
    ????????
    char[]?rtnChar?=?src.toCharArray();
    ????????StringBuffer?sb?
    =?new?StringBuffer();
    ????????
    for?(int?i?=?0;?i?<?num;?i++)?{
    ????????????sb.append(rtnChar[i]);
    ????????}
    ????????sb.append(append);
    ????????
    return?sb.toString();
    ????}

    toCharArray會把一個漢字當作一個char(java中一個char兩個字節)

    二.用DecimalFormat格式化字符
    這次用POI讀取Excel碰到的一個問題,即,如果Excel里的格式不同,比如同樣的20060623,有可能是字符型的格式,也可能是普通數字型的,而且在數字型的時候如果比較長會給你返回科學計數法的格式,如:2.002623E7,而這個不是我希望出現的,所以后來只好碰到這種格式的就自己給轉換一下:

    if?(null?!=?row.getCell((short)?i))?{
    ????????????????????
    switch?(row.getCell((short)?i).getCellType())?{
    ????????????????????????
    case?HSSFCell.CELL_TYPE_FORMULA?:
    ????????????????????????????strExcelLine[i]?
    =?"FORMULA?";
    ????????????????????????????
    break;
    ????????????????????????
    case?HSSFCell.CELL_TYPE_NUMERIC?:
    ????????????????????????????strExcelLine[i]?
    =?String.valueOf(row.getCell((short)?i).getNumericCellValue());
    ????????????????????????????
    break;
    ????????????????????????
    case?HSSFCell.CELL_TYPE_STRING?:
    ????????????????????????????strExcelLine[i]?
    =?row.getCell((short)?i).getStringCellValue();
    ????????????????????????????
    break;
    ????????????????????????
    case?HSSFCell.CELL_TYPE_BLANK?:
    ????????????????????????????strExcelLine[i]?
    =?"";
    ????????????????????????????
    break;
    ????????????????????????
    default?:
    ????????????????????????????strExcelLine[i]?
    =?"";
    ????????????????????????????
    break;
    ????????????????????}
    ????????????????????
    //如果讀取的是科學計數法的格式,則轉換為普通格式
    ????????????????????
    //added?by Alex at?20060626
    ????????????????????if(null?!=?strExcelLine[i]?&&
    ????????????????????????????strExcelLine[i].indexOf(
    ".")?!=?-1?&&
    ????????????????????????????strExcelLine[i].indexOf(
    "E")?!=?-1){
    ????????????????????????DecimalFormat?df?
    =?new?DecimalFormat();
    ????????????????????????strExcelLine[i]?
    =?df.parse(strExcelLine[i]).toString();
    ????????????????????}
    ????????????????}



    posted @ 2006-07-03 18:31 Alex 閱讀(930) | 評論 (0)編輯 收藏

    原文地址 :這里

    ?怎樣成為優秀的軟件模型設計者?

    ??????????????????? 來自:blog ??雪茶技術

    我們期待自己成為一個優秀的軟件模型設計者,但是,要怎樣做,又從哪里開始呢?

    將下列原則應用到你的軟件工程中,你會獲得立桿見影的成果。

    1. 人遠比技術重要

    你開發軟件是為了供別人使用,沒有人使用的軟件只是沒有意義的數據的集合而已。許多在軟件方面很有成就的行家在他們事業的初期卻表現平平,因為他們 那時侯將主要精力都集中在技術上。顯然,構件(components),EJB(Enterprise Java Beans)和代理(agent)是很有趣的東西。但是對于用戶來說,如果你設計的軟件很難使用或者不能滿足他們的需求,后臺用再好的技術也于事無補。多 花點時間到軟件需求和設計一個使用戶能很容易理解的界面上。

    2. 理解你要實現的東西

    好的軟件設計人員把大多數時間花費在建立系統模型上,偶爾寫一些源代碼,但那只不過是為了驗證設計過程中所遇到的問題。這將使他們的設計方案更加可行。

    3. 謙虛是必須的品格

    你不可能知道一切,你甚至要很努力才能獲得足夠用的知識。軟件開發是一項復雜而艱巨的工作,因為軟件開發所用到的工具和技術是在不斷更新的。而且, 一個人也不可能了解軟件開發的所有過程。在日常生活中你每天接觸到的新鮮事物可能不會太多。但是對于從事軟件開發的人來說,每天可以學習很多新東西(如果 愿意的話)。

    4. 需求就是需求

    如果你沒有任何需求,你就不要動手開發任何軟件。成功的軟件取決于時間(在用戶要求的時間內完成)、預算和是否滿足用戶的需求。如果你不能確切知道用戶需要的是什么,或者軟件的需求定義,那么你的工程注定會失敗。

    5. 需求其實很少改變,改變的是你對需求的理解

    Object ToolSmiths公司(www.objecttoolsmiths.com) 的Doug Smith常喜歡說:“分析是一門科學,設計是一門藝術”。他的意思是說在眾多的“正確”分析模型中只存在一個最“正確”分析模型可以完全滿足解決某個具 體問題的需要(我理解的意思是需求分析需要一絲不茍、精確的完成,而設計的時候反而可以發揮創造力和想象力 - 譯者注)。

    如果需求經常改動,很可能是你沒有作好需求分析,并不是需求真的改變了。

    你可以抱怨用戶不能告訴你他們想得到什么,但是不要忘記,收集需求信息是你工作。

    你可以說是新來的開發人員把事情搞得一團糟,但是,你應該確定在工程的第一天就告訴他們應該做什么和怎樣去做。

    如果你覺得公司不讓你與用戶充分接觸,那只能說明公司的管理層并不是真正支持你的項目。

    你可以抱怨公司有關軟件工程的管理制度不合理,但你必須了解大多同行公司是怎么做的。

    你可以借口說你們的競爭對手的成功是因為他們有了一個新的理念,但是為什么你沒先想到呢?

    需求真正改變的情況很少,但是沒有做好需求分析工作的理由卻很多。

    6. 經常閱讀

    在這個每日都在發生變化的產業中,你不可能在已取得的成就上陶醉太久。

    每個月至少讀2、3本專業雜志或者1本專業書籍。保持不落伍需要付出很多的時間和金錢,但會使你成為一個很有實力的競爭者。

    7. 降低軟件模塊間的耦合度

    高耦合度的系統是很難維護的。一處的修改引起另一處甚至更多處的變動。

    你可以通過以下方法降低程序的耦合度:隱藏實現細節,強制構件接口定義,不使用公用數據結構,不讓應用程序直接操作數據庫(我的經驗法則是:當應用程序員在寫SQL代碼的時候,你的程序的耦合度就已經很高了)。

    耦合度低的軟件可以很容易被重用、維護和擴充。

    8. 提高軟件的內聚性

    如果一個軟件的模塊只實現一個功能,那么該模塊具有高內聚性。高內聚性的軟件更容易維護和改進。

    判斷一個模塊是否有高的內聚性,看一看你是否能夠用一個簡單的句子描述它的功能就行了。如果你用了一段話或者你需要使用類似“和”、“或”等連詞,則說明你需要將該模塊細化。[注:only one]

    只有高內聚性的模塊才可能被重用。

    9. 考慮軟件的移植性

    移植是軟件開發中一項具體而又實際的工作,不要相信某些軟件工具的廣告宣傳(比如java 的宣傳口號write once run many ? 譯者注)。

    即使僅僅對軟件進行常規升級,也要把這看得和向另一個操作系統或數據庫移植一樣重要。

    記得從16位Windows移植到32位windows的“樂趣”嗎 ?當你使用了某個操作系統的特性,如它的進程間通信(IPC)策略,或用某數據庫專有語言寫了存儲過程。你的軟件和那個特定的產品結合度就已經很高了。

    好的軟件設計者把那些特有的實現細節打包隱藏起來,所以,當那些特性該變的時候,你的僅僅需要更新那個包就可以了。

    10. 接受變化

    這是一句老話了:唯一不變的只有變化。

    你應該將所有系統將可能發生的變化以及潛在需求記錄下來,以便將來能夠實現(參見“Architecting for Change”,Thinking Objectively, May 1999)

    通過在建模期間考慮這些假設的情況,你就有可能開發出足夠強壯且容易維護的軟件。設計強壯的軟件是你最基本的目標。

    11. 不要低估對軟件規模的需求

    Internet 帶給我們的最大的教訓是你必須在軟件開發的最初階段就考慮軟件規模的可擴充性。

    今天只有100人的部門使用的應用程序,明天可能會被有好幾萬人的組織使用,下月,通過因特網可能會有幾百萬人使用它。

    在軟件設計的初期,根據在用例模型中定義的必須支持的基本事務處理,確定軟件的基本功能。然后,在建造系統的時候再逐步加入比較常用的功能。

    在設計的開始考慮軟件的規模需求,避免在用戶群突然增大的情況下,重寫軟件。

    12. 性能僅僅是很多設計因素之一

    關注軟件設計中的一個重要因素--性能,這好象也是用戶最關心的事情。一個性能不佳的軟件將不可避免被重寫。

    但是你的設計還必須具有可靠性,可用性,便攜性和可擴展性。你應該在工程開始就應該定義并區分好這些因素,以便在工作中恰當使用。性能可以是,也可以不是優先級最高的因素,我的觀點是,給每個設計因素應有的考慮。

    13. 管理接口

    “UML User Guide”(Grady Booch,Ivar Jacobson和Jim Rumbaugh ,Addison Wesley, 1999)中指出,你應該在開發階段的早期就定義軟件模塊之間的接口。

    這有助于你的開發人員全面理解軟件的設計結構并取得一致意見,讓各模塊開發小組相對獨立的工作。一旦模塊的接口確定之后,模塊怎樣實現就不是很重要了。

    從根本上說,如果你不能夠定義你的模塊“從外部看上去會是什么樣子”,你肯定也不清楚模塊內要實現什么。

    14. 走近路需要更長的時間

    在軟件開發中沒有捷徑可以走。

    縮短你的在需求分析上花的時間,結果只能是開發出來的軟件不能滿足用戶的需求,必須被重寫。

    在軟件建模上每節省一周,在將來的編碼階段可能會多花幾周時間,因為你在全面思考之前就動手寫程序。

    你為了節省一天的測試時間而漏掉了一個bug,在將來的維護階段,可能需要花幾周甚至幾個月的時間去修復。與其如此,還不如重新安排一下項目計劃。

    避免走捷徑,只做一次但要做對(do it once by doing it right)。

    15. 別信賴任何人

    產品和服務銷售公司不是你的朋友,你的大部分員工和高層管理人員也不是。

    大部分產品供應商希望把你牢牢綁在他們的產品上,可能是操作系統,數據庫或者某個開發工具。

    大部分的顧問和承包商只關心你的錢并不是你的工程(停止向他們付款,看一看他們會在周圍呆多長時間)。

    大部分程序員認為他們自己比其他人更優秀,他們可能拋棄你設計的模型而用自己認為更好的

    只有良好的溝通才能解決這些問題

    要明確的是,不要只依靠一家產品或服務提供商,即使你的公司(或組織)已經在建模、文檔和過程等方面向那個公司投入了很多錢。

    16. 證明你的設計在實踐中可行

    在設計的時候應當先建立一個技術原型, 或者稱為“端到端”原型。以證明你的設計是能夠工作的。

    你應該在開發工作的早期做這些事情,因為,如果軟件的設計方案是不可行的,在編碼實現階段無論采取什么措施都于事無補。技術原型將證明你的設計的可行性,從而,你的設計將更容易獲得支持。

    17. 應用已知的模式

    目前,我們有大量現成的分析和設計模式以及問題的解決方案可以使用。

    一般來說,好的模型設計和開發人員,都會避免重新設計已經成熟的并被廣泛應用的東西。
    http://www.ambysoft.com/processPatternsPage.html 收藏了許多開發模式的信息。

    18. 研究每個模型的長處和弱點

    目前有很多種類的模型可以使用,如下圖所示。用例捕獲的是系統行為需求,數據模型則描述支持一個系統運行所需要的數據構成。你可能會試圖在用例中加 入實際數據描述,但是,這對開發者不是非常有用。同樣,數據模型對描述軟件需求來說是無用的。每個模型在你建模過程中有其相應的位置,但是,你需要明白在 什么地方,什么時候使用它們。

    19. 在現有任務中應用多個模型

    當你收集需求的時候,考慮使用用例模型,用戶界面模型和領域級的類模型。

    當你設計軟件的時候,應該考慮制作類模型,順序圖、狀態圖、協作圖和最終的軟件實際物理模型。

    程序設計人員應該慢慢意識到,僅僅使用一個模型而實現的軟件要么不能夠很好地滿足用戶的需求,要么很難擴展。

    20. 教育你的聽眾

    你花了很大力氣建立一個很成熟的系統模型,而你的聽眾卻不能理解它們,甚至更糟-連為什么要先建立模型都不知道。那么你的工作是毫無意義的。

    教給你開發人員基本的建模知識;否則,他們會只看看你畫的漂亮圖表,然后繼續編寫不規范的程序。

    另外, 你還需要告訴你的用戶一些需求建模的基礎知識。給他們解釋你的用例(uses case)和用戶界面模型,以使他們能夠明白你要表達地東西。當每個人都能使用一個通用的設計語言的時候(比如UML-譯者注),你的團隊才能實現真正的合作。

    21. 帶工具的傻瓜還是傻瓜

    你給我CAD/CAM工具,請我設計一座橋。但是,如果那座橋建成的話,我肯定不想當第一個從橋上過的人,因為我對建筑一竅不通。

    使用一個很優秀的CASE工具并不能使你成為一個建模專家,只能使你成為一個優秀CASE工具的使用者。成為一個優秀的建模專家需要多年的積累,不 會是一周針對某個價值幾千美元工具的培訓。一個優秀的CASE工具是很重要,但你必須學習使用它,并能夠使用它設計它支持的模型。

    22. 理解完整的過程

    好的設計人員應該理解整個軟件過程,盡管他們可能不是精通全部實現細節。

    軟件開發是一個很復雜的過程,還記得《object-oriented software process》第36頁的內容嗎?除了編程、建模、測試等你擅長工作外,還有很多工作要做。

    好的設計者需要考慮全局。必須從長遠考慮如何使軟件滿足用戶需要,如何提供維護和技術支持等。

    23. 常做測試,早做測試

    如果測試對你的軟件來說是無所謂的,那么你的軟件多半也沒什么必要被開發出來。

    建立一個技術原型供技術評審使用,以檢驗你的軟件模型。

    在軟件生命周期中,越晚發現的錯誤越難修改,修改成本越昂貴。盡可能早的做測試是很值得的。

    24. 把你的工作歸檔

    不值得歸檔的工作往往也不值得做。歸檔你的設想,以及根據設想做出的決定;歸檔軟件模型中很重要但不很明顯的部分。 給每個模型一些概要描述以使別人很快明白模型所表達的內容

    25. 技術會變,基本原理不會

    如果有人說“使用某種開發語言、某個工具或某某技術,我們就不需要再做需求分析,建模,編碼或測試”。不要相信,這只說明他還缺乏經驗。拋開技術和 人的因素,實際上軟件開發的基本原理自20世紀70年代以來就沒有改變過。你必須還定義需求,建模,編碼,測試,配置,面對風險,發布產品,管理工作人員 等等。

    軟件建模技術是需要多年的實際工作才能完全掌握的。好在你可以從我的建議開始,完善你們自己的軟件開發經驗。

    以雞湯開始,加入自己的蔬菜。然后,開始享受你自己的豐盛晚餐吧。

    posted @ 2006-06-20 14:28 Alex 閱讀(415) | 評論 (1)編輯 收藏

    mark下資源,有空再來慢慢研究。

    Prototype.js--Javascript編寫者的小軍刀


    posted @ 2006-06-18 22:22 Alex 閱讀(323) | 評論 (0)編輯 收藏

    key words: bash編程

    轉自 這里

    另一篇 詳解Bash命令行處理

    bash編程教學實例

    bash編程

    --------------------------------------------------------------------------------
    時間:2004/03/02    來源:不詳

      Shell Script(bash)簡介

      眾所皆知地,UNIX上以小工具著名,利用許多簡單的小工具,來完成原本需要大量軟體開發的工作,這一點特色,使得UNIX成為許多人心目中理想的系統平臺。 
      在眾多的小工具中,Shell Script算得上是最基本、最強大、運用最廣泛的一個。它運用圍之廣,不但從系統啟動、程式編譯、定期作業、上網連線,甚至安裝整個Linux系統,都可以用它來完成。 

      因為Shell Script是利用您平日在使用的一些指令,將之組合起來,成為一個"程式"。如果您平日某些序列的指令下得特別頻繁,便可以將這些指令組合起來,成為另 一個新的指令。這樣,不但可以簡化并加速操作速度,甚至還可以干脆自動定期執行,大大簡化系統管理工作。 

      *************************
      Bash(GNU Bourne-Again SHell)是許多Linux平臺的內定Shell,事實上,還有許多傳統UNIX上用的Shell,像tcsh、csh、ash、bsh、ksh等等, Shell Script大致都類同,當您學會一種Shell以后,其它的Shell會很快就上手,大多數的時候,一個Shell Script通常可以在很多種Shell上使用。 
      這里我介紹您bash的使用方法。事實上,當您"man bash"時,就可以看到bash的說明書,不過對許多人來說,這份說明書猶如"無字天書"一樣難懂。這份文件,主要資料來源為"man bash",我加上一些實際日常的應用例來說明。希望這樣能讓那些始終不得其門而入的人們,多多少少能有點概念。 


      教學例子

      "Hello world" Shell Script 
      照傳統程式教學例,這一節介紹Shell Script的"Hello World"如何撰寫。 

      *************************

      #!/bin/sh 
      # Filename : hello 
      echo "Hello world!" 

      *************************

      大家應該會注意到第一行的"#!/bin/sh"。在UNIX下,所有的可執行Script,不管是那一種語言,其開頭都是"#!",例如Perl是 "#!/usr/bin/perl",tcl/tk是"#!/usr/bin/wish",看您要執行的Script程式位置在那里。您也可以用"#! /bin/bash"、"#!/bin/tcsh"等等,來指定使用特定的Shell。 
      echo是個bash的內建指令。 

      *************************

      接下來,執行hello這個script: 
      要執行一個Script的方式有很多種。 

      *************************

      第一種 : 將hello這個檔案的權限設定為可執行。 
      [foxman@foxman bash]# chmod 755 hello 
      執行 
      [foxman@foxman bash]# ./hello 
      hello world 

      *************************

      第二種 : 使用bash內建指令"source"或"."。 
      [foxman@foxman bash]# source hello 
      hello world 
      或 
      [foxman@foxman bash]# . hello 
      hello world 

      *************************

      第三種 : 直接使用sh/bash/tcsh指令來執行。 
      [foxman@foxman bash]# sh hello 
      hello world 
      或 
      [foxman@foxman bash]# bash hello 
      hello world 

      *************************

      Bash執行選項 

      *************************

      -c string : 讀取string來當命令。 
      -i : 互動介面。 
      -s : 由stdin讀取命令。 
      - : 取消往后選項的讀取。 
      -norc : 不要讀~/.bashrc來執行。 
      -noprofile : 不要讀/etc/profile、~/.bash_profile、~/.bash_login、~/.profile等等來執行。 
      -rcfile filename : 執行filename,而非~/.bashrc 
      -version : 顯示版本。 
      -quiet : 啟動時不要哩唆。 
      -login : 確保bash是個login shell。 
      -nobraceexpansion : 不要用curly brace expansion({}符號展開)。 
      -nolineediting : 不用readline來讀取命令列。 
      -posix : 改采Posix 1003.2標準。 


      用于自動備份的Shell Script


      一個用于自動備份的Shell Script
      我們先前提到,可利用Shell Script搭配crond來作定期的工作。要作定期性的工作,在UNIX上,就是與crond的搭配運用。 

      *************************

      首先我們先來研究如何對系統進行備份。 
      要對系統進行備份,不外乎便是利用一些壓縮工具。在許多UNIX系統上,tar及gzip是de facto的資料交換標準。我們經常可以看見一些tar.gz或tgz檔,這些檔案,被稱為tarball。當然了,您也可以用bzip2、zip等等壓 縮工具來進行壓縮,不必限定于gzip。但tar配合gzip是最普遍的,也是最方便的方式。 

      要將我們想要的資料壓縮起來,進行備份,可以結合tar及gzip一起進行。方式有很多種,最常用的指令是以下這一種: 

      tar -c file/dir ... | gzip -9 > xxxx.tar.gz 

      您也可以分開來做: 

      tar -r file/dir ... -f xxxx.tar 
      gzip -9 xxxx.tar 

      或 

      tar -r file/dir ... -f xxxx.tar 
      gzip -9 < xxxx.tar > xxxx.tar.gz 

      *************************

      在解過Linux下檔案備份的基本知識后,我們來寫一個將檔案備份的Script。 
      #!/bin/sh 
      # Filename : backup 

      DIRS="/etc /var /your_directories_or_files" 
      BACKUP="/tmp/backup.tgz" 

      tar -c $DIRS | gzip -9 > $BACKUP 

      其中DIRS放的是您要備份的檔案及目錄,BACKUP是您的備份檔。可不要將/tmp放進DIRS中,那樣做,您是在做備份的備份,可能將您的硬碟塞爆。 


      *************************

      接下來測試 
      [foxman@foxman bash]# chmod 755 backup 
      [foxman@foxman bash]# ./backup 

      執行完成后在/tmp就會有一個backup.tgz,里面儲存了您重要的資料。您可用 

      gzip -dc /tmp/backup.tgz | tar -vt 
      或 
      tar vtfz /tmp/backup.tgz 

      來看看里面的檔案列表。 

      要解開時,可用以下指令來完成復原: 

      gzip -dc /tmp/backup.tgz | tar -xv 
      或 
      tar xvfz /tmp/backup.tgz 

      備份通常是僅備份系統通常最重要的部份,/etc可說是不可缺少的一部份。另外,看您系統中有那些重要的資料需要備份。通常來說,您沒有必要備份 /bin、/sbin、/usr/bin、/usr/sbin、/usr/X11R6/bin等等這些執行檔目錄。只要備份您重要的檔案即可,別把整個硬 碟備份,那是蠻呆的動作。 

      *************************

      如果您有許多臺機器,可利用其中一臺任務較輕的內部網路主機,做為主要備份主機。將所有機器都自動執行備份,然后利用NFS/Coda/Samba等網路檔案系統,將備份的資料放到該備份機器中,該機器則定時收取備份資料,然后您再由該機器中進行一次備份。 
      這里是整個系統備份方案的圖示。 

      在您進行之前,先解一下,系統中那些是要備份的,那些是不需要的。 

      *************************

      新的backup
      #!/bin/sh 
      HOSTNAME=`hostname` 
      DIRS="/etc /var /your_important_directory" 
      BACKUP="/tmp/$HOSTNAME.tgz" 
      NFS="/mnt/nfs" 

      tar -c $DIRS | gzip -9 > $BACKUP 
      mv -f $BACKUP $NFS 


      *************************

      備份主機內的Script : collect_backup
      #!/bin/sh 
      NFS="/mnt/nfs" 
      BACKUP="/backup" 

      mv -f $NFS/*.tgz $BACKUP 


      在此,您不能夠將所有備份都直接放在/mnt/nfs,這是危險的。萬一任一臺機器不小心將/mnt/nfs所有內容刪除,那么備份就會消失。因此,您需要將/mnt/nfs移到一個只有該備份主機可存取的目錄中。 


      *************************

      當這些個別的Script都測試好以后,接下來我們將他們放到crontab里面。找到您的crontab,它的位置可能在/var/spool/cron/crontabs/root、/etc/crontab、/var/cron/tabs/root。 
      在crontab中選擇以下之一加入(看您定期的時間): 

      Slackware : /var/spool/cron/crontabs/root
      01 * * * * /full_backup_script_path/backup 1> /dev/null 2> /dev/null # 每小時(太過火一點) 
      30 16 * * * /full_backup_script_path/backup 1> /dev/null 2> /dev/null # 每日16:30,下班前備份 
      30 16 * * 0 /full_backup_script_path/backup 1> /dev/null 2> /dev/null # 每周一16:30 
      0 5 1 * * /full_backup_script_path/backup 1> /dev/null 2> /dev/null # 每月一號5:0 
      RedHat/Debian : /etc/crontab
      RedHat可直接將backup放入/etc/cron.hourly, /etc/cron.daily, /etc/cron.weekly, /etc/cron.monthly。或采用如上加入/etc/crontab的方式: 
      有關crontab的用法,可查"man 5 crontab",在此不詳述。 

      備份主機的設定類同。 

      注意: 所有機器不要同時進行備份,否則網路會大塞車。備份主機收取備份的時間要設為最后,否則會收不到備份資料。您可以在實作后,將時間間隔調整一下。 


      *************************

      看看,兩個小小不到三行的Shell Script,配合cron這個定時工具。可以讓原本需要耗時多個小時的人工備份工作,簡化到不到十分鐘。善用您的想像力,多加一點變化,可你讓您的生活變得輕松異常,快樂悠哉。


      檔案系統檢查

      系統安全一向是大多數電腦用戶關心的事,在UNIX系統中,最重視的事,即系統中有沒有"木馬"(Trojan horse)。不管Trojan horse如何放進來的,有一點始終會不變,即被放置木馬的檔案,其檔案日期一定會被改變,甚至會有其它的狀態改變。此外,許多狀況下,系統會多出一些不 知名的檔案。因此,平日檢查整個檔案系統的狀態是否有被改變,將所有狀態有改變的檔案,以及目前有那些程式正在執行,自動報告給系統管理員,是個避免坐上 "木馬"的良方。 

      *************************

      #!/bin/sh 
      # Filename : whatever_you_name_it 
      DIRS="/etc /home /bin /sbin /usr/bin /usr/sbin /usr/local /var /your_directory" 
      ADMIN="email@your.domain.com" 
      FROM="admin@your.domain.com" 
      # 寫入Sendmail的標頭 
      echo "Subject: $HOSTNAME filesystem check" > /tmp/today.mail 
      echo "From: $FROM" >> /tmp/today.mail 
      echo "To: $ADMIN" >> /tmp/today.mail 
      echo "This is filesystem report comes from $HOSTNAME" >> /tmp/today.mail 
      # 報告目前正在執行的程式 
      ps axf >> /tmp/today.mail 
      # 檔案系統檢查 
      echo "File System Check" >> /tmp/today.mail 
      ls -alR $DIRS | gzip -9 > /tmp/today.gz 
      zdiff /tmp/today.gz /tmp/yesterday.gz >> /tmp/today.mail 
      mv -f /tmp/today.gz /tmp/yesterday.gz 
      # 寄出信件 
      sendmail -t < /tmp/today.mail 

      然后把它放到一個不顯眼的地方去,讓別人找不到。 

      把它加入crontab中。 

      30 7 * * * /full_check_script_path/whatever_you_name_it 1> /dev/null 2> /dev/null #上班前檢查 

      有些檔案是固定會更動的,像/var/log/messages、/var/log/syslog、/dev/ttyX等等,不要太大驚小怪。


      控制圈for

      演示了幾個簡單的Shell Script,相信您應該對Shell Script有點概念了。現在我們開始來仔細研究一些較高等的Shell Script寫作。一些進一步的說明,例如"$"、">"、"<"、">>"、"1>"、"2>"符號的使用,會在 稍后解釋。 

      *************************

      for name [ in word; ] do list ; done
      控制圈。 
      word是一序列的字,for會將word中的個別字展開,然后設定到name上面。list是一序列的工作。如果[in word;]省略掉,那么name將會被設定為Script后面所加的參數。 


      *************************

      例一: 
      #!/bin/sh 

      for i in a b c d e f ; do 
      echo $i 
      done 

      它將會顯示出a到f。 


      *************************

      例二: 另一種用法,A-Z
      #!/bin/sh 
      WORD="a b c d e f g h i j l m n o p q r s t u v w x y z" 

      for i in $WORD ; do 
      echo $i 
      done 

      這個Script將會顯示a到z。 


      *************************

      例三 : 修改副檔名
      如果您有許多的.txt檔想要改名成.doc檔,您不需要一個一個來。 
      #!/bin/sh 

      FILES=`ls /txt/*.txt` 

      for txt in $FILES ; do 
      doc=`echo $txt | sed "s/.txt/.doc/"` 
      mv $txt $doc 
      done 

      這樣可以將*.txt檔修改成*.doc檔。 


      *************************

      例四 : meow
      #!/bin/sh 
      # Filename : meow 
      for i ; do 
      cat $i 
      done 

      當您輸入"meow file1 file2 ..."時,其作用就跟"cat file1 file2 ..."一樣。 


      *************************

      例五 : listbin 
      #!/bin/sh 
      # Filename : listbin 

      for i in /bin/* ; do 
      echo $i 
      done 

      當您輸入"listbin"時,其作用就跟"ls /bin/*"一樣。 


      *************************

      例六 : /etc/rc.d/rc 
      拿一個實際的例來說,Red Hat的/etc/rc.d/rc的啟動程式中的一個片斷。 

      for i in /etc/rc.d/rc$runlevel.d/S*; do 
      # Check if the script is there. 
      [ ! -f $i ] && continue 

      # Check if the subsystem is already up. 
      subsys=${i#/etc/rc.d/rc$runlevel.d/S??} 
      [ -f /var/lock/subsys/$subsys ] || \ 
      [ -f /var/lock/subsys/${subsys}.init ] && continue 

      # Bring the subsystem up. 
      $i start 
      done 

      這個例中,它找出/etc/rc.d/rcX.d/S*所有檔案,檢查它是否存在,然后一一執行。 


      流程控制case

      case word in [ pattern [ | pattern ] ... ) list ;; ] ... esac
      case/esac的標準用法大致如下: 
    case $arg in 
      pattern | sample) # arg in pattern or sample 
      ;; 
      pattern1) # arg in pattern1 
      ;; 
      *) #default 
      ;; 
    esac 
      arg是您所引入的參數,如果arg內容符合pattern項目的話,那么便會執行pattern以下的程式碼,而該段程式碼則以兩個分號";;"做結尾。 

      可以注意到"case"及"esac"是對稱的,如果記不起來的話,把"case"顛倒過來即可。 


    *************************

      例一 : paranoia
    #!/bin/sh 
    case $1 in 
        start | begin) 
         echo "start something" 
        ;; 
        stop | end) 
         echo "stop something" 
        ;; 
        *) 
         echo "Ignorant" 
        ;; 
    esac 

      執行
      [foxman@foxman bash]# chmod 755 paranoia 
      [foxman@foxman bash]# ./paranoia 
      Ignorant 
      [foxman@foxman bash]# ./paranoia start 
      start something 
      [foxman@foxman bash]# ./paranoia begin 
      start something 
      [foxman@foxman bash]# ./paranoia stop 
      stop something 
      [foxman@foxman bash]# ./paranoia end 
      stop something 

    *************************

      例二 : inetpanel
      許多的daemon都會附上一個管理用的Shell Script,像BIND就附上ndc,Apache就附上apachectl。這些管理程式都是用shell script來寫的,以下示一個管理inetd的shell script。 
    #!/bin/sh 

    case $1 in 
      start | begin | commence) 
        /usr/sbin/inetd 
      ;; 
      stop | end | destroy) 
        killall inetd 
      ;; 
      restart | again) 
        killall -HUP inetd 
      ;; 
      *) 
        echo "usage: inetpanel [start | begin | commence | stop | end | destory | restart | again]" 
      ;; 
    esac 


    *************************

      例三 : 判斷系統
      有時候,您所寫的Script可能會跨越好幾種平臺,如Linux、FreeBSD、Solaris等等,而各平臺之間,多多少少都有不同之處,有時候需要判斷目前正在那一種平臺上執行。此時,我們可以利用uname來找出系統資訊。 
    #!/bin/sh 

    SYSTEM=`uname -s` 

    case $SYSTEM in 
      Linux) 
        echo "My system is Linux" 
        echo "Do Linux stuff here..." 
      ;; 
      FreeBSD) 
        echo "My system is FreeBSD" 
        echo "Do FreeBSD stuff here..." 
      ;; 
      *) 
        echo "Unknown system : $SYSTEM" 
        echo "I don't what to do..." 
      ;; 
    esac 


      流程控制select

      select name [ in word; ] do list ; done
      select顧名思義就是在word中選擇一項。與for相同,如果[in word;]省略,將會使用Script后面所加的參數。 
      例:
    #!/bin/sh 
    WORD="a b c" 

    select i in $WORD ; do 
     case $i in 
      a) 
       echo "I am A" 
      ;; 
      b) 
       echo "I am B" 
      ;; 
      c) 
       echo "I am C" 
      ;; 
      *) 
       break; 
      ;; 
     esac 
    done 

      執行結果
      [foxman@foxman bash]# ./select_demo 
    1) a 
    2) b 
    3) c 
    #? 1 
    I am A 
    1) a 
    2) b 
    3) c 
    #? 2 
    I am B 
    1) a 
    2) b 
    3) c 
    #? 3 
    I am C 
    1) a 
    2) b 
    3) c 
    #? 4 


      返回狀態Exit

      在繼續下去之前,我們必須要切入另一個話題,即返回狀態值 - Exit Status。因為if/while/until都遷涉到了使用Exit Status來控制程式流程的問題。 

      *************************

      許多人都知道,在許多語言中(C/C++/Perl....),都有一個exit的函數,甚至連Bash自己都有個exit的內建命令。而exit后面所帶的數字,便是返回狀態值 - Exit Status。 
      返回狀態值可以使得程式與程式之間,利用Shell script來結合的可能性大增,利用小程式,透過Shell script,來完成很雜的工作。 

      在shell中,返回值為零表示成功(True),非零值為失敗(False)。 


      *************************

      舉例來說,以下這個兩個小程式yes/no分別會返回0/1(成功/失敗): 
      /* yes.c */ 
      void main(void) { exit(0); } 
      /* no.c */ 
      void main(void) { exit(1); } 
      那么以下這個"YES"的shell script便會顯示"YES"。 
      #!/bin/sh 
      # YES 
      if yes ; then 
      echo "YES" 
      fi 
      而"NO"不會顯示任何東西。 
      #!/bin/sh 
      # NO 
      if no ; then 
      echo "YES" 
      fi 

      *************************

      test express 
      [ express ] 
      在Shell script中,test express/[ express ]這個語法被大量地使用,它是個非常實用的指令。由于它的返回值即Exit Status,經常被運用在if/while/until的場合中。而在后面,我們也會大量運用到,在進入介紹if/while/until之前,有必要 先解一下。 

      其返回值為0(True)或1(False),要看表述(express)的結果為何。 

      express格式 

      -b file : 當檔案存在并且屬性是Block special(通常是/dev/xxx)時,返回True。 
      -c file : 當檔案存在并且屬性是character special(通常是/dev/xxx)時,返回True。 
      -d file : 當檔案存在并且屬性是目錄時,返回True。 
      -e file : 當檔案存在時,返回True。 
      -f file : 當檔案存在并且是正常檔案時,返回True。 
      -g file : 當檔案存在并且是set-group-id時,返回True。 
      -k file : 當檔案存在并且有"sticky" bit被設定時,返回True。 
      -L file : 當檔案存在并且是symbolic link時,返回True。 
      -p file : 當檔案存在并且是name pipe時,返回True。 
      -r file : 當檔案存在并且可讀取時,返回True。 
      -s file : 當檔案存在并且檔案大小大于零時,返回True。 
      -S file : 當檔案存在并且是socket時,返回True。 
      -t fd : 當fd被開啟為terminal時,返回True。 
      -u file : 當檔案存在并且set-user-id bit被設定時,返回True。 
      -w file : 當檔案存在并且可寫入時,返回True。 
      -x file : 當檔案存在并且可執行時,返回True。 
      -O file : 當檔案存在并且是被執行的user id所擁有時,返回True。 
      -G file : 當檔案存在并且是被執行的group id所擁有時,返回True。 
      file1 -nt file2 : 當file1比file2新時(根據修改時間),返回True。 
      file1 -ot file2 : 當file1比file2舊時(根據修改時間),返回True。 
      file1 -ef file2 : 當file1與file2有相同的device及inode number時,返回True。 
      -z string : 當string的長度為零時,返回True。 
      -n string : 當string的長度不為零時,返回True。 
      string1 = string2 : string1與string2相等時,返回True。 
      string1 != string2 : string1與string2不相等時,返回True。 
      ! express : express為False時,返回True。 
      expr1 -a expr2 : expr1及expr2為True。 
      expr1 -o expr2 : expr1或expr2其中之一為True。 
      arg1 OP arg2 : OP是-eq[equal]、-ne[not-equal]、-lt[less-than]、-le[less-than-or-equal]、-gt [greater-than]、-ge[greater-than-or-equal]的其中之一。 



      *************************

      在Bash中,當錯誤發生在致命信號時,bash會返回128+signal number做為返回值。如果找不到命令,將會返回127。如果命令找到了,但該命令是不可執行的,將返回126。除此以外,Bash本身會返回最后一個 指令的返回值。若是執行中發生錯誤,將會返回一個非零的值。 
      Fatal Signal : 128 + signo 
      Can't not find command : 127 
      Can't not execute : 126 
      Shell script successfully executed : return the last command exit status 
      Fatal during execution : return non-zero

      流程控制if

      if list then list [ elif list then list ] ... [ else list ] fi
      幾種可能的寫法 

    *************************

    第一種 
    if list then 
     do something here 
    fi 
    當list表述返回值為True(0)時,將會執行"do something here"。 

    例一 : 當我們要執行一個命令或程式之前,有時候需要檢查該命令是否存在,然后才執行。 
    if [ -x /sbin/quotaon ] ; then 
      echo "Turning on Quota for root filesystem" 
      /sbin/quotaon / 
    fi 

    例二 : 當我們將某個檔案做為設定檔時,可先檢查是否存在,然后將該檔案設定值載入。 
    # Filename : /etc/ppp/settings 
    PHONE=1-800-COLLECT 

    #!/bin/sh 
    # Filename : phonebill 
    if [ -f /etc/ppp/settings ] ; then 
      source /etc/ppp/settings 
      echo $PHONE 
    fi 
    執行 
    [foxman@foxman ppp]# ./phonebill 
    1-800-COLLECT 


    *************************

    第二種 
    if list then 
     do something here 
    else 
     do something else here 
    fi 
    例三 : Hostname 
    #!/bin/sh 
    if [ -f /etc/HOSTNAME ] ; then 
      HOSTNAME=`cat /etc/HOSTNAME` 
    else 
      HOSTNAME=localhost 
    fi 


    *************************

    第三種 
    if list then 
     do something here 
    elif list then 
     do another thing here 
    fi 
    例四 : 如果某個設定檔允許有好幾個位置的話,例如crontab,可利用if then elif fi來找尋。 
    #!/bin/sh 

    if [ -f /etc/crontab ] ; then 
      CRONTAB="/etc/crontab" 
    elif [ -f /var/spool/cron/crontabs/root ] ; then 
      CRONTAB="/var/spool/cron/crontabs/root" 
    elif [ -f /var/cron/tabs/root ] ; then 
      CRONTAB="/var/cron/tabs/root" 
    fi 
    export CRONTAB 


    *************************

    第四種 
    if list then 
     do something here 
    elif list then 
     do another thing here 
    else 
     do something else here 
    fi 
    例五 : 我們可利用uname來判斷目前系統,并分別做各系統狀況不同的事。 
    #!/bin/sh 

    SYSTEM=`uname -s` 

    if [ $SYSTEM = "Linux" ] ; then 
     echo "Linux" 
    elif [ $SYSTEM = "FreeBSD" ] ; then 
     echo "FreeBSD" 
    elif [ $SYSTEM = "Solaris" ] ; then 
     echo "Solaris" 
    else 
     echo "What?" 
    fi 

    控制圈while/until

    while list do list done
    當list為True時,該圈會不停地執行。 
    例一 : 無限回圈寫法 
    #!/bin/sh 

    while : ; do 
     echo "do something forever here" 
     sleep 5 
    done 

    例二 : 強迫把pppd殺掉。 
    #!/bin/sh 

    while [ -f /var/run/ppp0.pid ] ; do 
      killall pppd 
    done 


    *************************

    until list do list done
    當list為False(non-zero)時,該圈會不停地執行。 
    例一 : 等待pppd上線。 
    #!/bin/sh 
    until [ -f /var/run/ppp0.pid ] ; do 
      sleep 1 
    done 


      參數與變數

      在繼續下去介紹function之前,我們必須停下來介紹"參數與變數"。 

      *************************

      參數(Parameters)是用來儲存"值"的資料型態,有點像是一般語言中的變數。它可以是個名稱(name)、數字(number)、或者是以下所列出來一些特殊符號(Special Parameters)。 
      在shell中,變數是由name形式的參數所構成的。 


      *************************

      在前面的許多例中,我們事實上已經看到許多參數的運用。要設定一個Parameter實際很簡單: 
      name=value 

      例如說: 

      MYHOST="foxman" 

      而要使用它時,則是加個"$"符號。 

      echo $MYHOST 

      *************************

      位置參數(Positional Parameters) 

      *************************

      所謂的位置參數便是0,1,2,3,4,5,6,7,8,9...。使用時,用$0,$1,$2...。 
      位置參數是當script被載入時,后面所附加的參數。$0是本身,$1則為第一個參數,$2為第二個,依此類推。而當Positional Parameters被function所使用時,它們會被暫時取代(下一節會介紹function)。 

      例如以下這個script: 
      #!/bin/sh 
      # Filename : position 
      echo $0 
      echo $1 

      執行時: 
      [foxman@foxman bash]# ./position abc 
      ./position 
      abc 

      當位置參數超過兩位數時,有特別的方法來展開,稱為Expansion。 

      *************************

      特殊參數(Speical Parameters) 
      這些符號,非常不人性,對新手來說很困擾。但上手后,會覺得方便無比,有些如果您看不懂的話,就--算了,不用浪費太多時間在上面。 

      *************************

      * 星號 
      將Positional Parameters合成一個參數,其間隔為IFS內定參數的第一個字元(見內建變數一節)。 
      例: 
      #!/bin/sh 
      # starsig 
      echo $* 

      執行: 
      [foxman@foxman bash]# starsig a b c d e f g 
      a b c d e f g 

      *************************

      @ at符號 
      與*星號類同。不同之處在于不參照IFS。 

      例: 
      #!/bin/sh 
      # atsig 
      echo $@ 

      執行: 
      [foxman@foxman bash]# atsig a b c d e f g 
      a b c d e f g 


      *************************

      # 井字號 
      展開Positional parameters的數量。 

      例: 
      #!/bin/sh 
      # poundsig 
      echo $# 

      執行 
      [foxman@foxman bash]# poundsig a b c d e f g 
      7 

      *************************

      ? 問號 
      最近執行的foreground pipeline的狀態。 


      *************************

      - 減號 
      最近執行的foreground pipeline的選項參數。 

      *************************

      $ 錢錢錢 
      本身的Process ID。 

      [foxman@foxman bash]# ps ax | grep bash 
      1635 p1 S  0:00 /bin/bash 

      [foxman@foxman bash]# echo $$ 
      1635 

      *************************

      ! 驚號 
      最近執行背景命令的Process ID。 

      *************************

      0 零 
      在Positional Parameters一部份已經說明過了,是執行的shell script本身。但如果是用"bash -c",則$0被設為第一個參數。 

      [foxman@foxman bash]# echo $0 
      /bin/bash 

      *************************

      _ 底線符號 
      顯示出最后一個執行的命令。 

      [foxman@foxman bash]# echo $_ 
      bash 


      *************************

      內建變數(Shell Variables) 
      Bash有許多內建變數,像PATH、HOME、ENV......等等。這些內建變數將在另一節中,專門一一說明。

      函數function

      [ function ] name () { list; }
      function的參數是Positional Paraments。 

      例 
    #!/bin/sh 

    function func() { 
     echo $1 
     echo $2 
     return 1 
    } 

    func "Hello" "function" 

      局部變數可用local來宣告。 

      函數可export,使用下一層的shell可以使用。 

      函數可遞,沒有遞層數的限制。

      Bash內建指令集 

      以下的命令,大部份都沒有使用例,您可能會看不出所以然,摸不著頭腦。在我加入例說明前,建議您"man bash",然后自己實際操作一次。 

      *************************
      : [arguments] 
      不做任何事,除了[arguments]一些參數展開及一些特定重導向的作業外。 

      永遠返回零。它的用法跟true一樣。 

      *************************
      . filename [arguments] 
      source filename [arguments] 
      由filename中讀取命令,并執行。 
      您會在/etc/rc.d/*中發現很多 
      . /xxxx 
      的指令,而xxxx的permission都不是可執行的。事實上,在tcsh中,需要用 
      source /xxxx 
      來做同樣的指令。 
      注意到"."的后面是有空格的(比較一下". /"跟"./",不一樣)。filename是內含指令的純文字檔即可,無須chmod 755 filename。 

      例
      filename : my_source 
      DEV=lo 
      IP=127.0.0.1 
      NETMASK=255.0.0.0 
      BROADCAST=127.255.255.255 

      ifconfig $IP netmask $NETMASK broadcast $BROADCAST dev $DEV 

      接下來 
      . my_source 
      或 
      source my_source 

      便可執行該script,而不需要"chmod 755 my_source" 

      *************************
      alias [name[=value] ...] 
      昵稱命令 
      例如您如果來自DOS的世界,對UNIX的指令不習慣,可用alias來修改,以符合您的習慣。 

      例
      alias ls="ls --color" 
      alias dir="ls" 
      alias cd..="cd .." 
      alias copy="cp -f" # dangerous, recommend, "cp -i" 
      alias del="rm -f" # dangerous, recommend, "rm -i" 
      alias move="mv -f" # dangerous, recommend, "mv -i" 
      alias md="mkdir" 
      alias rd="rmdir" 

      *************************
      unalias [-a] [name ...] 
      unalias取消alias的設定。"unalias -a"將全部alias取消。 

      例
      unalias copy 

      *************************
      bg [jobspec] 
      將指定任務放到背景中,如果jobspec未指定,內定為目前的。 

      *************************
      fg [jobspec] 
      將指定任務放到前景中,如果jobsepc沒有指定,那么內定為目前的。 

      *************************
      jobs [-lnp] [ jobspec ... ] 
      第一種形式列出目前正在工作的任務。 
      -l : 除了列出一般資訊外,還列出Process IDs。 
      -p : 僅列出該工作群"首腦"(Process group leader)的Process ID. 
      -n : 則僅列出有改變的jobs的狀態。 
      如果給定jobspec,輸出資訊則只有該jobspec。 

      返回值為零,除非有非法的選項發生。 

      jobs -x command [ args ... ] 

      如果使用第二種形式(-x),jobs取代指定的command及args,并執行返回其Exit Status。 

      *************************
      kill [-s sigspec | -sigspec] [pid | jobspec] ... 
      將sigspec的信號送到pid或jobspec。 
      sigspec可以是SIGKILL/KILL這種形式或是信號號碼。如果sigspec是signal name,則大小寫無關,而且可以沒有SIG。 
      kill -l [signum] 
      列出信號名稱。 

      [foxman@foxman bash]# kill -l 
      1) SIGHUP    2) SIGINT    3) SIGQUIT   4) SIGILL 
      5) SIGTRAP   6) SIGIOT    7) SIGBUS    8) SIGFPE 
      9) SIGKILL   10) SIGUSR1   11) SIGSEGV   12) SIGUSR2 
      13) SIGPIPE   14) SIGALRM   15) SIGTERM   17) SIGCHLD 
      18) SIGCONT   19) SIGSTOP   20) SIGTSTP   21) SIGTTIN 
      22) SIGTTOU   23) SIGURG   24) SIGXCPU   25) SIGXFSZ 
      26) SIGVTALRM  27) SIGPROF   28) SIGWINCH  29) SIGIO 
      30) SIGPWR 

      *************************
      wait [n] 
      等待指定的行程,并返回其結束狀態。n可以是個jobspec或Process ID。如果n未指定,則等待所有的子行程,及返回值為零。若n為不存在的job或process,則返回127。否則,返回值為最后一個 job/process的Exit Status。 

      *************************
      bind [-m keymap] [-lvd] [-q name] 
      bind [-m keymap] -f filename 
      bind [-m keymap] keyseq:function-name 
      顯示出目前readline的按鍵及鏈結函數設定或是巨集。 

      -m keymap : 設定keymap binding。 
      -l : 顯示出所有readline function的名稱。 
      -v : 顯示出目前的function name及bindings。 
      -d : 顯示出function name及bindings。 
      -f filename : 從filename讀取key bindings。 
      -q function : 詢問那個按鍵觸發function。 

      *************************
      break [n] 
      跳出控制回圈for/while/until中使用。如果有指定n,則跳出n層。n必須是大于等于1。若n大于巢狀圈數,則所有的圈都會跳離。返回值回零。 

      *************************
      continue [n] 
      還原控制回圈for/while/until中使用。如果有指定n,則返回n層。n必須是大于等于1。若n大于巢狀圈數,則還原到最上層。返回值回零。 

      *************************
      exit [n] 
      離開程式。n是Exit Status。 

      *************************
      return [n] 
      在function中使用。n為返回值,其作用與Exit Status一樣。 

      *************************
      builtin shell-builtin [arguments] 
      執行內建函數。當您定義了與內建函數相同的指令時,可用此命令來執行內建函數。 

      *************************
      cd [dir] 
      更換目錄到dir。如果沒有指定,內定為HOME所指定的目錄。 

      *************************
      command [-pVv] command [arg ...] 
      用command指定可取消正常的shell function尋找。只有內建命令及在PATH中找得到的才會被執行。"-p"選項,搜尋命令的方式是用PATH來找。"-V"或"-v"選項,會顯示出該命令的一些簡約描述。 

      *************************
      declare [-frxi] [name[=value]] 
      typeset [-frxi] [name[=value]] 
      宣告參數并給它們設定屬性。如果沒有給定名稱,將會顯示各參數值。 

      -f : 僅使用函數名稱。 
      -r : 將name設為readonly。 
      -x : 將name輸出給后續環境使用。 
      -i : 該參數被設為integer來使用,可用于算術表述。 

      用"+"時,關閉該屬性。 

      *************************
      dirs [-l] [+/-n] 
      顯示目前記憶的目錄。目錄可透過pushd/popd來操作。 

      +n : 顯示開始的記錄n個。 
      -n : 顯示結尾的記錄n個。 
      -l : 顯示較多的資訊。 

      *************************
      echo [-neE] [arg ...] 
      輸出顯示args,由空白分隔。返回值永為零。 

      -n : 不跳行。 
      -e : 啟動"\"符號的解譯。 
      -E : 將ESC解譯功能取消。 

      "\a" : alert(bell),發出聲響。 
      "\b" : backspace,倒退。 
      "\c" : suppress trailing newline,不跳行。 
      "\f" : form feed,跳行跳格。 
      "\n" : new line,新行。 
      "\r" : carriage return,回到行起點。 
      "\t" : horizontal tab,水平跳位。 
      "\v" : vertical tab,垂直跳位。 
      "\\" : 輸出"\"。 
      "\nnn" : 輸出ASCII Code號碼nnn(八進位)。 

      *************************
      enable [-n] [-all] [name ...] 
      啟動或關閉內建函數命令。使用"-n"將所有指定命令皆關閉,否則都是啟動的。如果只有"-n"參數,它將會顯示所有關閉的函數。如果只有"-all",它將會顯示所有內建命令。 

      *************************
      eval [arg ...] 
      讀取args,并將args合為一個命令,然后執行。其返回值成為eval的返回值。如果沒有參數,eval返回True。 

      *************************
      exec [[-] command [arguments]] 
      當命令執行時,該命令取代shell,沒有新的process產生。如果第一個參數是"-",shell會將"-"放入第零個參數,傳給command。 

      *************************
      export [-nf] [name[=word]] ... 
      export -p 
      將name輸出給環境,給往后的命令使用。"-f"選項表示name是函數。"-p"顯示出所有export的名稱。"-n"移除name。 

      *************************
      set [--abefhkmnptuvxldCHP] [-o option] [arg ...] 
      -a : 自動將變數標記為可讓后面環境所使用。 
      -b : 立即報告被終結的背景程式狀態。 
      -e : 當命令(simple-command,見后面)返回非零值時,立即跳出。 
      -f : 取消pathname expansion。 
      -h : 找出所記憶的函數命令位置。 
      -k : 所有keyword參數都放到環境中。 
      -m : 監督模式。 
      -n : 讀取命令,但不要執行。可用于語法檢查。 
      -p : 打開privileged模式。 
      -t : 當讀取一個命令并執行后,立即離開。 
      -u : 當參數展開時,把unset參數當成是錯誤。 
      -v : 列出shell input lines。 
      -x : 在展開每個simple-command后,bash顯示展開值在PS4上。 
      -l : 儲存并還原name binding在for語法中。 
      -d : 關閉hasing command搜尋。 
      -C : 跟`noclobber=`一樣。請見內定參數一節。 
      -H : 啟動! style history substitution。 
      -P : 在使用像cd這種指令時,不要跟隨symbolic links。 
      -- : "--"之后,沒有參數跟在后面。 
      - : 指定將所有后面的參數當成是位置參數。 
      -o option-name : option-name可以是以下之一 
      allexport : 與"-a"相同。 
      braceexpand : 啟動Brace Expansion。這是內定設定。 
      emacs : 使用emacs-style命令列編輯界面。 
      errexit : 與"-e"相同。 
      histexpand : 與"-H"相同。 
      ignoreeof : 效果跟`IGNOREEOF=10`一樣。 
      interactive-commands : 允許#做為解。 
      monitor : 與"-m"相同。 
      noclobber : 與"-C"相同。 
      noexec : 與"-n"相同。 
      noglob : 與"-f"相同。 
      nohash : 與"-d"相同。 
      notify : 與"-b"相同。 
      nounset : 與"-u"相同。 
      physical : 與"-P"相同。 
      posix : Bash行為修改為Posix 1003.2標準。 
      privileged : 與"-p"相同。 
      verbose : 與"-v"相同。 
      vi : 使用vi-style命令列編輯程式。 
      xtrace : 與"-x"相同。 

      *************************
      unset [-fv] [name ...] 
      移除對映于name的參數。要注意PATH、IFS、PPID、PS1、PS2、UID、EUID不能unset。若RANDOM、SECONDS、 LINENO、HISTCMD被unset,它們會喪失原有意義,既始它們后來被重設也一樣。返回值為True,除非name是不能被unset的。 

      *************************
      fc [-e ename] [-nlr] [first] [last] 
      fc -s [pat=rep] [cmd] 
      修正命令。 

      *************************
      getopts optstring name [args] 
      解析位置參數。 

      *************************
      help [pattern] 
      顯示協助資訊。 

      *************************
      history [n] 
      history -rwan [filename] 
      沒有參數時,會顯示所下命令的歷史記錄。帶有參數"n"則顯示最后n個。 

      其它參數如下: 
      -a : 新增"新歷史"到歷史檔中。 
      -n : 讀取尚未讀到歷史中的記錄。 
      -r : 讀取filename做為歷史檔,并用它為目前歷史記錄。 
      -w : 將現有歷史記錄寫到filename中。 

      *************************
      let arg [arg ...] 
      算術表述。請參考算術表述一節。 

      *************************
      local [name[=value] ...] 
      產生一個局部參數。如果用于function,則其作用圍在function內及其子函數。 

      *************************
      logout 
      離開login shell。 

      *************************
      popd [+/-n] 
      移除目錄堆疊。"+n"移除上面n個,"-n"移除下面n個。 


      *************************
      pushd [dir] 
      pushd +/-n 
      將目錄新增到目錄堆疊的最上面。"+n"旋轉該堆疊,使第n個目錄變成最上面。"-n"旋轉該堆疊,使倒數第n個目錄變成最上面。 

      *************************
      pwd 
      列出目前工作目錄的絕對路徑。 

      *************************
      read [-r] [name ...] 
      讀進一行,然后第一個字設到第一個name,第二個設到第二個name,依此類推。如果沒有name在參數中,則read會將值設到REPLY。返回值為零,除非遇到End-Of-File。若有"-r"選項,則"\n"被考慮為該行的一部份。 

      *************************
      readonly [-f] [name ...] 
      readonly -p 
      將給定的name標記為readonly。如果是"-f"選項,則函數也一樣被標記為readonly。"-p"會列出所有readonly的name。"--"取消檢查剩余的參數。 

      *************************
      shift [n] 
      Positional Parameters從n+1...開始,會被改為$1...。n若為零,則沒有改變。n若未給定,則內定為1。n必須是非負數,并且小于或等于$#。若n大于$#,則沒有改變。返回值為零,除非n大于$#或小于零。 

      *************************
      suspend [-f] 
      暫停這個shell的執行,直到它收到SIGCONT信號。"-f"選項則是叫login shell不要抱怨,不過還是一樣暫停。返回狀態零,除非該shell是個login shell,而且沒有"-f"選項。 

      *************************
      test expr 
      [ expr ] 
      我們在Exit Status的部份已經說過了,不再重。 

      *************************
      times 
      列出該shell的累積的使用者及系統時間及從shell執行的process時間,返回值為零。 

      ------------------------------------------------------------------------------
      trap [-l] [arg] [sigspec] 
      當收到sigspec信號時,執行arg命令。"-l"顯示出信號名稱及號碼。 

      *************************
      type [-all] [-type | -path] name [name ...] 
      沒有參數的狀況下,它會顯示出shell如何解譯name做為命令。如果有"-type",它將會顯示alias、keyword、 function、builtin或file。如果有"-path"的參數,它將會顯示該命令的路徑,找不到的話,不顯示任何東西。如果有"-all"的 參數,它將會顯示所有可執行name的可能路徑。type接受"-a"、"-t"、"-p"做為縮寫。 

      *************************
      ulimit [-SHacdfmstpnuv [limit]] 
      ulimit提供了對shell的可獲取資源控制的功能。 

      -a : 報告目前所有限制。 
      -c : 設定最大可產生的core檔案。 
      -d : 行程資料段(process's data segment)最大值。 
      -f : 可被這個shell產生的最大檔案。 
      -m : resident set size最大值。 
      -s : 堆疊最大值。 
      -t : CPU TIME最大值(以秒計算)。 
      -p : pipe size in 512-byte blocks的最大值。 
      -n : 可開啟的file descriptors最大值。 
      -u : 單一使用者可使用的最大process數。 
      -v : 該shell最大虛擬記憶體可用值。 

      所有項目是以1024做為單位。 

      *************************
      umask [-S] [mode] 
      將使用者的file-creation mask設為mode。"-S"選項將mask印成符號形式。


      Bash內建參數

      PPID : 該bash的呼叫者process ID. 

      PWD : 目前的工作目錄。 

      OLDPWD : 上一個工作目錄。 

      REPLY : 當read命令沒有參數時,直接設在REPLY上。 

      UID : User ID。 

      EUID : Effective User ID。 

      BASH : Bash的完整路徑。 

      BASH_VERSION : Bash版本。 

      SHLVL : 每次有Bash執行時,數字加一。 

      RANDOM : 每次這個參數被用到時,就會產生一個亂數在RANDOM上。 

      SECONDS : 從這個Shell一開始啟動后的時間。 

      LINENO : Script的行數。 

      HISTCMD : 歷史記錄數。 

      OPTARG : getopts處理的最后一個選項參數。 

      OPTIND : 下一個要由getopts所處理的參數號碼。 

      HOSTTYPE : 機器種類。 

      OSTYPE : 作業系統名稱。 

      IFS : Internal Field Separator。 

      PATH : 命令搜尋路徑。 
      PATH="/usr/gnu/bin:/usr/local/bin:/usr/ucb:/bin:/usr/bin:." 

      HOME : 目前使用者的home directory; 

      CDPATH : cd命令的搜尋路徑。 

      ENV : 如果這個參數被設定,每次有shell script被執行時,將會執行它所設定的檔名做為環境設定。 

      MAIL : 如果這個參數被設定,而且MAILPATH沒有被設定,那么有信件進來時,bash會通知使用者。 

      MAILCHECK : 設定多久時間檢查郵件一次。 

      MAILPATH : 一串的郵件檢查路徑。 

      MAIL_WARNING : 如果有設定的話,郵件被讀取后,將會顯示訊息。 

      PS1 : 提示訊息設定,內定為"bash\$ "。(請詳見提示訊息一節。) 

      PS2 : 第二提示訊息設定,內定為"> "。 

      PS3 : select命令所使用的提示訊息。 

      PS4 : 執行追蹤時用的提示訊息設定,內定為"+ "。 

      HISTSIZE : 命令歷史記錄量,內定為500。 

      HISTFILE : 歷史記錄檔,內定~/.bash_history。 

      HISTFILESIZE : 歷史記錄檔行數最大值,內定500。 

      OPTERR : 如果設為1,bash會顯示getopts的錯誤。 

      PROMPT_COMMAND : 如果設定的話,該值會在每次執行命令前都顯示。 

      IGNOREEOF : 將EOF值當成輸入,內定為10。 

      TMOUT : 如果設為大于零,該值被解譯為輸入等待秒數。若無輸入,當成沒有輸入。 

      FCEDIT : fc命令的內定編輯器。 

      FIGNORE : 請詳見READLINE。 

      INPUTRC : readline的startup file,內定~/.inputrc 

      notify : 如果設定了,bash立即報告被終結的背景程式。 

      history_control, HISTCONTROL : history使用。 

      command_oriented_history : 存入多行指令。 

      glob_dot_filenames : 如果設定了,bash將會把"."包含入檔案路徑中。 

      allow_null_glob_expansion : 如果設定了,bash允許路徑明稱為null string。 

      histchars : history使用。 

      nolinks : 如果設定了,執行指令時,不會跟隨symbolic links。 

      hostname_completion_file, HOSTFILE : 包含與/etc/hosts相同格式的檔名。 

      noclobber : 如果設定了,Bash不會覆寫任何由">"、">&"及"<>"所操作的檔案。 

      auto_resume : 請見任務控制一節。 

      no_exit_on_failed_exec : 如果該值存在,非互動的shell不會因為exec失敗而跳出。 

      cdable_vars : 如果啟動,而cd命令找不到目錄,可切換到參數形態指定的目錄下。


      提示符號

      Bash使用PS1~PS4來顯示提示符號,其格式如下: 

      *************************

      \t : 現在時間。 
      \d : 現在日期。 
      \n : 新行。 
      \s : shell的名稱。 
      \w : 目前工作目錄。 
      \W : 目前工作目錄完整路徑。 
      \u : 使用者名稱。 
      \h : Hostname。 
      \# : 這個命令的號碼。 
      \! : 歷史號碼。 
      \$ : 如果EUID是0,則#,否則為$。 
      \nnn : 八進位的字元。 
      \\ : "\"符號。 
      \[ : 開始一序列不可列印的字元。 
      \] : 結束一序列不可列印的字元。


      算術表述

      - + 
      ! ~ 
      * / % 
      + - 
      << >> 
      <= >= < > 
      == != 
      & 
      ^ 
      | 
      && 
      || 
      = *= /= %= += -= <<= >>= &= ^= |= 

      重導Redirection

      > 
      >> 
      1> 
      . 
      . 


      語法

      Simple Command 


      Pipelines 


      Lists 

      (list) 
      { list; }
    posted @ 2006-06-16 14:20 Alex 閱讀(355) | 評論 (0)編輯 收藏

    key words: css? 隔行換色 控制表格  交替顏色

    最近用到不少css的東西,發現用css是可以省不少麻煩,特別更改效果。

    google了一篇 利用CSS控制表格的交替顏色

    下面是實現的代碼:

    <style?type="text/css">
    <!--
    tr?
    {background-color:expression((this.sectionRowIndex==0)?"":
    (this.sectionRowIndex%2==0)?"red":"blue")
    }
    -->
    </style>
    </HEAD>
    <table>
    <tr>
    <td>第1行</td><td>第1行</td>
    </tr>
    <tr>
    <td>第2行</td><td>第2行</td>
    </tr>
    <tr>
    <td>第3行</td><td>第3行</td>
    </tr>
    <tr>
    <td>第4行</td><td>第4行</td>
    </tr>
    <tr>
    <td>第5行</td><td>第5行</td>
    </tr>
    </table>

    關鍵就是這一句:
    tr?{background-color:expression((this.sectionRowIndex==0)?"":
    (this.sectionRowIndex%2==0)?"red":"blue")}


    對于td的控制如下:
    <style?type="text/css">
    <!--
    tr?
    {background-color:expression((this.sectionRowIndex%2==0)?"red":"blue")}
    td?
    {background-color:expression((this.cellIndex%2==0)?"":((this.parentElement.sectionRowIndex%2==0)?"green":"yellow"))}
    -->
    </style>
    </HEAD>
    <table>
    <tr><td>第1行</td><td>第1行</td><td>第1行<?/td><td>第1行</td><td>第1行</td></tr>
    <tr><td>第2行</td><td>第2行</td><td>第2行<?/td><td>第2行</td><td>第2行</td></tr>
    <tr><td>第3行</td><td>第3行</td><td>第3行<?/td><td>第3行</td><td>第3行</td></tr>
    <tr><td>第4行</td><td>第4行</td><td>第4行<?/td><td>第4行</td><td>第4行</td></tr>
    <tr><td>第5行</td><td>第5行</td><td>第5行<?/td><td>第5行</td><td>第5行</td></tr>
    </table>

    cellIndex 

    另:和數組一樣,第一行從0開始



    但愿對你有用.? :)



    posted @ 2006-06-15 18:14 Alex 閱讀(2990) | 評論 (2)編輯 收藏

    key words: jsp文件下載 中文亂碼 文件名亂碼

    文件上傳參看: 這里

    碰到文件亂碼,google了一下,發現這篇文章還不賴

    摘錄如下:


    之前,寫過一個Download.jsp文件,可以解決下載文件亂碼問題(諸如:DOC,XSL文件等等).
    后來發現,遇到中文名的文件的時候,文件下載將會報錯~~~~
    今天,通過改寫原Download.jsp文件已經徹底解決了這個問題~
    現在,把一整套的文件上傳下載的方法給貼出來~~~以便大家借鑒!~!~!~!~!?
    作者:古埃及法老

    download.jsp文件
    ---------------------------------------------------------
    <%
    ??java.io.BufferedInputStream?bis
    =null;
    ??java.io.BufferedOutputStream??bos
    =null;
    try{
    ?
    String?filename=request.getParameter("filename");
    ?????????????filename
    =new?String(filename.getBytes("iso8859-1"),"gb2312");
    ?response.setContentType(
    "application/x-msdownload");
    ?response.setHeader(
    "Content-disposition","attachment;?filename="+new?String(filename.getBytes("gb2312"),"iso8859-1"));
    ?bis?
    =new?java.io.BufferedInputStream(new?java.io.FileInputStream(config.getServletContext().getRealPath("files/"?+?filename)));
    ?bos
    =new?java.io.BufferedOutputStream(response.getOutputStream());?
    ?
    byte[]?buff?=?new?byte[2048];
    ?
    int?bytesRead;
    ?
    while(-1?!=?(bytesRead?=?bis.read(buff,?0,?buff.length)))?{
    ??bos.write(buff,
    0,bytesRead);
    ?}
    }
    catch(Exception?e){
    ?e.printStackTrace();
    }
    finally?{
    ?
    if?(bis?!=?null)bis.close();
    ?
    if?(bos?!=?null)bos.close();
    }
    %>?


    注意,關鍵就是setHeader里的filename需要重新編碼,格式是ISO-8859-1就OK了

    以下是我自己項目中用到的代碼片斷,供參考:

    list.jsp: 顯示附件名稱的頁面
    <tr>
    ????????????
    <td?height="25"?class="tdcor">&nbsp;&nbsp;&nbsp;</td>
    ????????????
    <td?colspan="3"?height=50>
    ????????????????
    <%
    ????????????????????
    if?(null?!=?publish.getAttatchFilename()?&&
    publish.getAttatchFilename().length()?
    >?0)?{
    ????????????????
    %>
    ????????????????
    <a?href="publish_do.jsp?method=download&fileName=
    <%=URLEncoder.encode(publish.getAttatchFilename(),"
    GBK")%>">
    <%
    =URLDecoder.decode(publish.getAttatchFilename(),"GBK")%></a>
    ????????????????
    <%
    ????????????????????}
    ????????????????
    %>
    ????????????
    </td>
    </tr>

    download.jsp:下載頁面
    else?if?(null?!=?method?&&?method.equals("download"))?{//下載附件

    ????????String?fileName?
    =?request.getParameter("fileName");
    ????????File?file?
    =?new?File(Constants.PUBLISH_FILE_PATH?+?"/"?+?URLDecoder.decode(fileName,"GBK"));
    ????????response.reset();
    ????????response.setContentType(
    "application/octet-stream;?charset=GBK");
    ????????response.addHeader(
    "Content-Disposition",?"attachment;?filename="?+?CourseDetailBusiness.transfer(URLDecoder.decode(fileName,"GBK"),"GBK","ISO-8859-1"));
    ????????response.setContentLength((
    int)?file.length());

    ????????
    byte[]?buffer?=?new?byte[4096];
    ????????BufferedOutputStream?output?
    =?null;
    ????????BufferedInputStream?input?
    =?null;

    ????????
    //?寫緩沖區:
    ????????try?{
    ????????????output?
    =?new?BufferedOutputStream(response.getOutputStream());
    ????????????input?
    =?new?BufferedInputStream(new?FileInputStream(file));

    ????????????
    int?n?=?(-1);
    ????????????
    while?((n?=?input.read(buffer,?0,?4096))?>?-1)?{
    ????????????????output.write(buffer,?
    0,?n);
    ????????????}
    ????????????response.flushBuffer();
    ????????}
    ????????
    catch?(Exception?e)?{
    ????????}?
    //?maybe?user?cancelled?download
    ????????finally?{
    ????????????
    if?(input?!=?null)?input.close();
    ????????????
    if?(output?!=?null)?output.close();
    ????????}


    說明:
    1。文件名在數據庫中保存的編碼為URLEncode
    2.在list.jsp顯示的時候多了一次encode,不知為什么,不encode一次還不行,實際上是第二次編碼了


    posted @ 2006-06-14 22:48 Alex 閱讀(35504) | 評論 (35)編輯 收藏

    key words: 數據字典 用戶 表 視圖

    1、用戶

    查看當前用戶的缺省表空間
    SQL>select username,default_tablespace from user_users;

    查看當前用戶的角色
    SQL>select * from user_role_privs;

    查看當前用戶的系統權限和表級權限
    SQL>select * from user_sys_privs;
    SQL>select * from user_tab_privs;

    顯示當前會話所具有的權限
    SQL>select * from session_privs;

    顯示指定用戶所具有的系統權限
    SQL>select * from dba_sys_privs where grantee='GAME';


    2、表

    查看用戶下所有的表
    SQL>select * from user_tables;

    查看名稱包含log字符的表
    SQL>select object_name,object_id from user_objects
    where instr(object_name,'LOG')>0;

    查看某表的創建時間
    SQL>select object_name,created from user_objects where object_name=upper('&table_name');

    查看某表的大小
    SQL>select sum(bytes)/(1024*1024) as "size(M)" from user_segments
    where segment_name=upper('&table_name');

    查看放在ORACLE的內存區里的表
    SQL>select table_name,cache from user_tables where instr(cache,'Y')>0;

    3、索引

    查看索引個數和類別
    SQL>select index_name,index_type,table_name from user_indexes order by table_name;

    查看索引被索引的字段
    SQL>select * from user_ind_columns where index_name=upper('&index_name');

    查看索引的大小
    SQL>select sum(bytes)/(1024*1024) as "size(M)" from user_segments
    where segment_name=upper('&index_name');

    4、序列號

    查看序列號,last_number是當前值
    SQL>select * from user_sequences;

    5、視圖

    查看視圖的名稱
    SQL>select view_name from user_views;

    查看創建視圖的select語句
    SQL>set view_name,text_length from user_views;
    SQL>set long 2000; 說明:可以根據視圖的text_length值設定set long 的大小
    SQL>select text from user_views where view_name=upper('&view_name');

    6、同義詞

    查看同義詞的名稱
    SQL>select * from user_synonyms;

    7、約束條件

    查看某表的約束條件
    SQL>select constraint_name, constraint_type,search_condition, r_constraint_name
    from user_constraints where table_name = upper('&table_name');

    SQL>select c.constraint_name,c.constraint_type,cc.column_name
    from user_constraints c,user_cons_columns cc
    where c.owner = upper('&table_owner') and c.table_name = upper('&table_name')
    and c.owner = cc.owner and c.constraint_name = cc.constraint_name
    order by cc.position;

    8、存儲函數和過程

    查看函數和過程的狀態
    SQL>select object_name,status from user_objects where object_type='FUNCTION';
    SQL>select object_name,status from user_objects where object_type='PROCEDURE';

    查看函數和過程的源代碼
    SQL>select text from all_source where owner=user and name=upper('&plsql_name');
    posted @ 2006-06-12 14:25 Alex 閱讀(354) | 評論 (0)編輯 收藏

    僅列出標題
    共15頁: First 上一頁 7 8 9 10 11 12 13 14 15 下一頁 
    主站蜘蛛池模板: 亚洲中文字幕久久精品无码APP | 亚洲Av熟妇高潮30p| 三年片在线观看免费大全| 精品国产污污免费网站入口| 一本色道久久88—综合亚洲精品 | 两个人日本免费完整版在线观看1| 亚洲综合精品伊人久久| 91天堂素人精品系列全集亚洲| 亚洲男人的天堂一区二区| 成年在线网站免费观看无广告| 99久久99久久精品免费观看| 国产在线精品一区免费香蕉| 偷自拍亚洲视频在线观看| 亚洲AV日韩综合一区尤物| 精品亚洲成a人片在线观看 | 东北美女野外bbwbbw免费| 高h视频在线免费观看| 亚洲sss综合天堂久久久| 亚洲美女色在线欧洲美女| 水蜜桃亚洲一二三四在线| 国产亚洲精品自在久久| 中文亚洲成a人片在线观看| 亚洲AV无码成H人在线观看| 国产高清视频在线免费观看| 在线观看无码AV网站永久免费 | 亚洲专区先锋影音| 亚洲VA中文字幕不卡无码| 亚洲一区精品伊人久久伊人| 亚洲Av无码国产情品久久| 免费中文字幕不卡视频| 国产一卡二卡≡卡四卡免费乱码| 四虎免费在线观看| 69成人免费视频无码专区| 永久免费av无码网站韩国毛片| 24小时日本电影免费看| 免费福利在线播放| www视频免费看| 成年免费大片黄在线观看岛国| 9久9久女女免费精品视频在线观看| 国产乱码免费卡1卡二卡3卡| 中国在线观看免费高清完整版|