SHELL SCRIPTING 教學與心得

来源:互联网 发布:java soap 客户端实例 编辑:程序博客网 时间:2024/06/05 18:34

SHELL SCRIPTING 教學與心得


版權聲明
前言
基本觀念與操作
Shell Script 概論
變數定義
shell的判斷式
迴圈 for while loop
shell script的陣列 (array) 處理
sed 字串編輯器
awk 結構化的資料處理工具
範例 (未完成)
舊版shell文件1
舊版shell文件2
舊版perl文件1

 

前言


底下的教學與心得分享是假設你已經有著基本的UNIX or Linux觀念
與技巧, 同時我們利用Linux預設提供的bash shell來操作, 例如 :

- login in
- "basic" commands (ls, mkdir..etc)
- how to move around the system(with cd command)
- 使用 vi

同時讀者可能也必須了解基本程式控制, 例如 : 變數定義, 流程控制
loop 等.

另外, shell script的技巧基本上是不分UNIX or Linux的, 所以學會
shell script是很吃香的喔! 作為一個系統管理者, 除了在系統管理上
需要熟悉那些工具, 檔案的位置等等外, shell script絕對是不可或缺的
能力.

同時, 也希望如果你覺得這個對你有幫助, 請給我一個email幫我加油 !

tiger2000@gmail.com

 


基本觀念與操作

建立第一個shell script

$ pwd
/home/xfish/bin
$ ls
myscript
myscript2
$ cat myscript
ls -aF
$ myscript
bash: ./myscript: Permission denied
$ chmod +x myscript
$ myscript
./       ../       myscript*     myscript2*

shell script其實就是一個很簡單的文字檔案,  檔案裡面有著
可以操作與控制相關動作與命令, 同時它必須具備能夠執行的能力,
在UNIX與Linux環境下, 也就是必須有 +x 的屬性

上面是一個很簡單的shell script, 簡單地列出目前目錄下的檔案.

同時由於Linux/UNIX底下有多種shell的編譯器, 我們也可以在該script
中的第一行來指定利用特定的編譯器執行, 例如我們可以指定 :
#!/bin/bash

使用echo命令

echo是用來顯示輸出的命令, 用幾個範例做個解釋, 後面的章節
將會看到常常利用echo來做些輸出的動作 :

$ echo a     b      c
a b c
$ echo "a           b        c"
a           b        c
$ echo $0
-bash       (說明, 這個會依照你使用的shell不同而改變)

同時echo還能搭配以下特殊符號來控制特別輸出 :

/a alert, beeps the bell
/b backspace character
/c suppresses the new line
/f formfeed character
/n new-line
/r return character
/t tab character
/v vertical tab character
// single backslash character

 

起始檔案 startup files


跟以往的DOS相同, 使用者登入後, 系統的shell在讀取完系統的
設定檔後(/etc/profile), 會自動讀取使用者shell,
例如 : $HOME/.bashrc 或者 $HOME/.profile

因此, 若有一些需要使用者登入後就執行或者設定的動作, 可以在這
兩個檔案作設定的起始檔案


命令區隔

若有同時兩個命令要執行, 必須使用 ; 來區隔

$ date who    (錯誤)
date: bad conversion
$ date ; who  (正確)
Tue Aug 25 15:38:24 2005
xfish  tty00  Aug 25 15:30
root  ttyp1  Aug 25 10:00
$ cd bin ; pwd
/home/xfish/bin
$ cd .. ; pwd
/home/xfish


Grouping 命令

利用( ) 來整合命令, 例如 :
$ pwd
/home/xfish
$ ( cd /bin ; pwd )
/bin
$ pwd
/home/xfish
$ ls -l /usr/bin ; ls -l /usr/share | wc -l
這樣會列出 /usr/bin 與/usr/share 目錄下的檔案, 但是
事實上我們只要算算這總共有多少命令, 因此我們可以利用下面的命令取代 :
$ (ls -l /usr/bin ; ls -l /usr/share ) | wc -l
185
$

 

Shell Script 概論

這一章, 我們會談到包括一些使用shell script的基本方法, 主要會提到 :
Aliases 別名
Here Documents 可以說是一種內嵌的文件
Job Control 工作控制

Aliases 別名


直接在命令提示符號下 : alias 命令 , 你就可以看到目前定義的 alias.
而alias就是常常用來把一大串命令縮寫的常用技巧.

