前言
底下的教學與心得分享是假設你已經有著基本的UNIX or Linux觀念 與技巧, 同時我們利用Linux預設提供的bash shell來操作, 例如 :
$ 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
跟以往的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
利用( ) 來整合命令, 例如 : $ 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 $
直接在命令提示符號下 : 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
$ 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 那個只是一個識別, 也就是說, 你可以用其他字元代替, 不過還是建議用單字或者字串做為識別, 而不要用符號
$ 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 (列印出 結果 並結束 ) $
$ 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" $
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 script的判斷, 就是說 我們會利用到if-then-else的一些判斷, 來讓shell script具備判斷能力. 討論的主題預計包括 :
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 ....case 敘述
[ 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 敘述與 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
迴圈的宣告基本上所有的語言都是類似, 也就是都靠
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
這部份要先偷懶一下, 因為之前已經寫過一些 sed 的簡易說明, 這裡直接把它拿過來, 同時加上一些補充.
(未完成)
這些部分都是我自己寫的例子, 可能不完全正確, 或者說 還有很多可以改進的地方, 如果有任何建議, 請給我email. 還有一些部份是直接從網路上收集的, 挑幾個作一些參考. 另外, 若你有什麼好的script, 願意一起放進來, 我都很歡迎喔.:)
本份學習講義, 是根據Caldera System出版的LINUX SHELL SCRIPTING所 整理而成的文件, 這份講義, 內容除了本身所提供外, 很多範例與說明都是 由該本書擷取出來, 因此我覺得我只是整理出來. 雖然有加上我自己的一些 經驗與自己寫的一些 script, 但是為了尊重原本製作的公司與個人. 所以我只擁有文件整理權. 許景超 - Last update : 2005/11/11 -tiger2000@gmail.com
来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/82392/viewspace-175136/ ,如需转载,请注明出处,否则将追究法律责任。