1.背景
Linux Shell是一種基本功,由於怪異的語法加之較差的可讀性,通常被python等腳本代替。既然是基本功,那就需要掌握,畢竟學習Shell腳本的過程中,還是能了解到很多Linux系統的內容。Linux腳本大師不是人人都可以達到的,但是用一些簡單的Shell實現一些常見的基本功能還是很有必要的。
2.正題
1) 熱身
下面的例子展示了如何向腳本傳遞參數、腳本如何獲取參數、if-else判斷、變量的使用等基本內容。
#!/bin/bash
if [[ $# -lt 1 ]]; then
echo "args count must > 1"
echo "Uage: bash +x example01.sh [args...]"
exit
fi
arg=$1
if [[ $arg -gt 10 ]]; then
echo "$arg > 10"
else
echo "$arg < 10"
fi
這個腳本的調用方式如下:
bash +x example01.sh 52).數組、函數傳參數,循環
下面的例子展示了數組、函數、循環等基本使用。
#!/bin/bash
if [[ $# -lt 1 ]]; then
echo "args count must > 1"
echo "Uage: bash +x example01.sh [args...]"
exit
fi
args=$@
for arg in $args; do
echo $arg
done
function fun() {
echo $1
}
fun "hello shell"
fun2() {
echo "Linux"
}
fun2注意,函數fun中的$1,獲取的是函數參數,不是腳本調用時傳入的參數。$@ 是獲取腳本調用時傳入的參數列表。
3).while 循環以及其他幾種循環、case、表達式expr的使用
#!/bin/bash
if [[ $# -lt 1 ]]; then
echo "args count must > 1"
echo "Uage: bash +x example01.sh [args...]"
exit
fi
case $1 in
"install" )
echo "operation type is install"
;;
"uninstall" )
echo "operation type is uninstall"
;;
* )
echo "operation type is not support"
;;
esac
for ((i=0;i<3;i++))
do
if ((i==1))
then
continue
fi
echo $i
done
for i in `seq 5`
do
echo "loop $i"
done
注意這裡的case * 並不是所有,而是輸入值不在case中,相當於default. 在循環中可以使用continue/break等關鍵字,非常類似java等其他語言的循環。
4).腳本之間互相引用
通過source 或者 . 的方式可以引用另一個腳本中的函數或者變量
first.sh
function fun(){
echo "i am from first."
}
file=firstsecond.sh
. first.sh
fun
echo $file
使用bash +x second.sh執行,在second.sh 中可以調用fun函數和使用file變量。
這裡的.和source都可以實現引用first文件中的變量。注意: 如果同時引用了多個腳本的同一個變量名的變量,後面的值會覆蓋前面的變量而不會報錯。
5).關於錯誤處理
a)在shell中有一個變量 $? ,這個變量記錄的是上次腳本執行的結果,如果正常結束則是0,否則是非0值;
b)如果在shell腳本中通過set -o errexit來實現遇到錯誤就退出,這樣能夠避免產生更多的錯誤;
c)在shell執行過程中如果出錯,可以通過重定向的方式,輸出到文件中,比如Command >> filename2>&1
6).字典
shell中的字典是非常好的數據結構,能夠很方便地處理配置
#!/bin/bash
set -o errexit
hput(){
eval "hkey_$1"="$2"
}
hget(){
eval echo '${'"hkey_$1"'}'
}
hput k1 v1
hget k1
declare -A dic
dic=([key1]="value1" [key2]="value2" [key3]="value3")
echo ${dic["key1"]}
# output all key
echo ${!dic[*]}
#outpull all value
echo ${dic[*]}
# access all
for key in $(echo ${!dic[*]})
do
echo "$key : ${dic[$key]}"
done
執行之後,輸出如下:
v1
value1
key3 key2 key1
value3 value2 value1
key3 : value3
key2 : value2
key1 : value1
7).文本處理
sed 命令能夠對對文本進行操作。
比如有一個文件sedfile,內容如下:
1
2
3
4
5執行 "sed '1,3d' sedfile,則會輸出4,5 兩行,即對1,2,3行做了刪除處理,注意這時文件裡面並沒有刪掉這兩行。
除了刪除之外,還可以做替換操作。
root@Ubuntu:~/codelab# sed 's/1/0/g' sedfile
0
2
3
4
5
root@ubuntu:~/codelab# cat sedfile
1
2
3
4
5我們發現,在輸出時,將1替換成了0;
文本處理還有一個非常強大的工具-awk
我們首先看看awk的基本處理-按照逗號(,)分割獲取想要的文本;
root@ubuntu:~/codelab# cat awkfile
this,is,a,awk,file
this,is,a,awk,file
root@ubuntu:~/codelab# cat awkfile | awk -F ',' '{print $0}'
this,is,a,awk,file
this,is,a,awk,file
root@ubuntu:~/codelab# cat awkfile | awk -F ',' '{print $1}'
this
this
root@ubuntu:~/codelab#
這裡我們可以看到awk可以按照任意形式分割文本,然後輸出;
8).特殊變量和值
下列變量通常具有固定意義
$# 表示變量的個數,常用於循環
$@ 當前命令行所有參數。置於雙引號中,表示個別參數
$* 當前命令行所有參數。置於雙引號中,表示將命令行所有參數當初一個單獨參數
$-(連字號) 在引用數給予Shell的選項
$? 表示上一個命令退出的狀態
$$ 表示當前進程編號
$0 表示當前程序名稱
$! 表示最近一個後台命令的進程編號
$HOME 表示當前用戶根目錄
$IFS 表示內部的字段分隔符
$LANG 當前locale默認名稱
$PATH 環境變量
$PPID 父進程編號
$PWD 當前工作目錄下列特殊值可以幫助排查問題
0 成功退出
>0 退出失敗
1-125 命令退出失敗,失敗返回的相關值由程序定義(譬如,程序內退出只執行 exit 2,則返回為2)
126 命令找到了,但無法執行
127 命令找不到
>128 命令因受到信號而死亡9.定時任務
windows系統下有定時任務,在linux系統上也一樣有定時任務。使用系統自帶的crontab即可實現;
我的系統上執行 cat /etc/crontab的輸出如下:
root@ubuntu:~/codelab# cat /etc/crontab
# /etc/crontab: system-wide crontab
# Unlike any other crontab you don't have to run the `crontab'
# command to install the new version when you edit this file
# and files in /etc/cron.d. These files also have username fields,
# that none of the other crontabs do.
SHELL=/bin/sh
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
# m h dom mon dow user command
17 * * * * root cd / && run-parts --report /etc/cron.hourly
25 6 * * * root test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.daily )
47 6 * * 7 root test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.weekly )
52 6 1 * * root test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.monthly )
#
從這裡我們可以看出cron是如何使用的,這裡設定了cron使用shell,下面是命令的設置方式。
minute: 表示分鐘,可以是從0到59之間的任何整數。
hour:表示小時,可以是從0到23之間的任何整數。
day:表示日期,可以是從1到31之間的任何整數。
month:表示月份,可以是從1到12之間的任何整數。
week:表示星期幾,可以是從0到7之間的任何整數,這裡的0或7代表星期日。
command:要執行的命令,可以是系統命令,也可以是自己編寫的腳本文件。我們可以使用如下一些基礎命令;
usage: crontab [-u user] file
crontab [ -u user ] [ -i ] { -e | -l | -r }
(default operation is replace, per 1003.2)
-e (edit user's crontab)
-l (list user's crontab)
-r (delete user's crontab)
-i (prompt before deleting user's crontab)10).技巧
a)判斷參數是否是數字。
#!/bin/sh
#1. $1是腳本的第一個參數,這裡作為awk命令的第一個參數傳入給awk命令。
#2. 由於沒有輸入文件作為輸入流,因此這裡只是在BEGIN塊中完成。
#3. 在awk中ARGV數組表示awk命令的參數數組,ARGV[0]表示命令本身,ARGV[1]表示第一個參數。
#4. match是awk的內置函數,返回值為匹配的正則表達式在字符串中(ARGV[1])的起始位置,沒有找到返回0。
#5. 正則表達式的寫法已經保證了匹配的字符串一定是十進制的正整數,如需要浮點數或負數,僅需修改正則即可。
#6. awk執行完成後將結果返回給isdigit變量,並作為其初始化值。
#7. isdigit=`echo $1 | awk '{ if (match($1, "^[0-9]+$") != 0) print "true"; else print "false" }' `
#8. 上面的寫法也能實現該功能,但是由於有多個進程參與,因此效率低於下面的寫法。
isdigit=`awk 'BEGIN { if (match(ARGV[1],"^[0-9]+$") != 0) print "true"; else print "false" }' $1`
if [[ $isdigit == "true" ]]; then
echo "This is numeric variable."
number=$1fib) 根據系統給變量賦值,確保可移植性
#1. 通過uname命令獲取當前的系統名稱,之後再根據OS名稱的不同,給PING變量賦值不同的ping命令的全稱。
osname=`uname -s`
#2. 可以在case的條件中添加更多的操作系統名稱。
case $osname in
"Linux")
PING=/usr/sbin/ping ;;
"FreeBSD")
PING=/sbin/ping ;;
"SunOS")
PING=/usr/sbin/ping ;;
*)
;;
esac
3.總結
這些只是shell開發中的最基礎部分,shell 其實是一種非常高效、強大的語言,不斷學習和積累一定會越來越熟練。