比如說我喜歡ls的時候加上ls -F選項, 同時我希望打ls時候預設就帶入 -F
參數, 就可以利用 :

$ alias ls='ls -F' 來達到你的目的
多看幾個例子 :
$ alias lf="/usr/bin/ls -CaF" 
$ alias rm=echo 
$ rm /tmp/junk
/tmp/junk


Here Documents

一種內嵌於shell script的技巧, 直接用例子說明 :


$ cat phone
#!/bin/bash
grep -i $1 << END 
A Company 404 123 5888
Rosebery Corp. 314 713 7639
Chedworth.com 212 987 6543
Atlanta.Net 770 111 2222
END

$ phone atla
Atlanta.Net 770 111 2222    (這是輸出結果)

另外, END 那個只是一個識別, 也就是說, 你可以用其他字元代替,
不過還是建議用單字或者字串做為識別, 而不要用符號


Job Control 工作控制

工作流程控制, 大概有幾個命令一定得先了解, 包括了 :


jobs [-lp] [job]
bg [job..]
fg [job..]
kill [-signal] job..
wait [job..]
同時上述的 [job] 可以用這樣的方式來表示 :

%number : refer to job by number
%string : job whose name begins with string
%?string : job whose name contains string
%+ or %% : current job
%- : previous job

所以希望參考完這個例子, 你就能對Job Control工作流程控制有些了解.


$ stty susp ^Z                      (確保 susp 是 ^Z (Ctrl + Z))
$ stty tostop
$ ls -R /                           (這個命令可以讓系統跑很久, 按下 Ctrl + Z)
^Z
[1]+ Stopped  ls -R /               (系統提示這個編號 1 的 job 被 stopped)
$ bg                                (執行 bg 命令, 把編號 1 丟到後端去跑)
[1]+ ls -R / &  
$ (sleep 36 ; echo Hello World) &   (執行第二道命令)
[2] 656                             (系統給編號 2 的命令)
[1]+ Stopped  ls -R /
$ jobs                              (利用 jobs 命令 查看所有後端工作)
[1]- Stopped ls -R /
[2]+ Stopped (sleep 36;echo Hello World)
$ kill %-                           (刪除前一編號的工作)     
[1]- Stopped ls -R /
$ jobs                              (利用 jobs 命令 再次查看所有後端工作)
[1]- Terminated ls -R /
[2]+ Stopped (sleep 36; echo Hello World)
$ fg %2                             (把編號 2 命令帶到 前端 )
(sleep 36; echo Hello World)
Hello World                         (列印出 結果 並結束 )
$

 

變數定義

變數在shell script裡面扮演很重要的腳色, 會寫程式的都應該知道變數的定義, 其重要性, 也不用我多說, 我們這邊會包括以下幾部分 :
變數的定義
保留/系統預設的變數
變數的定義

還是直接用例子來說明, 最容易了解. 底下這些都是變數定義的方式(在/bin/bash或者/bin/sh環境下) :


$ name=Derek
$ fullname='Derek Super'
$ completename="Derek Super Man"
$ echo my name is $name, $fullname, $completename
my name is Derek, Derek Super, Derek Super Man
$
$ question='What is "sheckle"'
$ echo my question is $question
my question is What is "sheckle"
$


保留/系統預設的變數

系統預設已經有相當多的變數定義了, 因此在你的shell script裡面要去避免這些變數. 以下就是一些預設的系統變數.


BASH_ENV  absolute path of startup file
CDPATH   directories searched by cd
FCEDIT   absolute path of history editor
HISTCMD   the history number of the current command
HISFILE   absolute path of history file
HISTSIZE  number of remembered commands
HOME   login directory
IFS   token delimiters
LINENO   current line number in shell script
LINES   terminal height
MAIL   absolute path of mailbox
MAILCHECK  number of seconds to check mail
OLDPWD   absolute path of previous directory
OPTARG   option set by getopt
OPTIND   option's ordinal position set by getopt
OSTYPE   the OS on which bash is executing
PATH   command search path
PPID   process ID of parent
PS1   primary prompt
PS2   secondary prompt
PWD   absolute path of current directory
RANDOM   random integer
REPLY   default variable for read
SECONDS   number of seconds since shell started
SHELL   absolute pathname of preferred shell
TMOUT   seconds to log out after lack of use
UID   user ID of the current user
$   process ID of current shell
?   exit status of most recent statement

