shell腳本學習六(呈現數據)

呈現數據:

           如果你有瀏覽過我之前的關於shell腳本的博客,就會發現到目前爲止都是通過將數據打印在屏幕上或將數據重定向到文件中來顯示信息,本篇博客將會講解如何將腳本的輸出重定向到linux系統的不同位置。

1. 理解輸入和輸出

              兩種顯示腳本輸出的方法:

                     ① 在顯示器屏幕上顯示輸出

                     ② 將輸出重定向到文件中

              這兩種方法要麼將數據輸出全部顯示,要麼什麼都不顯示,但有時將一部分數據在顯示器上顯示,另一部分數據保存到文件中也是不錯的

 2、標準文件描述符

                  linux系統將每個對象當做文件來處理,包括輸入和輸出過程,linux用文件描述符( file  descriptor )來標識每個文件對象,文件描述符是一個非負整數,可以唯一標識會話中打開的文件。每個進程一次最多可以有九個文件描述符。出於特殊目的,bash shell保留了前三個文件描述符(0、1、2)

linux的標準文件描述符

文件描述符

縮寫

描述

0

STDIN

標準輸入

1

STDOUT

標準輸出

2

STDERR

標準錯誤

           這三個特殊文件描述符會處理腳本的輸入和輸出,shell用它們將shell默認的輸入和輸出導向到相應的位置

   2.1  STDIN

        STDIN 文件描述符代表shell的標準輸入,對終端界來說,標準輸入是鍵盤。shell從STDIN文件描述符對應的鍵盤獲得輸入,在用戶輸入時處理每個字符。

        在使用輸入重定向符號( < )時,linux會用重定向指定的文件來替換標準輸入文件描述符,它會讀取文件並提取數據,就如同它是鍵盤上鍵入的。

          許多bash命令能接受STDIN的輸入,尤其是沒有在命令行上指定文件的話。

              

           當在命令行只輸入cat命令時,它會從STDIN接受輸入,輸入一行,cat命令就會顯示出一行,但你也可以通過STDIN重定向符號強制cat命令接受來自另一個非STDIN文件的輸入。

             

           現在cat命令會用testfile文件中的行作爲輸入,你可以使用這種技術將數據輸入到任何STDIN接受數據局的shell命令中。

     2.2  STDOUT

            STDOUT文件描述符代表shell的標準輸出,載終端界面上,標準輸出就是終端顯示器,shell的所有輸出(包括shell中運行的程序和腳本)會被定向到標準輸出中,也就是顯示器

            默認情況下,大多數bash命令會將輸出導向STDOUT文件描述符( 即,將輸出顯示到屏幕上 ),你可以用輸出重定向來改變

               

               通過輸出重定向符號,通常會顯示到顯示器的所有輸出會被shell重定向到指定的重定向文件,你也可以將數據追加到某個文件,這可以用 >>符號來完成。

               

          who命令生成的輸出會被追加到test2 文件中已有數據的後面,但是如果你對腳本使用了標準輸出重定向,你會遇到一個問題,來看下面的例子

              

         當命令生成錯誤消息時,shell並未將錯誤消息重定向到輸出重定向文件,shell創建了輸出重定向文件,但錯誤消息卻顯示在了顯示器屏幕上,注意,在顯示test3 文件的內容時並沒有任何錯誤,test3 文件創建成功了,只是裏面是空的。

         shlell對於錯誤消息的處理是跟普通輸出分開的,如果你創建了在後臺模式下運行的shell腳本,通常你必須依賴發送到日誌文件的輸出消息,用這種方法的話,如果出現了錯誤消息,這些信息是不會出現在日誌文件中的,你需要換種方法來處理

      2.3  STDERR

         shell 通過特殊的STDERR文件描述符來處理錯誤消息,STDERR 文件描述符代表shell的標準錯誤輸出,shell或者shell中運行的程序和腳本出錯時生成的錯誤消息都會發送到這個位置。默認情況下, STDERR 文件描述符會和 STDOUT 文件描述符指向同樣的地方(儘管分配給它們的文件描述符值不同),也就是說,默認情況下錯誤消息也會輸出到顯示器輸出中。

        但從上面的例子可以看出,STDERR 並不會隨着STDOUT的重定向而發生改變。使用腳本時,你常常會想改變這種行爲,尤其是當你希望將錯誤消息保存到日誌文件中的時候。

