本文的主要內容:1、流程控制:while/until 循環
2、流程控制:case 分支3、流程控制:for 循環
4、位置參數一、流程控制:while/until 循環
1.while,當遇到一個非零退出狀態的時候while 退出循環while語法:while commands; do commands; done
例如(循環輸出1-5):
#!/bin/bash # while-count: display a series of numbers count=1 while [ $count -le 5 ]; do echo $count count=$((count + 1)) done echo "Finished."2.跳出循環
break 命令立即終止一個循環, 且程序繼續執行循環之後的語句。continue 命令導致程序跳過循環中剩余的語句,且程序繼續執行 下一次循環
菜單例子:
#!/bin/bash # while-menu2: a menu driven system information program DELAY=3 # Number of seconds to display results while true; do clear cat <<- _EOF_ Please Select: 1. Display System Information 2. Display Disk Space 3. Display Home Space Utilization 0. Quit _EOF_ read -p "Enter selection [0-3] > " if [[ $REPLY =~ ^[0-3]$ ]]; then if [[ $REPLY == 1 ]]; then echo "Hostname: $HOSTNAME" uptime sleep $DELAY continue fi if [[ $REPLY == 2 ]]; then df -h sleep $DELAY continue fi if [[ $REPLY == 3 ]]; then if [[ $(id -u) -eq 0 ]]; then echo "Home Space Utilization (All Users)" du -sh /home/* else echo "Home Space Utilization ($USER)" du -sh $HOME fi sleep $DELAY continue fi if [[ $REPLY == 0 ]]; then break fi else echo "Invalid entry." sleep $DELAY fi done echo "Program terminated."3.until,當遇到一個0退出狀態的時候until退出循環
例如(循環輸出1-5):
#!/bin/bash # until-count: display a series of numbers count=1 until [ $count -gt 5 ]; do echo $count count=$((count + 1)) done echo "Finished."4.使用循環讀取文件(其中文件distros.txt中的每一行包含三個字段:distro version release)
重定向文件到循環中
#!/bin/bash # while-read: read lines from a file while read distro version release; do printf "Distro: %s\tVersion: %s\tReleased: %s\n" \ $distro \ $version \ $release done < distros.txt從標准輸入管道到循環中
#!/bin/bash # while-read2: read lines from a file sort -k 1,1 -k 2n distros.txt | while read distro version release; do printf "Distro: %s\tVersion: %s\tReleased: %s\n" \ $distro \ $version \ $release done二、流程控制:case 分支
1.case語句Bash 的多選復合命令稱為 case,其語法規則如下:
case word in
[pattern [| pattern]...) commands ;;]...
esac
使用case的菜單
#!/bin/bash # case-menu: a menu driven system information program clear echo " Please Select: 1. Display System Information 2. Display Disk Space 3. Display Home Space Utilization 0. Quit " read -p "Enter selection [0-3] > " case $REPLY in 0) echo "Program terminated." exit ;; 1) echo "Hostname: $HOSTNAME" uptime ;; 2) df -h ;; 3) if [[ $(id -u) -eq 0 ]]; then echo "Home Space Utilization (All Users)" du -sh /home/* else echo "Home Space Utilization ($USER)" du -sh $HOME fi ;; *) echo "Invalid entry" >&2 exit 1 ;; esac2.模式
case 語句使用的模式和路徑展開中使用的相同。模式以一個 “)” 為終止符。
常用的有效模式:
a):若單詞為 “a”,則匹配
[[:alpha:]]):若單詞是一個字母字符,則匹配
???):若單詞只有3個字符,則匹配
*.txt):若單詞以 “.txt” 字符結尾,則匹配
*):匹配任意單詞。可將此模式做為 case 命令的最後一個模式,以捕捉到任何可能的無效值。
模式例子:
#!/bin/bash read -p "enter word > " case $REPLY in [[:alpha:]]) echo "is a single alphabetic character." ;; [ABC][0-9]) echo "is A, B, or C followed by a digit." ;; ???) echo "is three characters long." ;; *.txt) echo "is a word ending in '.txt'" ;; *) echo "is something else." ;; esac還可以使用豎線字符作為分隔符,把多個模式結合起來形成一個 “或”條件模式,如" q|Q) "可匹配大寫或小寫的'q'
3.匹配多個測試條件以執行多個動作添加“;;&” 的語法允許 case 語句繼續執行下一條測試,而不是簡單地終止運行
例子:
#!/bin/bash # case4-2: test a character read -n 1 -p "Type a character > " echo case $REPLY in [[:upper:]]) echo "'$REPLY' is upper case." ;;& [[:lower:]]) echo "'$REPLY' is lower case." ;;& [[:alpha:]]) echo "'$REPLY' is alphabetic." ;;& [[:digit:]]) echo "'$REPLY' is a digit." ;;& [[:graph:]]) echo "'$REPLY' is a visible character." ;;& [[:punct:]]) echo "'$REPLY' is a punctuation symbol." ;;& [[:space:]]) echo "'$REPLY' is a whitespace character." ;;& [[:xdigit:]]) echo "'$REPLY' is a hexadecimal digit." ;;& esac當輸入'a'時能匹配[[:lower:]]、[[:alpha:]]、[[:graph:]]和[[:xdigit:]]並顯示相應輸出
三、流程控制:for 循環1.for: 傳統 shell 格式
for variable [in words]; do
commands
done
例如:
$ for i in A B C D; do echo $i; done
A
B
C
D
或者通過花括號展開:
$ for i in {A..D}; do echo $i; done
A
B
C
D
或者路徑名展開:
$ for i in distros*.txt; do echo $i; done
distros-by-date.txt
distros-dates.txt
distros-key-names.txt
distros-names.txt
distros.txt
或者命令替換:
#!/bin/bash # longest-word : find longest string in a file while [[ -n $1 ]]; do if [[ -r $1 ]]; then max_word= max_len=0 for i in $(strings $1); do len=$(echo $i | wc -c) if (( len > max_len )); then max_len=$len max_word=$i fi done echo "$1: '$max_word' ($max_len characters)" fi shift done2.for: C 語言格式
for (( expression1; expression2; expression3 )); do
commands
done
在行為方面,這相當於以下構造形式:
(( expression1 ))
while (( expression2 )); do
commands
(( expression3 ))
done
例如輸出0-4:
#!/bin/bash # simple_counter : demo of C style for command for (( i=0; i<5; i=i+1 )); do echo $i done四、位置參數
1.訪問命令行shell 提供了一個稱為位置參數的變量集合,這個集合包含了命令行中所有獨立的單詞。這些變量按照從0到9給予命名
例如
#!/bin/bash # posit-param: script to view command line parameters echo " \$0 = $0 \$1 = $1 \$2 = $2 "當執行$ posit-param時運行結果:
$0 = /home/me/bin/posit-param
$1 =
$2 =
其中位置參數 $0 為執行程序的路徑名
訪問大於9的參數時,用花括號把該數字括起來即可。 例如 ${10}, ${666} ...
2.確定參數個數shell 提供了一個名為 $#的變量,可以得到命令行參數個數
例如:
#!/bin/bash # posit-param: script to view command line parameters echo " Number of arguments: $# \$0 = $0 \$1 = $1 \$2 = $2 "當執行$ posit-param a b 時運行結果為Number of arguments: 2
$0 = /home/me/bin/posit-param
$1 = a
$2 = b
3.shift - 訪問多個參數的利器執行一次 shift 命令, 就會使所有的位置參數“向下移動一個位置”
例如:
#!/bin/bash # posit-param2: script to display all arguments count=1 while [[ $# -gt 0 ]]; do echo "Argument $count = $1" count=$((count + 1)) shift done當執行$ posit-param2 a b c時運行結果為:Argument 1 = a
Argument 2 = b
Argument 3 = c
每次 shift 命令執行的時候,變量 $2 的值會移動到變量 $1 中,變量 $3 的值會移動到變量 $2 中,依次類推。 變量 $# 的值也會相應的減1。
4.簡單應用顯示一個具體文件的文件類型(由 file 命令確定)和文件狀態(來自 stat 命令)
#!/bin/bash # file_info: simple file information program PROGNAME=$(basename $0) if [[ -e $1 ]]; then echo -e "\nFile Type:" file $1 echo -e "\nFile Status:" stat $1 else echo "$PROGNAME: usage: $PROGNAME file" >&2 exit 1 fi其中basename 命令清除 一個路徑名的開頭部分,只留下一個文件的基本名稱
5.Shell 函數中使用位置參數例如:
file_info () { # file_info: function to display file information if [[ -e $1 ]]; then echo -e "\nFile Type:" file $1 echo -e "\nFile Status:" stat $1 else echo "$FUNCNAME: usage: $FUNCNAME file" >&2 return 1 fi }6.處理集體位置參數
shell 提供了兩種特殊的參數:
$*:展開成一個從1開始的位置參數列表。當它被用雙引號引起來的時候,展開成一個由雙引號引起來 的字符串,包含了所有的位置參數,每個位置參數由 shell 變量 IFS 的第一個字符(默認為一個空格)分隔開。
$@:展開成一個從1開始的位置參數列表。當它被用雙引號引起來的時候, 它把每一個位置參數展開成一個由雙引號引起來的分開的字符串。
例如:
#!/bin/bash # posit-params3 : script to demonstrate $* and $@ print_params () { echo "\$1 = $1" echo "\$2 = $2" echo "\$3 = $3" echo "\$4 = $4" } pass_params () { echo -e "\n" '$* :'; print_params $* echo -e "\n" '"$*" :'; print_params "$*" echo -e "\n" '$@ :'; print_params $@ echo -e "\n" '"$@" :'; print_params "$@" } pass_params "word" "words with spaces"運行結果為:
$ posit-param3
$* :
$1 = word
$2 = words
$3 = with
$4 = spaces
"$*" :
$1 = word words with spaces
$2 =
$3 =
$4 =
$@ :
$1 = word
$2 = words
$3 = with
$4 = spaces
"$@" :
$1 = word
$2 = words with spaces
$3 =
$4 =
其中通過參數,$* 和 $@ 兩個都產生了一個有四個詞的結果:
"$*" 產生了一個串:"word words with spaces"
"$@" 產生了兩個串:"word" "words with spaces"
目前看來“$@” 在大多數情況下是最有用的方法,因為它保留了每一個位置參數的完整性。
7.實例:輸出一個包含系統信息的網頁
#!/bin/bash # sys_info_page: program to output a system information page PROGNAME=$(basename $0) TITLE="System Information Report For $HOSTNAME" CURRENT_TIME=$(date +"%x %r %Z") TIMESTAMP="Generated $CURRENT_TIME, by $USER" report_uptime () { cat <<- _EOF_ <H2>System Uptime</H2> <PRE>$(uptime)</PRE> _EOF_ return } report_disk_space () { cat <<- _EOF_ <H2>Disk Space Utilization</H2> <PRE>$(df -h)</PRE> _EOF_ return } report_home_space () { if [[ $(id -u) -eq 0 ]]; then cat <<- _EOF_ <H2>Home Space Utilization (All Users)</H2> <PRE>$(du -sh /home/*)</PRE> _EOF_ else cat <<- _EOF_ <H2>Home Space Utilization ($USER)</H2> <PRE>$(du -sh $HOME)</PRE> _EOF_ fi return } usage () { echo "$PROGNAME: usage: $PROGNAME [-f file | -i]" return } write_html_page () { cat <<- _EOF_ <HTML> <HEAD> <TITLE>$TITLE</TITLE> </HEAD> <BODY> <H1>$TITLE</H1> <P>$TIMESTAMP</P> $(report_uptime) $(report_disk_space) $(report_home_space) </BODY> </HTML> _EOF_ return } # process command line options interactive= filename= while [[ -n $1 ]]; do case $1 in -f | --file) shift filename=$1 ;; -i | --interactive) interactive=1 ;; -h | --help) usage exit ;; *) usage >&2 exit 1 ;; esac shift done # interactive mode if [[ -n $interactive ]]; then while true; do read -p "Enter name of output file: " filename if [[ -e $filename ]]; then read -p "'$filename' exists. Overwrite? [y/n/q] > " case $REPLY in Y|y) break ;; Q|q) echo "Program terminated." exit ;; *) continue ;; esac fi done fi # output html page if [[ -n $filename ]]; then if touch $filename && [[ -f $filename ]]; then write_html_page > $filename else echo "$PROGNAME: Cannot write file '$filename'" >&2 exit 1 fi else write_html_page fi
參考:《The Linux Command Line》