還有一些常用的 符號變數, 分列如下 :

$# 參數的數目
$* 代表所有參數
$? Exit status of previous command
$$ PID of this shell's process
$! PID of the most recently started backgroup job

 

shell的判斷式


從這邊開始, 我們將開始進入一些shell script的判斷, 就是說
我們會利用到if-then-else的一些判斷, 來讓shell script具備判斷能力.

討論的主題預計包括 :

If-Then-Else 敘述
Testing 判斷式
Case 敘述

If-Then-Else

先用一個簡單的例子, 來敘述If-Then-Else敘述.

$ cat myscript5
#!/bin/sh
if grep $1 /etc/passwd
then
  echo "FOUND !!!!"
fi

$ myscript5 xfish
xfish:x:200:200:Xfish:/home/xfish:/bin/bash
FOUND !!!!
$ myscript5 ABCdefga
$

說明 :
myscript5 將會對/etc/passwd檔案 進行尋找, 若找到參數所指定的字
串, 就印出 FOUND !!!!

因為我們/etc/passwd有xfish這個帳號, 因此 第一個命令會找到
該字串, 並印出 FOUND !!!!

所以 由於我們/etc/passwd並沒有ABCdefga字串, 因此直接結束程式.
沒有印出任何訊息.

但是如果我們只想要 在有找到我指定的字串時, 簡單的輸出 FOUND !!!!
不要把 grep 命令的 output 印出來, 同時加上一些變數, 讓輸出
更多樣化, 我們可以把 myscript5 再修改成 :

$ cat myscript5
#!/bin/sh
if grep $1 /etc/passwd > /dev/null
then
  echo "found $1"
else
  echo "didn't find $1"
fi

$ myscript5 xfish
found xfish

$ myscript5 ABAaa
didn't find ABAaa

那如果多重式的if-then-else要怎麼寫呢? 我們再以剛剛的例子來變化.

$ cat myscript6
#!/bin/sh
if grep $1 /etc/passwd > /dev/null
then
  echo "found $1 in /etc/passwd"
elif grep $1 /etc/greoup > /dev/null
then
  echo "found $1 in /etc/group"
else
  echo "didn't find $1"
fi

$ myscript6 other
found other in /etc/group

$ myscript6 AabcdB
didn't find AabcdB

關於if-then-else的寫法, 我自己通常的習慣, 都會將 then
往上提, 這樣的寫法 可以讓程式行數少一點, 比較美觀一點. 參考看看.

$ cat myscript6
#!/bin/sh
if grep $1 /etc/passwd > /dev/null; then
  echo "found $1 in /etc/passwd"
elif grep $1 /etc/group > /dev/null; then
  echo "found $1 in /etc/group"
else
  echo "didn't find $1"
fi

Testing 判斷式


Testing 判斷式通常與if-then-else一起互用, 以便發揮其效果.
先從一個最簡單的例子看起 :

$ cat myscript7
#!/bin/sh
if [ -f /etc/passwd ]; then
   echo "/etc/passwd is a file"
else
   echo "PANIC : /etc/passwd is not a file!!"
fi

先說明一下 [ -f /etc/passwd ] 這個最重要的部份, [ ] 裡面
就是判斷式, 而這裡是判斷 /etc/passwd 是不是一個 file ?
由結果來看看是執行 then 下面的敘述, 或者執行 else 的敘述.

同時很重要一點是 底下這些敘述都是不正確的敘述, 會有錯誤產生喔 :
[-f /etc/passwd ]  [-f 連在一起
[ -f /etc/passwd]  passwd] 連在一起
[ -f/etc/passwd ]  -f與/etc/passwd連在一起

所以利用 [ ] 我們可以做出以下這些判斷, 這些也都很常用喔 !!

test     true if ....
--------------------------------------------------------------------------------
[ string1 = string2 ]                   string1 and string2 are equal
[ string1 != string2 ]                  string1 and string2 are not equal
[ string1 /< string2 ]   string1 is lexically less than string2
                                        (e.g. 'a' is less than 'b')
[ string1 /> string2 ]   string1 is lexically greater than string2
                                        (e.g. 'b' is greater than 'a')