3、重定向錯誤 

        3.1 只重定向錯誤

            STDERR文件描述符值爲 2 ,可以以此來選擇只重定向錯誤消息,將該文件描述符值放在重定向符號前 ( 該值必須緊緊地放在重定向符號前,否則不會工作 )

               

           現在運行該命令,錯誤消息就不會出現在屏幕上了,該命令生成的任何錯誤消息都會保存在輸出文件中,用這種方法,shell會只重定向錯誤消息而非普通數據,下面的例子是將STDOUT 和 STDERR 消息混雜在同一輸出中的例子

               

            ls 的正常STDOUT輸出仍然 正常輸出( 顯示到了屏幕上 ),而且由於使用了文件描述符值2將生成的所有錯誤消息直接發送到指定的重定向文件中。

         3.2  重定向錯誤和數據

            如果想重定向錯誤和正常輸出,必須用兩個重定向符號,需要在符號前放上待重定向數據所對應的文件描述符,然後指向用於保存數據的輸出文件。

                 

           shell利用 1> 符號將ls命令的正常輸出重定向到outFile文件,而這些輸出本來是進入STDOUT(即屏幕顯示),所有本該輸出到STDERR的錯誤消息通過 2> 符號被重定向到了errFile,可以用這種方法將腳本的正常輸出和腳本生成的錯誤消息分離開來,這樣就可以輕鬆地識別出錯誤消息,再不用在成千上萬行正常輸出中來回翻找了。

           另外如果願意,也可以將STDERR和STDOUT的輸出重定向到同一個輸出文件,爲此bash shell提供了特殊的重定向符號 &>

               

            當使用&>符時,命令生成的所有輸出都會發送到同一位置,包括數據和錯誤

       (  值得注意的是,這種方法下爲了避免錯誤消息散落在輸出文件中,相較於標準輸出,bash shell自動賦予了錯誤消息更高的優先級 ,這樣你就能集中瀏覽錯誤消息了 )

 4、  在腳本中重定向輸出

           可以在腳本中用STDOUT和STDERR文件描述符以在多個位置生成輸出,只要簡單地重定向相應的文件描述符就行了,有兩種方法來在腳本中重定向

           ① 臨時重定向行輸出

           ② 永久重定向腳本中的所有命令

         4.1 臨時重定向

              如果有意在腳本中生成錯誤消息,可以將單獨的一行輸出重定向到STDERR,你所需要做的是使用輸出重定向符來將輸出信息重定向到STDERR文件描述符,在重定向到文件描述符時,你必須在文件描述符數字之前加一個&:

 echo  “ This  is  an  error  message  ”   >&2    (主動產生報錯信息並傳給STDERR)

              這行會在腳本的STDERR文件描述符所指向的位置顯示文本,而不是普通的STDOUT,下面這個例子就利用了這個功能:

                    

              注意,默認情況下Linux會將STDERR導向STDOUT。

                   

                  如果你想平常一樣運行可能看不出來什麼區別,但是,如果你在運行腳本時重定向了STDERR,腳本中的所有導向STDERR的文本都會被重定向。

             

                 通過STDOUT顯示的文本顯示在了屏幕上。而發送給 STDERR 的echo語句的文本則被重定向到了輸出文件,這個方法非常適合在腳本中生成錯誤消息,如果有人用了你的腳本,他們可以像上面的例子中那樣輕鬆地通過STDERR文件描述符重定向錯誤消息。

