深入了解bash shell script

深入了解bash shell script

前言

相信所有玩過 Unix的人都會同意 shell script 是一個十分強大的工具,可以用來幫助自已在短時間內解決許多枯\躁而乏味的工作,而且讓工作變得有趣。

對於 shell script 要怎麼寫,為什麼會想到這麼寫?對於這個問題,我個人的答案是:用多了,就知道了。有時很聰明的想法,往往是靈機一動,就想到了。我會建議大家,如果寫了好用的 script 就把他留下來,給個好名字。有時候用得上時,就會很高興有這個自製的工具。

寫 script 之前,要先了解:

1. 資料流

  • >、>>、<、| , #&># .....

2. 一些好用的指令

cat ,grep,awk,sed,find,more...........

3. 變數

4. 運算式

5. 判斷式

if ,if else,if elif else,case ... esac

6. 迴圈

for,while,until

7.函式

參考 : 如何撰寫Shell Script

資料流

UNIX是第一個支援資料流的作業系統。什麼是資料流呢?
就是把程式的輸入和輸出想像成一個串流,在 Unix 之下,任和一個一般的程式都會直接開三個檔案。
  • 0. stdin
  • 1. stdout
  • 2. stderr

這三個檔案分別是 資料流的 輸入和輸出,及錯誤輸出。在 default 之下,stdin 是接著 keyboard,stdout 是接著 tty (你想成 monitor 就好了),stderr 也是接著 tty

資料流既然是"流",那當然可以轉向。"<"  這個指令呢就是把 stdin 接上後面的檔案,如:
  • $cat < test.txt
  • ">" 是把 stdout 寫入後面的 file。
  • 如檔案內有東西,會把檔案內的東西清空才寫入。

">>" 和 ">" 相同,但不會把檔案內的東西清空,而是寫在檔尾,如:

  • $cat test.txt >> foo
"|" (pipe)這個呢則是把前面的stdout 接到後面程式的 stdin,如:
  • cat test.txt | more
  • 這樣, cat 的輸出就可以被 more 接下來。

>, >> 還可以把 stderr 轉到 stdout,如:

  • 2>&1
  • 也可把 stdout 轉到 stderr,如:1>>&2
在 shell script 中資料流轉向,和pipe 是十分常用的技巧(應該說是基本工夫),在一般下指令的時候也是很常用上,如:
  • cat pbsd.c | grep CONFIG_PBS

善用工具

有時候如果要做一些比較複雜的動作。接個四五個 pipe 再加上轉向,也不為過。如果有好的能力,沒有好的工具,那也是沒有用,但很幸運的是,之前的 Unix Programer 寫了許多方便自己的工具,相同的那些工具,也能方便我們。

下面是一些工具的簡單介紹。(都可用 pipe 和轉向)

cat : concatenate,把檔案打開,印到 stdout 上。

RegExp Regular Expression 正則表達式。

以下幾個非常強大的指令 "grep" "awk" "find" ....或是"ls" ,不的不提到正則表達式 RegExp,簡直是如虎添翼。可參考影片 : 

grep :globally search a regular expression and print,以正則表達式進行全域尋找以及列印

grep 是一個在輸入資料中找出指明要找的字串的那一行文字的工具。如:
  • $grep "I Love Shell Script" test.txt
  • 就是把 test.txt 這個檔案中,有 "I Love Shell Script" 這個字串的那一行印>到stdout 中。
  • $cat test.txt | grep "I Love Shell Script"        #也是一樣的

awk: 全稱為"Aho Weiberger and Kernighan"三個作者的性的第一個字母。

一個用來分析內文的工具,它可以掃描文章內容,找出你要的文字,且依你的要求做輸出,你可以把它當做 grep 的加強版。awk 的語法很像 C 語言,所以對會寫 C 語言的人而言,這是一個很好用的工具,如:
  • $awk '{print NR,$0}' text.txt
  • 就會把 text.txt 的內容印出來,且加上行數,

sed:

這個工具可以幫你把你想換掉的字串一次換掉,awk 和 sed 這兩個工具是十分強大的工具,它們可以做到的事十分的多,遠比它一開始被定義時所想到的功能還多,多到可以寫一本書來談。O'Reilly 就有出一本 awk & sed 的書,有興趣的話可以找一本來看看。