[ -z string ]    string is zero (e.g. a empty string)
[ -n string ]    string is nonzero (e.g. a VAR string)
[ -e file ]    file exists
[ -f file ]    file is a file
[ -d file ]    file is a directory
[ -c file ]    file is a character device
[ -b file ]    file is a block device
[ -p file ]    file is a named pipe
[ -s file ]    file is not empty
[ -k file ]    file's sticky bit is set
[ -S file ]    file is a socket
[ -L file ]    file is a symbolic link
[ -r file ]    file is readable by user
[ -w file ]    file is writeable by user
[ -x file ]    file is executeable by user
[ -O file ]    file is owner by user
[ -G file ]    file is group owned by a greoup
[ -u file ]    file has its set user ID bit set
[ -g file ]    file has its group user ID bit set
[ file1 -nt file2 ]   file1 is newer than file2
[ file1 -ot file2 ]   file1 is older than file2
[ file -ef file2 ]   file1 is another name for file2
[ n1 -eq n2 ]    true if integer n1 = integer n2
[ n1 -ne n2 ]    true if integer n1 <> n2    
[ n1 -gt n2 ]    true if n1 > n2                
[ n1 -ge n2 ]    true if n1 >= n2
[ n1 -lt n2 ]    true if n1 < n2
[ n1 -le n2 ]    true if n1 <= n2
--------------------------------------------------------------------------------

case 敘述


case 敘述與 if-then-else 效用類似, 請參考下述例子 :

$ cat myscript9
#!/bin/sh
case $1 in
  [a-z]*) echo "starts with lower case"
  ;;
  [A-Z]*) echo "starts with upper case"
  ;;
  ?*)  echo "starts with something else"
  ;;
  *)  echo "doesn't start"
esac

case敘述是由 case 開頭與 esac (case的到寫) 組合而成, 而每一個
判斷之後的兩個分號 (;;) 就是代表 break 的功能, 也就是說遇到 ;;
就會跳出整個 case 判斷式.

而上述的範例能夠判斷script所附帶的參數, 若參數為小寫字母開頭,
則印出 "starts with lower case", 反之若是大寫字母開頭, 則印出
"starts with uppper case".

再用一個簡單的例子說明 :

$ cat myscript10
#!/bin/sh
case $1 in
  0) echo zero ;;
  1) echo one ;;
  2) echo two ;;
  3) echo three ;;
  4) echo four ;;
  5) echo five ;;
  6) echo six ;;
  7) echo seven ;;
  8) echo eight ;;
  9) echo night ;;
  *) echo $1 ;;
esac

 

迴圈 for while loop

迴圈的宣告基本上所有的語言都是類似, 也就是都靠
while, until 或者 for 這兩個方法來做迴圈.

while迴圈

$ cat myloop1
#!/bin/sh
read -r filename
while [ ! -f $filename ]; do
  echo "--> $filename does not exist"
  read -r filename
done
echo "$filename name OK"

$ myloop1
/etc/password
--> /etc/password does not exist
/etc/groups
--> /etc/groups does not exist
/etc/group
/etc/group name OK
$

說明 :
執行後, 會等待使用者輸入一個檔名, 接著會檢查
該輸入字串是否真為一個檔案 (利用 -f 判斷), 若是
則跳出 while loop 並且印出 "檔名 name OK"
反之, 則印出 "--> 檔名 does not exist" , 並繼續
要求使用者輸入.

until 迴圈

同 while , until 的功用與語法很類似, 請參考

$ cat myloop2
#!/bin/sh
until who | grep dino > /dev/null; do
  sleep 10
done
echo "dino has logged in"

$ myloop2 &
[2] 9281
$
$
$ dino has logged in

我們再用一個例子來讓迴圈多點變化 :

$ cat myloop3
#!/bin/sh
i=1
while [ $i != 7 ]; do
  echo $i
  i=`expr $i + 1`
done

$ myloop3
1
2
3
4
5
6
$

這個範例 我想就不用怎麼說明了, 不過利用這個範例
跟大家分享一下, 我常常用來做倒數計時的一個迴圈 :

#!/bin/sh
interval=10
total=1
while [ $total -le 5 ]; do
  i=1
  while [ $i -le $interval ]; do
    echo -e "$total / $i / $interval /r/c"
    sleep 1
    i=`expr $i + 1`
  done
  total=`expr $total + 1`
done

請大家試試 就可以知道 這大概是什麼用的, 不過有時候
echo 的語法 似乎在各種不同的 shell (e.g. /bin/sh, /bin/bash)
之間 並不完全相同 這可能要花一點點時間去確認的.

