Linux Shell進階技巧(二)

來源:互聯網
上載者:User

七、非直接引用變數:

      在Shell中提供了三種為標準(直接)變數賦值的方式:
      1. 直接賦值。
      2. 儲存一個命令的輸出。
      3. 儲存某類型計算的結果。
      然而這三種方式都是給已知變數名的變數賦值,如name=Stephen。但是在有些情況下,變數名本身就是動態,需要依照啟動並執行結果來構造變數名,之後才是為該變數賦值。這種變數被成為動態變數,或非直接變數。
      /> cat > test7.sh
      #!/bin/sh
      work_dir=`pwd`
      #1. 由於變數名中不能存在反斜線,因此這裡需要將其替換為底線。
      #2. work_dir和file_count兩個變數的變數值用於構建動態變數的變數名。
      work_dir=`echo $work_dir | sed 's/\//_/g'`
      file_count=`ls | wc -l`
      #3. 輸出work_dir和file_count兩個變數的值,以便確認這裡的輸出結果和後面構建的命令名一致。
      echo "work_dir = " $work_dir
      echo "file_count = " $file_count
      #4. 通過eval命令進行評估,將變數名展開,如${work_dir}和$file_count,並用其值將其替換,如果不使用eval命令,將不會完成這些展開和替換的操作。最後為動態變數賦值。
      eval BASE${work_dir}_$file_count=$(ls $(pwd) | wc -l)
      #5. 先將echo命令後面用雙引號擴住的部分進行展開和替換,由於是在雙引號內,僅完成展開和替換操作即可。
      #6. echo命令後面的參數部分,先進行展開和替換,使其成為$BASE_root_test_1動態變數,之後在用該變數的值替換該變數本身作為結果輸出。
      eval echo "BASE${work_dir}_$file_count = " '$BASE'${work_dir}_$file_count
      CTRL+D
      /> . ./test7.sh
      work_dir =  _root_test
      file_count =  1
      BASE_root_test_1 = 1
   
八、在迴圈中使用管道的技巧:

      在Bash Shell中,管道的最後一個命令都是在子Shell中執行的。這意味著在子Shell中賦值的變數對父Shell是無效的。所以當我們將管道輸出傳送到一個迴圈結構,填入隨後將要使用的變數,那麼就會產生很多問題。一旦迴圈完成,其所依賴的變數就不存在了。
      /> cat > test8_1.sh
      #!/bin/sh
      #1. 先將ls -l命令的結果通過管道傳給grep命令作為管道輸入。
      #2. grep命令過濾掉包含total的行,之後再通過管道將資料傳給while迴圈。
      #3. while read line命令從grep的輸出中讀取資料。注意,while是管道的最後一個命令,將在子Shell中運行。
      ls -l | grep -v total | while read line
      do
          #4. all變數是在while塊內聲明並賦值的。
          all="$all $line"
          echo $line
      done
      #5. 由於上面的all變數在while內聲明並初始化,而while內的命令都是在子Shell中運行,包括all變數的賦值,因此該變數的值將不會傳遞到while塊外,因為塊外地命令是它的父Shell中執行。
      echo "all = " $all
      CTRL+D
      /> ./test8_1.sh
      -rw-r--r--.  1 root root 193 Nov 24 11:25 outfile
      -rwxr-xr-x. 1 root root 284 Nov 24 10:01 test7.sh
      -rwxr-xr-x. 1 root root 108 Nov 24 12:48 test8_1.sh
      all =

      為瞭解決該問題,我們可以將while之前的命令結果先輸出到一個臨時檔案,之後再將該臨時檔案作為while的重新導向輸入,這樣while內部和外部的命令都將在同一個Shell內完成。
      /> cat > test8_2.sh
      #!/bin/sh
      #1. 這裡我們已經將命令的結果重新導向到一個臨時檔案中。
      ls -l | grep -v total > outfile
      while read line
      do
          #2. all變數是在while塊內聲明並賦值的。
          all="$all $line"
          echo $line
          #3. 通過重新導向輸入的方式,將臨時檔案中的內容傳遞給while迴圈。
      done < outfile
      #4. 刪除該臨時檔案。
      rm -f outfile
      #5. 在while塊內聲明和賦值的all變數,其值在迴圈外部仍然有效。
      echo "all = " $all
      CTRL+D
      /> ./test8_2.sh
      -rw-r--r--.  1 root root   0 Nov 24 12:58 outfile
      -rwxr-xr-x. 1 root root 284 Nov 24 10:01 test7.sh
      -rwxr-xr-x. 1 root root 140 Nov 24 12:58 test8_2.sh
      all =  -rwxr-xr-x. 1 root root 284 Nov 24 10:01 test7.sh -rwxr-xr-x. 1 root root 135 Nov 24 13:16 test8_2.sh

      上面的方法只是解決了該問題,然而卻帶來了一些新問題,比如臨時檔案的產生容易導致效能問題,以及在指令碼異常退出時未能及時刪除當前使用的臨時檔案,從而導致產生過多的垃圾檔案等。下面將再介紹一種方法,該方法將同時解決以上兩種方法同時存在的問題。該方法是通過HERE-Document的方式來替代之前的臨時檔案方法。
      /> cat > test8_3.sh
      #!/bin/sh
      #1. 將命令的結果傳給一個變數    
      OUTFILE=`ls -l | grep -v total`
      while read line
      do
          all="$all $line"
          echo $line
      done <<EOF
      #2. 將該變數作為該迴圈的HERE文檔輸入。
      $OUTFILE
      EOF
      #3. 在迴圈外部輸出迴圈內聲明並初始化的變數all的值。
      echo "all = " $all
      CTRL+D
      /> ./test8_3.sh
      -rwxr-xr-x. 1 root root 284 Nov 24 10:01 test7.sh
      -rwxr-xr-x. 1 root root 135 Nov 24 13:16 test8_3.sh
      all =  -rwxr-xr-x. 1 root root 284 Nov 24 10:01 test7.sh -rwxr-xr-x. 1 root root 135 Nov 24 13:16 test8_3.sh
   
