資料來源:http://oss.csie.fju.edu.tw/note/linux/bash%20shell%20script.txt
相信所有玩過 Unix的人都會同意 shell script 是一個十分強大的工具,可以用來幫助自已在短時間內解決許多枯\躁而乏味的工作,而且讓工作變得有趣。
對於 shell script 要怎麼寫,為什麼會想到這麼寫?對於這個問題,我個人的答案是:用多了,就知道了。有時很聰明的想法,往往是靈機一動,就想到了。我會建議大家,如果寫了好用的 script 就把他留下來,給個好名字。有時候用得上時,就會很高興有這個自製的工具。
當然,要寫 script 之前,要先了解一些東西。
1. 資料流
>, >> , < , | , #&># .....
2. 一些好用的工具
cat
grep
awk
sed
find
more
........ everything you can use.
3. 變數
4. 指令代換
5. 判斷式
if else fi
6. 流程控制
while for
....
在之後的幾篇文章中,我會慢慢把一個個 topic 寫完...
希望不會寫得太糟
UNIX是第一個支援資料流的作業系統。
什麼是資料流呢?
就是把程式的輸入和輸出想像成一個串流,
在 Unix 之下,任和一個一般的程式都會直接開三個檔案。
0. stdin
1. stdout
2. stderr
這三個檔案分別是 資料流的 輸入和輸出,及錯誤輸出
stdin ====> 程式 =====> stdout
∟==> 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 寫了許多方便自己的工具,相同的那些工具\r
也能方便我們。
下面是一些工具的簡單介紹。(都可用 pipe 和轉向)
cat :
把檔案打開,印到 stdout 上
grep :
grep 是一個在輸入資料中找出指明要找的字串的那一行文字的工具。
如
grep "I Love Shell Script" test.txt
就是把 test.txt 這個檔案中,有 "I Love Shell Script" 這個字串的那一行印>到
stdout 中。
cat test.txt | grep "I Love Shell Script" #也是一樣的
awk :
一個用來分析內文的工具,它可以掃描文章內容,找出你要的文字,且
依你的要求做輸出,你可以把它當做 grep 的加強版。
awk 的語法很像 C 語言,所以對會寫 C 語言的人而言,這是一個很好用的工具
如
awk '{print NR,$0}' text.txt
就會把 text.txt 的內容印出來,且加上行號/數
sed :
這個工具可以幫你把你想換掉的字串一次換掉
awk 和 sed 這兩個工具是十分強大的工具,它們可以做到的事十分的多
遠比它一開始被定義時所想到的功能還多,多到可以寫一本書來談。\r
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 寫得真是清楚極了。
上 google 查了一下,有一個網頁整理的很好
http://www.fanqiang.com/a4/b1/index_b.html
看來我可以不用寫了
在 bash 之中 我們可以用 ` command` 來執行指令,且將其視為一個整體,最後的結果為全部的結果
例如
ls -alF `find -size 0`
就是找出所有 size == 0 的檔案
將之結果當做是 ls -alF 的參數
這個技巧在 shell 之中很常見如
NumberOfFiles=`find | wc -l`
就是把 find | wc -l 這結果指定為 NumberOfFiles 的值
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. (( expression ))
這個格式等同於 let "expression"
這就是把 expression 內的資料做計算
例如
(( $abc + 1 ))
2. [[ expression ]]
這個格式等是可以比較這個 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 讓我們省下了很多的力氣
在 bash shell 之中 for 和 while 的用法如下
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回圈的運作過程,希望對大家有幫助。
cat+grep+awk時間過濾組合語法:
回覆刪除01透過grep透過先抓有「disconnect」的整行資料
02.並且用awk -F" "(以tab為分隔方式)抓出第2、16、17欄的資料
03.在從抓出來三欄的第一欄資料來做時間比對,滿足條件才輸出到另一檔案中
cat wowzastreamingengine_access.log|grep " disconnect"|awk -F" " '{print $2","$16","$17}'|awk '{if (($1>"01:10:10") && ($1<"01:10:50")) print}' > data.txt;sync;sync