for 迴圈

繼續談到 for 迴圈, for 迴圈通常使用在比較有明確範圍的情況.
例如 :

$ cat myloop4
#!/bin/sh
for foo in *; do
  if [ -f $foo ]; then
     echo "$foo is a file"
  elif [ -d $foo ]; then
     echo "$foo is a directory"
  else
     echo "$foo is not a file, nor directory"
  fi
done

$ myloop4
snake.txt is a file
tab.txt is a file
tinrc.txt is a file
tmp is a directory
tmp1 is a file

 


shell script的陣列 (array) 處理
陣列(array)的用途應該不用特別說明, 就是讓你用變數更方便, 用來
處理相類型的資料更方便.

所以, 用實例說明比較快:

比如說, 我今天要寄信給三個管理者, 沒有陣列的時候, 可以這樣寫:

#!/bin/bash
admin1=aaa@example.com
admin2=bbb@example.com
admin3=ccc@example.com

mail -s "Log file" $admin1 < $mylog   # $mylog 隨便定義
mail -s "Log file" $admin2 < $mylog   # $mylog 隨便定義
mail -s "Log file" $admin3 < $mylog   # $mylog 隨便定義

這是腳踏實地的寫法, 但是比較彈性的作法:

#!/bin/bash
admins=(aaa@example.com bbb@example.com ccc@example.com)

for name in ${admins[@]}; do
  mail -s "Log file" $name < $mylog
done

或者這樣的 array 存取也是一樣的:
#!/bin/bash
admins=(aaa@example.com bbb@example.com ccc@example.com)