九、自連結指令碼:

      通常而言,我們是通過指令碼的命令列選項來確定指令碼的不同行為,告訴它該如何操作。這裡我們將介紹另外一種方式來完成類似的功能,即通過指令碼的軟串連名來協助指令碼決定其行為。
      /> cat > test9.sh
      #!/bin/sh
      #1. basename命令將剝離指令碼的目錄資訊,只保留指令碼名,從而確保在相對路徑的模式下執行也沒有任何差異。
      #2. 通過sed命令過濾掉指令碼的副檔名。
      dowhat=`basename $0 | sed 's/\.sh//'`
      #3. 這裡的case語句只是為了示範方便,因此類比了應用情境,在實際應用中,可以為不同的分支執行不同的操作,或將某些變數初始化為不同的值和狀態。
      case $dowhat in
      test9)
          echo "I am test9.sh"
          ;;
      test9_1)
          echo "I am test9_1.sh."
          ;;
      test9_2)
          echo "I am test9_2.sh."
          ;;
      *)
          echo "You are illegal link file."
          ;;
      esac
      CTRL+D
      /> chmod a+x test9.sh
      /> ln -s test9.sh test9_1.sh
      /> ln -s test9.sh test9_2.sh
      /> ls -l
      lrwxrwxrwx. 1 root root   8 Nov 24 14:32 test9_1.sh -> test9.sh
      lrwxrwxrwx. 1 root root   8 Nov 24 14:32 test9_2.sh -> test9.sh
      -rwxr-xr-x. 1 root root 235 Nov 24 14:35 test9.sh
      /> ./test9.sh
      I am test9.sh.
      /> ./test9_1.sh
      I am test9_1.sh.
      /> ./test9_2.sh
      I am test9_2.sh.

十、Here文檔的提示:

      在命令列互動模式下,我們通常希望能夠直接輸入更多的資訊,以便當前的命令能夠完成一定的自動化任務,特別是對於那些支援自訂指令碼的命令來說,我們可以將指令碼作為輸入的一部分傳遞給該命令,以使其完成該自動化任務。
      #1. 通過sqlplus以dba的身份登入Oracle資料庫伺服器。
      #2. 在通過登入後,立即在sqlplus中執行oracle的指令碼CreateMyTables和CreateMyViews。
      #3. 最後執行sqlplus的退出命令,退出sqlplus。自動化工作完成。
      /> sqlplus "/as sysdba" <<-SQL
      > @CreateMyTables
      > @CreateMyViews
      > exit
      > SQL
        