find:

這個工具可以依你的要求,找出你想要的檔案,在系統管理上很好用。可依時間,大小,檔名,修改時間等等的選項來找檔案。

more (less) :

把 stdin or 檔案內容分成一頁一頁的來看,一般來說, less 比較強一些。可以支援 vi 的語法,但目前 more 也一一支援了。

以上每個工具都是十分好用的工具。但我沒有詳細的談,詳細的東西呢,你可以用 man(使用手冊)這個工具來看。如:

man find : 

  • 然後你就會了解為什麼我只說一下它們的功能了。

變數

在 bash 中設定變數很容易,不用指定形態直接指定就可以了,如
  • myvar="I love linux." ,就是指定 myvar 這個變數為 "I love linux." 這個字串。
  • 使用這個變數呢,則是用 $myvar 來使用,如:   echo $myvar  

如想對 變數做運算,可用 let :

  • let "expression", 如:  let i=i+1

參數

在 shell script 中,可以在 script 加上參數,而參數會依你所打入的指令依序定義,如有一個script 叫 showme

#!/bin/bash
# filename showme
echo $0
echo $1
echo $2

執行結果如下

>./showme abc def
> ./showme
abc
def
如果要 show出所有參數,可以 $* 表之。要知道參數數量,可以 $# 表之,$$ 為 script 執行時的 pid。

寫著寫著,有一些東西不清楚,找了一下 man,發現 man 寫得真是清楚極了。

在 bash 之中 我們可以用 ` command` 來執行指令,且將其視為一個整體,最後的結果為全部的結果,例如:

  • ls -alF `find -size 0`
就是找出所有 size == 0 的檔案,將之結果當做是 ls -alF 的參數。這個技巧在 shell 之中很常見,
如 :
  • NumberOfFiles=`find | wc -l`
就是把 find | wc -l 這結果指定為 NumberOfFiles 的值

Shell Script 完整的表達式

shell script 是一個完整的語言,當然會有 if else 等等的指令,就讓我們來看看如何使用 bash shell 的 判斷式吧!

判斷式

在這裏我是假設大家會使用某種程式語言。格式是如下的:
  • if expression; then
  •   list
  • elif list; then
  •    list
  • else
  •   list
  • fi