for ((i=0; i<${#admins[@]}; i++)); do    #請注意 ((   )) 雙層括號
  mail -s "Log file" ${admins[$i]} < $mylog     # thanks to keng.tisy@msa
done

所以重點:
${#array[@]} 代表所有array總數
${array[0]} 是array的第一個變數值, 也就是從0開始, 最大值-1也就是最後一個值

 

 

sed 字串編輯器
這部份要先偷懶一下, 因為之前已經寫過一些 sed 的簡易說明,
這裡直接把它拿過來, 同時加上一些補充.


將 filename 檔案內的 Giga 字串取代成 GigaRama
sed s/Giga/GigaRama/ filename

但是我們這樣只能做 stdout , 若是要把 file1 裡面的 Giga
都取代成 GigaRama 的話, 我們可以這樣做 :

cat file1 | sed s/Giga/GigaRama/ > file2

將 filename 檔案內的 xfish 字串那一行刪除
sed /xfish/d filename

同樣, 若是要將修改過的檔案變成一個新檔案, 可以這樣做 :

cat file1 | sed /xfish/d > file2

指定哪一行,將之刪除
sed '4d' filename

這裡是指定第四行 刪除

將filename的第一行到第幾行,將之刪除
sed '1,4d' filename

這裡是指定第一行到第四行 刪除

將filename的第一行到第五行印出
sed -n 1,5p filename

將 file 檔案內的出現 xfish 字串的那一行單獨寫到 file2 內
sed -n '/xfish/w file2' file

萬用字元的使用,將 file 檔案內的 xfis? 哪一行寫到 file2 內
sed '/xfis./w file2' file

萬用字串的使用,將 file 檔案內的 xfis* 哪一行寫到 file2 內
sed '/xfis*/w file2' file

選定字元的使用,將 file 檔案內的 xfis[abcd] 哪一行寫到 file2 內
sed '/xfis[abcd]/w file2' file

特別符號的取消,利用 /
sed s///// file

一行的起頭的取代,將 file 檔案的每一行起頭都加上 Hi..
sed s/^/Hi.. / file

一行的結尾的取代,將 file 檔案的每一行結尾都加上 Hi..
sed s/$/Hi.. / file

多重條件的指定,利用 -e 選項
sed -e 's/Giga/GigaRama/' -e 's/^/Hi../' file
 

awk 結構化的資料處理工具
(未完成)


範例 (未完成)

這些部分都是我自己寫的例子, 可能不完全正確, 或者說
還有很多可以改進的地方, 如果有任何建議, 請給我email.

還有一些部份是直接從網路上收集的, 挑幾個作一些參考.

另外, 若你有什麼好的script, 願意一起放進來, 我都很歡迎喔.:)

把目錄下大寫的檔案名稱 修改成小寫的檔案名稱

#!/bin/sh
for file in [A-Z]*; do
   echo "processing $file"
   mv $file `echo $file | tr '[A-Z]' '[a-z]'`
done

用shell script計算經過時間, 時間的加減法, 比較時間方法
這是從別人那邊學來的, 因為shell script可以使用的函數不夠多,
但是透過這樣的方式就可以來計算經過的時間:

##### 取得開始時間  這個也可以用   date '+%s' 取代
before=`date '+%s'`

##### 這裡就是要系統去作什麼事情的命令
sleep 125

##### 舉得命令結束的時間
after=`date '+%s'`
elapsed=`echo "$after - $before" | bc`
hours=`echo "elapsed / 3600" | bc`
elapsed=`echo "$elapsed - $hours * 3600" | bc`
minutes=`echo "$elapsed / 60" | bc`
seconds=`echo "$elapsed - $minutes * 60" | bc`

echo "it takes : $hours:$minutes:$seconds"
# 這個例子會  -->    0:2:5


修改檔名, 把 *.htm 修改成 *.html

這個在DOS下可能用一個 rename 就可以解決了, 但是很抱歉
UNIX/Linux上面可不行. 所以 shell script 就可以幫上忙了.

#!/bin/sh
for i in *.htm ; do
  echo "processing $i"
  mv $i `basename $i .htm`.html
done

輸入 mycmd f1 f2 f3 f4 .. fn 把每個 f* 複製成 f*.bak

利用 while 與 shift 的技巧 :

#!/bin/sh
while [ $# -gt 0 ]; do
  # 註解  把 echo 拿掉  就真的變成命令了..
  # 這裡只有 echo 出來  並沒真正執行
  echo "cp $1 $1.bak"
  shift
done

系統登入後 提示選單 選擇連上 PTT BBS (2007/03/22新增)

這是懶人的做法, 就是登入login系統後, 出現一個選單, 選單內
讓你能夠選擇連上 PTT BBS , 並且自動登入 PTT BBS , 這個有利用
expect , 也就是hacker喜歡用的一個工具 , 直接看例子吧 :

檔案 1 : 命名 bbs.sh

#!/bin/bash

while [ 1 ]; do
   clear
   echo "==================================================="
   echo "  (1) PTT BBS          "          #註解:我只給一個選項 for test
   echo "  (q) QUIT             "          #註解:要更多 請自己加
   echo "  (0) 離開選單         "          #註解:要更多 請自己加
   echo "==================================================="
   echo -n "your choice? ==> "
   read ans
   case $ans in
        1)
           ptt.expect                #註解:所以你還必須有個 ptt.expect 檔案
           ;;
        q)
           kill -9 $MY_PID     #註解:這個必須在你的 .login 中註明
           ;;
        0)
           exit
           ;;
        *)
           echo "Please choose the number from the list"
           ;;
   esac
done
檔案結束


檔案 2 : 命名 ptt.expect           #系統必須安裝expect套件喔

#!/usr/local/bin/expect --         #註解:路徑請自行修改喔
set host 140.112.90.72             #註解:這裡是ptt.cc的 IP , 用ptt.cc也OK

spawn telnet $host

expect "入代號"
send "superman/r"                 #註解:把你的帳號名稱放這裡
expect "輸入您的密"
send "supermanisgoodboy/r"        #註解:把你的密碼放這裡

send "/r"

interact
檔案結束

檔案 3 : $HOME/.login  or $HOME/.bashrc  or $HOME/.tcshrc
         依照你自己設定 看看你一進入系統會讀取哪個檔案
         我只有用兩行 說明  當然你可能還有自己的變數 or 設定

export MY_PID $$        #註解:這個是給 bbs.sh 用的
bbs.sh                  #啟動剛剛的 bbs.sh  路徑自己寫.
檔案結束


移除字串與字串間的空白
這個透過簡單的 sed 命令即可達成 , 例如我的檔案如下:

aaaaa  :12345
bbbb:33333
ccc  : 5555

可以透過這樣的命令讓檔案變成:
aaaaa:12345
bbbb:33333
ccc:5555

cat file | sed 's/ //g'

利用awk找出檔案內重複的紀錄

假如我的檔案內容如下:

account1:passwd1
account2:passwd2
account3:passwd3
account2:passwd4
account4:passwd5
account6:passwd6
account1:passwd7