十一、擷取進程的運行時間長度(單位: 分鐘):

      在進程監控指令碼中,我們通常需要根據指令碼的參數來確定有哪些績效參數將被收集,當這些績效參數大於最高閾值或小於最低閾值時,監控指令碼將根據實際的情況,採取預置的措施,如郵件通知、直接殺死進程等,這裡我們給出的例子是收集進程運行時間長度績效參數。
      ps命令的etime值將給出每個進程的運行時間長度,其格式主要為以下三種:
      1. minutes:seconds,如20:30
      2. hours:minutes:seconds,如1:20:30
      3. days-hours:minute:seconds,如2-18:20:30
      該指令碼將會同時處理這三種格式的時間資訊,並最終轉換為進程所流經的分鐘數。
      /> cat > test11.sh
      #!/bin/sh
      #1. 通過ps命令擷取所有進程的pid、etime和comm資料。
      #2. 再通過grep命令過濾,只擷取init進程的資料記錄,這裡我們可以根據需要替換為自己想要監控的進程名。
      #3. 輸出結果通常為:1 09:42:09 init
      pid_string=`ps -eo pid,etime,comm | grep "init" | grep -v grep`
      #3. 從這一條記錄資訊中抽取出etime資料,即第二列的值09:42:09,並賦值給exec_time變數。
      exec_time=`echo $pid_string | awk '{print $2}'`
      #4. 擷取exec_time變數的時間組成部分的數量,這裡是3個部分,即時:分:秒,是上述格式中的第二種。
      time_field_count=`echo $exec_time | awk -F: '{print NF}'`
      #5. 從exec_time變數中直接提取分鐘數,即倒數第二列的資料(42)。
      count_of_minutes=`echo $exec_time | awk -F: '{print $(NF-1)}'`
    
      #6. 判斷當前exec_time變數儲存的時間資料是屬於以上哪種格式。
      #7. 如果是第一種,那麼天數和小時數均為0。
      #8. 如果是後兩種之一,則需要繼續判斷到底是第一種還是第二種,如果是第二種,其小時部分將不存在橫線(-)分隔字元分隔天數和小時數,否則需要將這兩個時間欄位繼續拆分,以擷取具體的天數和小時數。對於第二種,天數為0.
      if [ $time_field_count -lt 3 ]; then
          count_of_hours=0
          count_of_days=0
      else
          count_of_hours=`echo $exec_time | awk -F: '{print $(NF-2)}'`
          fields=`echo $count_of_hours | awk -F- '{print NF}'`
          if [ $fields -ne 1 ]; then
              count_of_days=`echo $count_of_hours | awk -F- '{print $1}'`
              count_of_hours=`echo $count_of_hours | awk -F- '{print $2}'`
          else
              count_of_days=0
          fi
      fi
      #9. 通過之前代碼擷取的各個欄位值,計算出該進程實際所流經的分鐘數。
      #10. bc命令是計算機命令,可以將echo輸出的數學運算式計算為最終的數字值。
      elapsed_minutes=`echo "$count_of_days*1440+$count_of_hours*60+$count_of_minutes" | bc`
      echo "The elapsed minutes of init process is" $elapsed_minutes "minutes."
      CTRL+D
      /> ./test11.sh

      The elapsed minutes of init process is 577 minutes.
   
十二、類比簡單的top命令:
    
      這裡用指令碼實現了一個極為簡單的top命令。為了示範方便,我們在指令碼中將很多參數都寫成硬代碼,你可以根據需要更換這些參數,或者用更為靈活的方式替換現有的實現。
      /> cat > test12.sh
      #!/bin/sh
      #1. 將ps命令的title賦值給一個變數,這樣在每次輸出時,直接列印該變數即可。
      header=`ps aux | head -n 1`
      #2. 這裡是一個無限迴圈,等價於while true
      #3. 每次迴圈先清屏,之後列印uptime命令的輸出。
      #4. 輸出ps的title。
      #5. 這裡需要用sed命令刪除ps的title行,以避免其參與sort命令的排序。
      #6. sort先基於CPU%倒排,再基於owner排序,最後基於pid排序,最後再將結果輸出給head命令,僅顯示前20行的資料。
      #7. 每次等待5秒後重新整理一次。
     while :
      do
          clear
          uptime
          echo "$header"
          ps aux | sed -e 1d | sort -k3nr -k1,1 -k2n | head -n 20
          sleep 5
      done
      CTRL+D    
      /> ./test12.sh
      21:55:07 up 13:42,  2 users,  load average: 0.00, 0.00, 0.00
      USER       PID %CPU %MEM    VSZ   RSS   TTY      STAT START   TIME   COMMAND
      root      6408     2.0      0.0   4740   932   pts/2    R+    21:45     0:00   ps aux
      root      1755     0.2      2.0  96976 21260   ?        S      08:14     2:08   nautilus
      68        1195     0.0      0.4   6940   4416    ?        Ss    08:13     0:00   hald
      postfix   1399    0.0      0.2  10312  2120    ?        S      08:13     0:00   qmgr -l -t fifo -u
      postfix   6021    0.0      0.2  10244  2080    ?        S      21:33     0:00   pickup -l -t fifo -u
      root         1       0.0      0.1   2828   1364    ?        Ss     08:12    0:02   /sbin/init
      ... ...

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.