5、 永久重定向

               如果腳本中有大量數據需要重定向,那重定向每個echo語句就會很煩瑣,取而代之你可以用exec命令告訴shell在腳本執行期間重定向某個特定文件描述符

                    

                    

              exec命令會啓動一個新shell並將STDOUT文件描述符重定向到文件,腳本中發給STDOUT的所有輸出會被重定向到文件,也可以在腳本執行過程中重定向STDOUT.

                   

                   

            上面這個腳本使用exec命令來將所有發給STDERR輸出重定向到文件testerror,然後使用echo直接在屏幕顯示兩句話,隨後又使用exec命令來將STDOUT重定向到testout文件,通過執行腳本可以看出,儘管STDOUT被重定向了,但是你仍然可以將echo語句的輸出發給STDERR,在本例中還是重定向到testerror文件。

6、在腳本中重定向輸入

         你可以使用與腳本中重定向STDOUT和STDERR相同的方法來將STDIN從鍵盤重定向到其他位置。exec命令允許你將STDIN重定向到Linux系統的文件中

            exec   0<  testfile

          此命令會告訴shell它應該從文件testfile中獲得輸入,而不是STDIN,這個重定向只要在腳本需要輸入時就會作用:

             

             

         將STDIN重定向到文件後,當read命令試圖從STDIN讀入數據時,它會到文件去取數據,而不是鍵盤,這是在腳本中從待處理的文件中讀取數據的絕妙方法,Linux系統管理員的一項日常任務就是從日誌文件中讀取數據並處理,這是完成該任務最簡單的方法。

 7、創建自己的重定向

        在腳本中重定向輸入和輸出時,並不侷限於這三個默認的文件描述符,前面的博客中曾提到過,在shell中最多可以有9個打開的文件描述符,其他6個從3~8的文件描述符可用作輸入或輸出重定向。你可以將這些文件描述符中的任意一個分配給文件,然後在腳本中使用它們。

       7.1  創建輸出文件描述符

              可以用exec命令來給輸出分配文件描述符。和標準的文件描述符一樣,一旦將另一個文件描述符分配給一個文件,這個重定向就會一直有效,直到你重新分配,這裏有個在腳本中使用其他文件描述符的例子。

           

           \

        這個腳本用exec命令將文件描述符3重定向到另一個文件,當腳本執行echo語句時,輸出內容會像預想中那樣出現在STDOUT上,但你重定向到文件描述符3的那行echo語句的輸出卻進入了另一個文件。這樣你就可以在顯示器上保持正常的輸出,而將特定信息重定向到文件中(比如日誌文件)。

       也可以不用創建新文件,而是使用exec命令來將輸出追加到現有文件中

          exec  3>>test3out 

     7.2  重定向文件描述符

        這個小節介紹怎麼恢復已重定向的文件描述符,你可以分配另外一個文件描述符給標準文件描述符,這意味着你可以將STDOUT的原來位置重定向到另一個文件描述符,然後再利用該文件描述符重定向回STDOUT( 可通過例子進行理解 )

         

         

         我們一段一段的分析,首先,腳本將文件描述符3重定向到文件描述符1的當前位置,也就是STDOUT。這意味着任何發送給文件描述符3的輸出都將出現在顯示器上。

         第二個exec命令將STDOUT重定向到文件,shell現在會將發送給STDOUT的輸出直接重定向到輸出文件中。但是,文件描述符3仍然指向STDOUT原來的位置,也就是顯示器。如果此時將輸出數據發送給文件描述符3,它仍然會出現在顯示器上,儘管STDOUT已經被重定向了。

          最後在向STDOUT( 現在指向一個文件 )發送一些輸出之後,腳本使用exec將STDOUT重定向到文件描述符3的當前位置( 現在仍然是顯示器)。這意味着現在STDOUT又指向了它原來的位置:顯示器。

           這個方法可能會讓你感到困惑,但這是一種在腳本中臨時重定向輸出,然後恢復默認輸出設置的常用方法。(  按照我個人理解,這個方法有點像寫代碼時爲了保持一個變量值初始不變的同時使用其值,遂找一個臨時變量存儲最開始的值,操作完之後再將臨時變量存儲的原始值返還  ) 簡單點說就是下面的說法

         7.3  創建輸入文件描述符

           可以用和重定向輸出文件描述符同樣的方法重定向輸入文件描述符,在重定向到文件之前,先將STDIN文件描述符保存到另外一個文件描述符,然後在讀取完文件之後再將STDIN恢復到它原來的位置。

            

                 

            在這個例子中,文件描述符6用來保存STDIN的位置,然後腳本將STDIN重定向到一個文件,read命令的所有輸入都來自重定向後的STDIN( 也就是輸入文件 )。

            在讀取了所有行之後,腳本會將STDIN重定向到文件描述符6,從而將STDIN恢復到原先的位置,該腳本用了另外一個read命令來測試STDIN是否恢復正常了,這次它會等待鍵盤的輸入。

        7.4  創建讀寫文件描述符

             你可以打開單個文件描述符來作爲輸入和輸出,可以用一個文件描述符對同一個文件進行讀寫。

             不過用這種方法時,你要特別小心。由於你是對同一個文件進行數據讀寫,shell會維護一個內部指針,指明在文件中的當前位置。任何讀或寫都會從文件指針上次的位置開始。如果不夠小心,它會產生一些令人瞠目的結果,

                  

                   

             這個例子用了exec命令將文件描述符3分配給文件testfile以進行文件讀寫。接下來,它 通過分配好的文件描述符,使用read命令讀取文件中的第一行,然後將這一行顯示在STDOUT上。 後,它用echo語句將一行數據寫入由同一個文件描述符打開的文件中。 在運行腳本時,一開始還算正常。輸出內容表明腳本讀取了testfile文件中的第一行。但如果 你在腳本運行完畢後,查看testfile文件內容的話,你會發現寫入文件中的數據覆蓋了已有的數據。
             當腳本向文件中寫入數據時,它會從文件指針所處的位置開始。read命令讀取了第一行數據,所以它使得文件指針指向了第二行數據的第一個字符。在echo語句將數據輸出到文件時, 它會將數據放在文件指針的當前位置,覆蓋了該位置的已有數據。 

         7.5  關閉文件描述符

               如果你創建了新的輸入或輸出文件描述符,shell會在腳本退出時自動關閉它們。然而在有些情況下,你需要在腳本結束前手動關閉文件描述符。

                要關閉文件描述符,將它重定向到特殊符號&-  

                           exec   3>&-

                該語句會關閉文件描述符3。不再在腳本中使用它。這裏有個例子來說明當你嘗試使用已關閉的文件描述符時會怎樣

                       

                       

             一旦關閉了文件描述符,就不能在腳本中向它寫入任何數據,否則shell會生成錯誤消息。

             在關閉文件描述符時還要注意另一件事。如果隨後你在腳本中打開了同一個輸出文件,shell 會用一個新文件來替換已有文件。這意味着如果你輸出數據,它就會覆蓋已有文件。

              

                  

             在向test17file文件發送一個數據字符串並關閉該文件描述符之後,腳本用了cat命令來顯示 文件的內容。到目前爲止,一切都還好。下一步,腳本重新打開了該輸出文件並向它發送了另一 個數據字符串。當顯示該輸出文件的內容時,你所能看到的只有第二個數據字符串。shell覆蓋了 原來的輸出文件

 8、列出打開的文件描述符

            lsof命令會列出整個linux系統打開的所有文件描述符,這是個有爭議的功能,因爲它會向非系統管理員提供linux系統的信息,鑑於此,許多linux系統隱藏了該命令,這樣用戶就不會一不小心就發現了。

            在許多linux系統中,lsof命令位於/usr/sbin目錄,要想以普通用戶賬戶來運行它,必須通過全路徑名來引用。

            $/usr/sbin/lsof

            該命令會產生大量的輸出,它會顯示當前linux系統上打開的每個文件的有關信息,包括後臺運行的所有進程以及登錄到系統的任何用戶

            有大量的命令行選項和參數可以用來幫助過濾lsof的輸出,最常用的有-p和-d,前者允許指定進程ID(PID),後者允許指定要顯示的文件描述符編號

            要想知道進程的當前PID,可以用特殊環境變量$$(shell會將它設爲當前PID)。-a選項用來對其他兩個選項的結果執行布爾AND運算,這會產生如下輸出(本人安裝的)

               

          上例顯示了當前進程(bash shell )的默認文件描述符(0,1,2)。lsof的默認輸出中有7列信息,如下表所示

               

    列                           描述