可以看到 account1 與 account2 都有兩筆紀錄, 可以透過
這樣的 awk 命令找出這兩筆紀錄 :   (請注意第二個欄位的 passwd
不同喔)

awk -F: 'x[$1]++ {print $1 " is duplicated"}' filename

似乎: uniq -d 也可達到這個目的. 剛剛才發現


將檔案內的大寫字串轉換成小寫字串 (檔案內大小寫字串轉換)
比如說我有一個檔案內容如下 :
A123456
HSU
Lee
g567
12345
768ABC
我可以透過一行命令, 就可以把檔案內大寫字母, 轉換成小寫字母:
awk '{print(tolower($0))}' filename  
上面命令會產生 STDOUT :
a123456
hsu
lee
g567
12345
768abc
再利用導向方式接起來就可以了
那如果要轉換成大寫字母呢? 一樣的命令, 只是改成:
awk '{print(toupper($0))}' filename


檔案內容比對 (找出檔案內容差異)
常見的應用比如說, 公司本身有LDAP server, 而email server
的帳號是從LDAP server同步過來的, 我們就可以利用這個檔案內容
比對的方式, 找出差異的帳號, 也就是需要新增 or 刪除的部份:

不討論取得ldap server與email server帳號的命令, 不過取出帳號
的時候, 請特別注意, 強烈建議要先做過排序, 可以透過 sort 命令,
直接舉例如下:

ldap清單(file1):
aaaa@example.com
bbbb@example.com
cccc@example.com
dddd@example.com
xxxx@example.com
yyyy@example.com

email server清單(file2):
aaaa@example.com
cccc@example.com
dddd@example.com
xxxx@example.com

兩個檔案, 很清楚下面檔案少了 bbb@example.com 與 yyy@example.com
我們可以用命令來達成 :

diff -u file1 file2  (請注意 file1 file2 的順序會影響命令輸出)

--- 3   2009-09-02 09:22:37.000000000 +0800
+++ 4   2009-09-02 09:22:44.000000000 +0800
@@ -1,6 +1,4 @@
 aaaa@example.com
-bbbb@example.com
 cccc@example.com
 dddd@example.com
 xxxx@example.com
-yyyy@example.com

所以 :   diff -u file1 file2 | grep "@example.com" | grep "^-"
就可以取出:
-bbbb@example.com
-yyyy@example.com

接著再透過 sed s/^-//g 的命令即可完整取出新增的部份.

而刪除的部份呢?  一樣可透過 diff 命令, 只要把檔案順序互換,
同時去 grep "^+" 的部份就可以了.

較完整的script範例應該大致如下:

#!/bin/bash

LDAP_TMP=/tmp/ldap.list
MAIL_TMP=/tmp/mail.list
DIFF_TMP=/tmp/diff.list
DOMAIN="@example.com"

# get ldap server account list
ldapsearch xxxxxx > $LDAP_TMP

# get mail server account list
xxxxxx > $MAIL_TMP

diff -u $LDAP_TMP $MAIL_TMP | grep $DOMAIN | grep "^-" | sed s/^-// > $DIFF_TMP

cat $DIFF_TMP |
while read account; do
  echo -n "add new account : $account  "

  # run the actual command below
  XXXXX
 
  if [ $? -eq 0 ]; then
     echo "OK"
  else
     echo "Fail"
  fi
done

此外, 除了 diff , 還有另外一個 comm 也很好用,
例如:
# cat 3
aaaa@example.com
bbbb@example.com
cccc@example.com
dddd@example.com
xxxx@example.com
yyyy@example.com

# cat 4
aaaa@example.com
cccc@example.com
dddd@example.com
xxxx@example.com

# comm -2 -3 3 4       取得後者檔案內容沒有的部分
bbbb@example.com
yyyy@example.com

還有 comm -1 -3 , comm -1 -2 的應用, 請自行參考comm manual page

 

 


版權說明

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

本份學習講義, 是根據Caldera System出版的LINUX SHELL SCRIPTING所
整理而成的文件, 這份講義, 內容除了本身所提供外, 很多範例與說明都是
由該本書擷取出來, 因此我覺得我只是整理出來. 雖然有加上我自己的一些
經驗與自己寫的一些 script, 不過都蠻粗糙的.

許景超 - Last update : 2009/08/06 - tiger2000@gmail.com

 

 

http://www.mgt.ncu.edu.tw/~dino/script/

原创粉丝点击