這裏的 list 代表是一個或一堆指令的集合。expression 代表的是一個會有return 值的指令。

  • 如expression 不為空,或 0 就表示為 true
  • fi 代表 這個 if 結束了 如同 C 語言的 }
  • then 就如同 C 語言的 {
  • elif 後面的 list 會依序執行,到了最後如不為空,或 0 就為真 也就是再執行後面的 list

對於 expression 如要寫判斷式 有兩種格式

1. ((表達式))

這個格式等同於 let "expression",這就是把 expression 內的資料做計算,例如
  • (( $abc + 1 ))

2. [[表達式]]

這個格式等是可以比較這個 expression 的兩邊,如
  • [[ $1 == "abc" ]]
如果相同,就為 1 否則為 0

比較的運算子有 == != ! && ||

  • == 是全等
  • != 不等
  • !  not 運算子
  • &&  and 運算子
  • ||  or 運算子
如: ( (( $# > 3 )) && [[ $1=="abc" ]] && ( [[ $2!="def" ]] || ![[ $3=="ghi" ]]) )

範例

if (( $#==0 )) ; then
echo "usage: xxx [xxx | xxxx]"
else
 if ( [[ $1 != "xxx" ]] && [[ $1 != "xxxx" ]] ) ; then
  echo "prometer [xxx | xxxx]"
  exit
 fi
 if [ $1 = "xxx" ]; then
  .............
 else
  ................
 fi
fi

for 和 while

在程式之中,for 和 while 讓我們省下了很多的力氣。在bash shell 之中for 和while 的用法如下
為了:在bash 中 for 的用法有兩種

for : 在bash 中 for 的用法有兩種

  • for name [ in word ] ; do list ; done
  • for (( expr1 ; expr2 ; expr3 )) ; do list ; done
第一種是從 字串的array 中拿出一個,成為 name (name 為變數),再進入迴圈中,直到array 中的字拿完為止 (list 為迴圈內容)。

第二種就很像 C 語言了, 第一次跑時,一開始 run expr1,再check expr2,跑完 list 後 跑 expr3 再跑 expr2,一旦expr2 的結果是 0 or 空 就跳離。

while : while 的用法

  • while list_a ; do list_b; done

當 list_a 的最後一個 command 結果不為 0 或 空之時,就進入迴圈做 list_b

until:

  • until list_a; do list_b; done
和 while 相反,當 list_a 的最後一個 command 的結果不為 0 或空時,離開迴圈。

還有一些東西 如 select case esac 等... 這些東西可以用 if for or while 組合而成,就不再說明了。

開始來寫寫 bash shell script 吧

就拿之前post 過的 chout 為例子好了。首先,一始開寫 shell script 的人很有可能不知道如何執行 shell script,假設我們有一個 shell script 叫 chout 已經寫好了,我們有兩個方法可以執行它
  • 1. bash chout
    • 這個方法是執行一個 bash ,且叫它讀入 chout 並執行
  • 2. 在chout 的第一行加入#!/bin/bash
    • (告訴你的shell 這個 shell script 指定用 bash)
再chmod a+x chout,(指定 chout 為一個可執行檔),之後你就可以把 chout 當一個程式來使用了。

讓我們來看看這個例子

01 #!/bin/bash
02 if (( $#==1 )) ; then
03  ares=`find -name '*.c'`
04  ares="$ares `find -name '*.h'`"
05 else
06   ares=`find -name $2`
07 fi
08 for arrs in $ares
09 do
10  if [ -f $arrs ] && (( `cat $arrs | grep -i $1 | wc -l` > 0 )); then
11   echo "In file $arrs"
12   cat $arrs | awk '{print "#"NR"\t"$0}' | grep -i $1
13   echo " "
14  fi
15 done
01 行告訴我們,這是一個 bash 的程式
02 $# 是變數的數量,也就是 : 若 變數的數量為 1 則做 03 04 否則做 06
03 把 "find -name *.c" 這個指令的結果存在ares 中
04 ares 再加上 find -name *.h 的結果
06 找 名字為第二個參數的檔案,且存成 ares
08 在 ares 中依序抓出一個元素,命之為 arrs ,如抓完了,做 15 行的下一行(沒有了,就是結束了)
09 10~14 是迴圈範圍
10 當 $arrs 是檔案 ( [ ] 是 test 的簡寫 (有興趣請看 man test)) 且 $arrs 有大於一行包涵 第一個參數的話,做 11~13
11 應該不用解釋吧
12 為$arrs 中的內容加上行號,只 show 出有 $1 的行
13 印空白行

以上只是一個小小的 shell script,因為 shell script 不用 compile ,只要隨手改改,隨手寫寫就能用。不用考慮太多形態的問題,所有程式都是指令。都是可用的function,所以在使用上很方便,只要多加一點點巧思,就可以用一點點的時間,做出一些很炫的東西。
試著玩玩看吧,保証 Linux 功力會大增喔!!

讓我們在看個例子

#!/bin/bash
for file in *
do
echo $file
done
這樣的shell,會列出目前目錄中所有的檔名
  • *   是shell default的符號,它表示一個array存放所有的檔名資料,for 會將array中的資料一筆一筆的丟進file變數中,直到file為空,才跳出回圈。
  • $   將file變數中的值取出
以上是整個for回圈的運作過程,希望對大家有幫助。

參考資料

特色、摘要,Feature、Summary:

關鍵字、標籤,Keyword、Tag:

  • Ubuntu,Shell Script,Shell,Linux,

留言

這個網誌中的熱門文章

Ubuntu 常用指令、分類與簡介

iptables的觀念與使用

網路設定必要參數IP、netmask(遮罩)、Gateway(閘道)、DNS

了解、分析登錄檔 - log

Python 與SQLite 資料庫

Blogger文章排版範本

Pandas 模組

如何撰寫Shell Script

查詢指令或設定 -Linux 線上手冊 - man

下載網頁使用 requests 模組