COMMAND          正在運行的命令名的前9個字符

PID                       進程的PID

USER                   進程屬主的登錄名

FD                        文件描述符號以及訪問類型(r代表讀,w代表寫,)

TYPE                   文件的類型

DEVICE               設備的設備號

SIZE                     如果有的話,表示文件的大小

NODE                  本地文件的節點號

NAME                   文件名

                     與STDIN、STDOUT和STDERR關聯的文件類型是字符型。因爲STDIN、STDOUT和STDERR文 件描述符都指向終端,所以輸出文件的名稱就是終端的設備名。所有3種標準文件都支持讀和寫 (儘管向STDIN寫數據以及從STDOUT讀數據看起來有點奇怪)。 

                             

                             

               該腳本創建了3個替代性文件描述符,兩個作爲輸出(3和6),一個作爲輸入(7)。在腳本 運行lsof命令時,可以在輸出中看到新的文件描述符。我們去掉了輸出中的第一部分,這樣你 就能看到文件名的結果了。文件名顯示了文件描述符所使用的文件的完整路徑名。它將每個文件 都顯示成REG類型的,這說明它們是文件系統中的常規文件。 

9、阻止命令輸出

        有時候,你可能不想顯示腳本的輸出。這在將腳本作爲後臺進程運行時很常見(參見第16 章)。如果在運行在後臺的腳本出現錯誤消息,shell會通過電子郵件將它們發給進程的屬主。這 會很麻煩,尤其是當運行會生成很多煩瑣的小錯誤的腳本時。

        要解決這個問題,可以將STDERR重定向到一個叫作null文件的特殊文件。null文件跟它的名 字很像,文件裏什麼都沒有。shell輸出到null文件的任何數據都不會保存,全部都被丟掉了。

       在Linux系統上null文件的標準位置是/dev/null。你重定向到該位置的任何數據都會被丟掉, 不會顯示。

                 

       也可以在輸入重定向中將/dev/null作爲輸入文件。由於/dev/null文件不含有任何內容,程序員 通常用它來快速清除現有文件中的數據,而不用先刪除文件再重新創建。

                  

         文件testfile仍然存在系統上,但現在它是空文件。這是清除日誌文件的一個常用方法,因爲 日誌文件必須時刻準備等待應用程序操作。     

