本文的主要內容: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 = aArgument 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》