10、創建臨時文件

        Linux系統有特殊的目錄,專供臨時文件使用。Linux使用/tmp目錄來存放不需要永久保留的 文件。大多數Linux發行版配置了系統在啓動時自動刪除/tmp目錄的所有文件。

        系統上的任何用戶賬戶都有權限在讀寫/tmp目錄中的文件。這個特性爲你提供了一種創建臨時文件的簡單方法,而且還不用操心清理工作。

        有個特殊命令可以用來創建臨時文件。mktemp命令可以在/tmp目錄中創建一個唯一的臨時 文件。shell會創建這個文件,但不用默認的umask值(參見第7章)。它會將文件的讀和寫權限分配給文件的屬主,並將你設成文件的屬主。一旦創建了文件,你就在腳本中有了完整的讀寫權限, 但其他人沒法訪問它(當然,root用戶除外)。 

          10.1   創建本地臨時文件

                默認情況下,mktemp會在本地目錄中創建一個文件。要用mktemp命令在本地目錄中創建一 個臨時文件,你只要指定一個文件名模板就行了。模板可以包含任意文本文件名,在文件名末尾 加上6個X就行了。

                    

              mktemp命令會用6個字符碼替換這6個X,從而保證文件名在目錄中是唯一的。你可以創建多個臨時文件,它可以保證每個文件都是唯一的。  

             如上例所示,mktemp命令的輸出正是它所創建的文件的名字。在腳本中使用mktemp命令時,可能要將文件名保存到變量中,這樣就能在後面的腳本中引用了。 

              

                 

           這個腳本用mktemp命令來創建臨時文件並將文件名賦給$tempfile變量。接着將這個臨時 文件作爲文件描述符3的輸出重定向文件。在將臨時文件名顯示在STDOUT之後,向臨時文件中寫 入了幾行文本,然後關閉了文件描述符。後,顯示出臨時文件的內容,並用rm命令將其刪除 

        10.2   在/tmp目錄創建臨時文件 
        -t  選項會強制mktemp命令來在系統的臨時目錄來創建該文件。在用這個特性時,mktemp命令會返回用來創建臨時文件的全路徑,而不是隻有文件名。

              

        10.3   創建臨時目錄 

         -d 選項告訴mktemp命令來創建一個臨時目錄而不是臨時文件。這樣你就能用該目錄進行任何需要的操作了,比如創建其他的臨時文件。 

               

             

         10.4  記錄消息
          將輸出同時發送到顯示器和日誌文件,這種做法有時候能夠派上用場。你不用將輸出重定向兩次,只要用特殊的tee命令就行。 tee命令相當於管道的一個T型接頭。它將從STDIN過來的數據同時發往兩處。

         一處是 STDOUT,另一處是tee命令行所指定的文件名: 
                      tee filename

          由於tee會重定向來自STDIN的數據,你可以用它配合管道命令來重定向命令輸出。 

         

          -a 選項將數據追加到文件中        

        

 11、小結

          在創建腳本時,理解了bash shell如何處理輸入和輸出會給你帶來很多方便。你可以改變腳本 獲取數據以及顯示數據的方式,從而在任何環境中定製腳本。腳本的輸入/輸出都可以從標準輸 入(STDIN)/標準輸出(STDOUT)重定向到系統中的任意文件。除了STDOUT,你可以通過重定 向STDERR輸出來重定向由腳本產生的錯誤消息。這可以通過重定向與STDERR輸出關聯的文件描 述符(也就是文件描述符2)來實現。可以將STDERR輸出和STDOUT輸出到同一個文件中,也可 以輸出到完全不同的文件中。這樣就可以將腳本的正常消息同錯誤消息分離開。

         bash shell允許在腳本中創建自己的文件描述符。你可以創建文件描述符3~9,並將它們分配 給要用到的任何輸出文件。一旦創建了文件描述符,你就可以利用標準的重定向符號將任意命令 的輸出重定向到那裏。

         bash shell也允許將輸入重定向到一個文件描述符,這給出了一種將文件數據讀入到腳本中的 簡便途徑。你可以用lsof命令來顯示shell中在用的文件描述符。

         Linux系統提供了一個特殊的文件(稱爲/dev/null)來重定向不需要的輸出。Linux系統會刪 掉任何重定向到/dev/null文件的東西。你也可以通過將/dev/null文件的內容重定向到一個文件中來 產生空文件。

         mktemp命令是bash shell中一個很方便的特性,可以輕鬆地創建臨時文件和目錄。只需要給 mktemp命令指定一個模板,它就能在每次調用時基於該文件模板的格式創建一個唯一的文件。 也可以在Linux系統的/tmp目錄創建臨時文件和目錄,系統啓動時會清空這個特殊位置中的內容。
        tee命令便於將輸出同時發送給標準輸出和日誌文件。這樣就可以在顯示器上顯示腳本的消 息的同時,又能將它們保存在日誌文件中。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章