Linux系統——shell指令碼
shell指令碼程式設計
作用:通過命令列解析的方式,自動執行設定好的程式或命令程式碼。(若將指令碼掛到定時任務中,就會自動在非工作時間裡自動觸發執行程式)
Shell指令碼檔案以“.sh”結尾
規範的Shell指令碼第一行會指出由哪個程式(直譯器)來執行指令碼中的內容。在linux bash程式設計中一般為:#!/bin/bash (表示該指令碼運用/bin/bash命令進行解析)
Shell的輸出用echo命令;
Python的輸出用print命令
執行指令碼的方式:
方法一:/bin/sh是
# sh yunjisuan.sh
# bash yunjisuan.sh
方法二:以絕對路徑的方式執行指令碼(前提:將該指令碼新增x執行許可權 )
# chmod +x yunjisuan.sh
# /root/benet/yunjisuan.sh
方法三:以source指令碼路徑的方式執行指令碼(等同於“.指令碼路徑” )只在當前環境生效
# source yunjisuan.sh
# . yunjisuan.sh
方法四:以相對路徑的方式執行指令碼(前提:將該指令碼新增
# ./yunjisuan.sh
寫指令碼
(1)vim編輯yunjisuan.sh
(2)檢視yunjisuan.sh指令碼
(3)執行yunjisuan.sh指令碼
指令碼中書寫的命令必須是非互動式的命令!!!
雖然指令碼可以自動化執行,但指令碼不會判斷命令是否執行成功,因此需要進行邏輯判斷
方法一:
test命令
判斷該字串表示為檔案還是目錄
-d 測試是否為目錄
-e 測試目錄或檔案是否存在
-f 測試是否為檔案
-l 測試是否為連結檔案
-r 測試當前使用者是否可讀
-w 測試當前使用者是否可寫
-x 測試當前使用者是否可執行
-s 測試檔案大小非0時為真(若為非空檔案,則為真)
-u 測試是否具有SUID屬性
-g 測試是否具有SGID屬性
-k 測試是否具有sticky bit 粘滯位屬性
-z 測試字串是否為空(zero)
file1 -nt file2測試第一個檔案是否比第二個檔案新(new then)
file1 -ot file2 測試第一個檔案是否比第二個檔案舊(old then)
file1 -ef file2 測試第一個檔案是否與第二個檔案為同一個檔案(link之類的檔案)
# test -d 目標路徑
# echo $?判斷是否是目錄,若顯示0表示真(是目錄),若顯示非0則假(不是目錄)
# test -f 目標路徑
判斷目標是不是檔案
# echo $?
$?返回值參考
0 表示執行成功
2 許可權拒絕
1~125 表示執行失敗,指令碼命令,系統命令錯誤或引數傳遞錯誤;
126 找到該命令,但無法執行
127 未找到要執行的命令
128 命令被系統強制結束
方法二:
使用“[]”,表示開啟判斷條件(“[]”兩邊須有空格)
# xx=”welcome”
# [ $xx == welcome] && echo “0” || echo “1”
0
“[]”應用於整數 (格式:整數1 操作符 整數2)
-gt(great than)表示大於
-lt(less than)表示小於
-eq(equal)表示等於
-ne (not equal)表示不等於
-ge 表示大於等於
-le表示小於等於
&& 邏輯與,表示前一指令為真,執行後一指令,否則不執行
|| 邏輯或,表示前一指令為假,執行後一指令,否則不執行
# xx=”44”
# if [ -f "$file1" ];then echo 1;else echo 0;fi
相當於# [ -f "file1" ] && echo 1 ||echo 0
# [ $xx -eq 34 ] && echo “0” || echo “1”
1
字串測試
[ -z “字串” ] 字串內容為空
[ -n “字串” ] 字串內容不為空
${#變數名} 求變數的字串位數,判斷字串是否為空
# xx=”123411”
# echo ${#xx}
6
# xx=””
# echo ${#xx}
0
指令碼的互動式輸出模式
方法一:
執行指令碼過程中,存在需要使用者輸入內容的情況,通過read命令進行互動式輸入
添加註釋給使用者:
輸入格式:read -p “文字提示” 變數名
輸0出格式:echo $變數名
建立使用者名稱和密碼
#!/bin/bash
User="yunjisuan"
Passwd="123456"
read -p "請輸入使用者名稱" user
read -p "請輸入密碼" passwd
[ $User -eq $user ] && echo "YOU ARE RIGHT" || echo “YOU ARE WRONG”
[ $Passwd -eq $user ] && echo "YOU ARE RIGHT" || echo "YOU ARE WRONG"
邏輯測試
“[]”中,-a(&&) 表示並且;
-o (||)表示或者
!邏輯否
方法二:通過引數傳遞的方式進行互動式輸入
/etc/init.d/ 服務啟動指令碼
服務啟動指令碼/etc/init.d/後面寫的內容就是引數,通過這個引數進行互動式輸入
$# 表示指令碼要處理的的引數個數
$? 表示命令或指令碼執行狀態碼
$* 表示橫向羅列指令碼要處理的所有引數(把所有引數視為整體)
[email protected] 表示橫向羅列指令碼要處理的所有引數(把所有引數視為單個引數的組合)
$0 表示指令碼檔案的絕對路徑或相對路徑(指令碼檔案的執行路徑)
$1 第一個引數
$n 第n個引數
指令碼中反引號“``”、“$()”的應用
變數的算數運算
變數的數值運算多用於指令碼程式的過程控制,只能進行簡單的整數運算,不支援小數運算,整數值得運算主要通過內部命令expr進行。
# x=11
# y=22
# expr $x \* $y
格式: expr 變數1 運算子 變數2...
++ -- 增加及減少,可前置也可放在結尾
! ~ 一元運算的正負號,非,邏輯與位的取反
\* 乘法
/ 除法
% 取餘
** 冪運算
+ 加法
- 減法
<; <=;>; >= 比較符號
== 1+= 相等,不相等
<< 向左移動
>> 向右移動
>>> 填0右移
& 位的與AND
^ 位的異或
| 位的或
&& 位的AND
|| 位的OR
?: 條件表示式
=; += ;-= ;*=等 賦值運算子
(())用法(常用於簡單的整數運算)
“(())”在命令列執行時不需要$符號,但是輸出需要$符號
“(())”裡所有字元之間有無或多個空格沒有任何影響
# b=$((1+2**3-4%3))
# echo $b
8
# echo $((1+2**3-4%3))
8
a++,a--,++a,--a區別
變數a在前,表示式的值為a,然後a自增或自減,變數a在符號後,表示式值自增或自減,然後a值自增或自減。
# a=8
# echo $a
8(a賦值為8)
# echo $((a+=1)) #相當於a=a+1
9(a賦值為a+1=9)
# echo $((a++)) #a在前,先輸出a的值,在加1
9(a賦值為上一值a為9)
# echo $a
10(a賦值上一值的9++,9+1為10)
# echo $((a--))
10(a取上一值a為10)
# echo $a
9(a取上一值10--,10-1為9)
# echo $((++a))
10(先+1,在取上一值9,1+9=10)
# echo $a
10(取上一值10)
# echo $((--a))
9(先-1,取上一值10,10-1=9)
# echo $a
9(取上一值9)
Shell指令碼中不支援“i++”表達,可以用C語言的((i++))在shell中表示
或者“let h++”
If條件語句
格式(fi 表示結束)
(一)單分支條件判斷語句
(1)
if 條件1
then
動作1
else
動作2
fi
(2)(用;分隔)
if 條件1;then
動作1
else
動作2
fi
在vim中編輯yunjisuan.sh指令碼
#!/bin/bash
read -p "請輸入一個數字:" num
if [ $num == 60 ];then
echo "猜對了"
fi
# sh yunjisuan.sh
請輸入一個數字:60
猜對了
在vim中編輯yunjisuan.sh指令碼
#!/bin/bash
read -p "請輸入一個數字:" num
if [ $num == 60 ];then
echo "猜對了"
else
echo "猜錯了"
fi
# sh yunjisuan.sh
請輸入一個數字:45
猜錯了
(二)多分支條件判斷語句(elif就是else if)
if 條件1;then
動作1
elif 條件2;then
動作2
else
動作3
fi
檢視當前檔案的絕對目錄
# dirname 目標檔案的絕對路徑
檢視當前檔案的基本檔名稱
# basename 目標檔案的絕對路徑
自定義搭建本地yum倉庫指令碼
(1)移除光碟,再掛載
(2)判斷光碟掛載
(3)掛載本地yum倉庫指令碼
(4)判斷一級目錄、二級目錄是否存在
(5)配置檔案手動生成,避免之前被修改過(重定向或echo -e)
#!/bin/bash
umount /dev/sr0&>/dev/null
[ -d /media/cdrom ]|| mkdir -p /media/cdrom(還可以用test和if的方法)
mount /dev/sr0 /media/cdrom &>/dev/null
if [ $? -ne 0 ];then
echo "請插入光碟"
exit
fi
[ -d /etc/yum.repos.d ] || mkdir -p /etc/yum.repos.d
cd /etc/yum.repos.d
mv * /tmp/
echo -e “[local]\nname=local\nbaseurl=file:///media/cdrom/\ngpgcheck=0\nenabled=1”>/etc/
yum.repos.d/localyum.repo
(或)
cat > /etc/yum.repos.d/localyum.repo << FOF
[local]
name=local
baseurl=file:///media/cdrom
gpgcheck=0
enabled=1
F0F
yum -y clean all &>/dev/null
yum makecache &>/dev/null
[ $? -eq 0 ] && echo "yum倉庫搭建完畢" || echo "快取建立失敗!"
配置檔案手動生成
用指令碼手動生成配置檔案,一般用重定向
輸入重定向通過FOF標識(任意定義,但成對出現),將FOF之間的內容輸入重定向到cat,cat再輸出重定向給/etc/yum.repos.d/yum/repo檔案
while迴圈語句
格式:
while 條件
do
迴圈體(指令)
done
休息命令:sleep 1 休息1秒,usleep 1000000 休息1秒,單位微妙
從1到100求和
#!/bin/bash
i=1
sum=0
while [ $i -lt 100 ]
do
((sum=sum+i))
((i++))
done
echo $sum
守護程序
#!/bin/bash
while true
do
uptime >> /var/log/uptime.log
sleep 2
done
倒計時
#!/bin/bash
i=10
while [ $i -gt 0 ]
do
echo $i
((i--))
done
無限迴圈
(1)
#!/bin/bash
read -p "輸入:" i
while [ $i -gt 0 ]
do
echo $i
let i++
done
(2)
#!/bin/bash
read -p "輸入:" i
while :
do
echo $i
let i++
done
強行中止
# exit
防止指令碼執行中斷的方法
1)sh while01.sh & #放在後臺執行
2)screen 分離 ctrl+a+d 檢視screen -ls進入screen -r num
3)nohup while01.sh &
for迴圈語句
格式:
for 變數名 in 變數取值列表
do
迴圈體(指令)
done
示例:迴圈
for ((i=0;i<10:i++))
do
echo $i
done
列印列表元素
for的三種輸出方式
(1)
#!/bin/bash
for i in 1 2 3 4 5
do
echo $i
done
(2)
#!/bin/bash
for i in {1..5}
do
echo $i
done
(3)
#!/bin/bash
for i in `seq 5`
do
echo $i
done
#!/bin/bash
h=0
for i in {1..10}
do
echo $h
let h++
done
開機啟動項優化
#!/bin/bash
for i in `chkconfig | grep "3:on" | awk '{print $1}'`
do
chkconfig $i off
done
if [ -e sysstat ]
then echo "sysstat exit"
else mkdir -p /media/cdrom
mount /dev/sr0 /media/cdrom
if [ $? -ne 0 ];then
echo "FAILED"
exit
else yum -y install sysstat
if [ $? -ne 0 ];then
echo "install error"
exit
else echo "install successed"
fi
fi
fi
for h in sshd network crond rsyslog sysstat
do
chkconfig $h on
done
[ $? == 0 ]&& echo "successed" || echo "failed"
在/yunjisuan目錄批量建立檔案
#!/bin/bash
Path=/yunjisuan
[ -d "$Path" ] || mkdir -p $Path
for i in `seq 10`
do
touch $Path/yunjisuan_$i.html
done
批量改名
#!/bin/bash
Path=/yunjisuan
[ -d "$Path" ] || mkdir -p $Path
for file in `ls $Path`
do
mv $Path/$file "$Path/"`echo $file | sed -r 's#yunjisuan(.*).html#linux\1.HTML#g'`
done
批量建立使用者並設定密碼
#!/bin/bash
User=yunjisuan
Path=/tmp
for user in ${User}{01..10}
do
useradd $user > /dev/null 2>&1
if [ $user -ne 0 ];then
echo "$user created failed"
echo "scripts begin to rollback"
for i in ${User}{01..10}
do
userder -r $i >/dev/null 2>&1
[ $? -eq 0 ] || exit 1
done
echo >$Path/usr_passwd
exit 1
else
passWD=`echo $RANDOM | md5sum | cut -c1-8` (表示取一大串隨機數,從這串隨機數前擷取1-8位)
[ -d $Path ] || mkdir $Path
echo $passWD | passwd --stdin $user
echo "$user:$passWD">> $Path/user_passwd
fi
done
exit 0 表示正常執行程式並退出程式
exit 1 或exit -1 表示非正常執行導致退出程式
echo -n 不換行
獲取當前目錄下的目錄名做為變數列表列印輸出
#!/bin/bash
Path=`pwd`
echo $Path
for filename in `ls`
do
[ -d ${Path}/${filename} ] && echo $filename
done
九九乘法表
#!/bin/bash
for ((i=1;i<10;i++))
do
for ((j=1;j<=i;j++))
do
echo -n "$j * $j =$((i*j))"
echo -n " "
done
echo " "
done
顯示出1-100的偶數
#!/bin/bash
for i in {1..100}
do
[ $(($i%2)) -eq 0 ] && echo $i
done
顯示出1000-2000的質數
#!/bin/bash
for i in {1000..2000}
do
[ $(factor $i | awk '{print NF}') -le 2 ] && echo $i
done
factor命令:分解因數
Case語句
用途:選單;啟動指令碼
case語句適合變數的值少,且為固定的數字或字串集合。 系統服務啟動指令碼傳參的判斷多用case語句
格式:
case "字串變數" in
值1)
指令1
;;
值2)
指令2
;;
*)
指令
esac
注意:case語句相當於一個if的多分支結構語句
值1的選項
apple)
echo -e "@RED_COLOR apple $RES"
;;
也可以這樣寫,輸入2種格式找同一個選項
apple|APPLE)
echo -e "$RED_COLOR apple $RES"
;;
服務指令碼框架
#!/bin/bash
. /etc/init.d/functions
case $1 in
start)
action "服務開始啟動" /bin/true
;;
stop)
action "服務準備停止" /bin/false
;;
restart)
action "服務準備停止" /bin/true
action "服務來時啟動" /bin/true
;;
*)
echo "請輸入正確引數"
;;
esac
在當前指令碼引用函式庫(絕對路徑)
Function對應的是action “ ” 路徑
/bin/true 表示一個標誌
設定指令碼配置啟動級別
(1)為指令碼設定啟動級別
將指令碼複製到/etc/init.d/下,vim編輯/etc/init.d/cash.sh指令碼
# chkconfig: 35 90 10
(2)在chkconfig中新增指令碼
# chkconfig --add case.sh
# chkconfig --list case.sh
(3)設定關閉cash.sh
# chkconfig case.sh off
Shell函式
不論什麼程式語言,基本只有三種程式設計的方法(指如何去編寫程式碼的方法論)
1、面向過程
2、面向物件
3、函數語言程式設計
在shell語言中,只能支援面向過程這種程式設計方法。
在shell中,function表示函式(function、return可以不寫)
格式:
function 函式名(){
命令序列
[return x]
}
函式名
函式體只有被呼叫時才會啟動,若要重複啟動該函式,只需將函式名重複n遍
獲取隨機數的幾種方法
(1)通過系統環境變數$RANDOM
# echo $RANDOM
6178
# echo $RANDOM
30890
# echo $((RANDOM%9)) #輸出0~9之間隨機數
2
# echo $((RANDOM%9))
# echo $((RANDOM%9))$((RANDOM%9)) #輸出00~99 隨機數
64
# echo $RANDOM|md5sum #隨機數長短不一,可以用md5sum命令統一格式化
599e328a94329684ce5c92b850d32f26 -
(2)通過openssl產生
# openssl rand -base64 8
aND8WMRM6vQ=
# openssl rand -base64 8
RsRdRq/9vi4=
# openssl rand -base64 8|md5sum
b1108cafbc2291392e41d2c914360138 -
# openssl rand -base64 10
1frkA2kIJODxqQ==
(3)通過時間獲得隨機數(date命令詳解見下頁文件)
# echo $(date +%N)
361599138
# echo $(date +%t%N)
950526316
(4)Urandom
# head /dev/urandom | cksum
621330951 2535
# head /dev/urandom | cksum
404398617 2470
(5)UUID
# cat /proc/sys/kernel/random/uuid
8a6c5bbe-2d42-44ac-9ef1-3e7683a613e3
# cat /proc/sys/kernel/random/uuid
c828c209-5b5f-4bc7-917c-678ed4215988
# uuidgen
961dc354-81b2-4564-9b85-6095ed4bc7b5
迴圈控制語句break、continue、exit、return
作用:用於迴圈結構中控制迴圈語句
break n:n表示跳出迴圈的層數,如果省略n表示跳出整個迴圈(只跳出所在位置的迴圈)
continue n:n表示退出到第n層繼續迴圈,如果省略n表示跳過本次迴圈,忽略本次迴圈剩餘程式碼,進入迴圈的下一次迴圈exit n:退出當前shell程式,n為返回值,n也可以省略,在下一個shell裡通過$?接收這個n值
return n:用在函式裡,做為函式的返回值,用於判斷函式執行是否正確。和exit一樣,如果函式裡有迴圈,也會直接退出迴圈,退出函式
break n:n表示跳出迴圈的層數,如果省略n表示跳出整個迴圈
#!/bin/bash
for ((i=0;i<=5;i++))
do
[ $i -eq 3 ] && break
echo $i
done
echo "ok"
break只跳出所在位置的一個整個迴圈迴圈
continue n:n表示退出到第n層繼續迴圈,如果省略n表示跳過本次迴圈,忽略本次迴圈剩餘程式碼,進入迴圈的下一次迴圈
#!/bin/bash
for ((i=0;i<=5;i++))
do
[ $i -eq 3 ] && continue
echo $i
done
echo "ok"
exit n:退出當前shell程式,n為返回值,n也可以省略,在下一個shell裡通過$?接收這個n值
#!/bin/bash
for ((i=0;i<=5;i++))
do
[ $i -eq 3 ] && exit 2
echo $i
done
echo "ok"
return n:用在函式裡,做為函式的返回值,用於判斷函式執行是否正確。和exit一樣,如果函式裡有迴圈,也會直接退出迴圈,退出函式
#!/bin/bash
function xxx() {
for ((i=0;i<=5;i++))
do
[ $i -eq 3 ] && return 7
echo $i
done
echo "ok"
}
xxx
echo $?
shell指令碼的除錯
(1)使用dos2unix處理指令碼
從windows編輯的指令碼到Linux下需要使用這個命令
dos2unix windows.sh
(2)使用echo命令除錯
在變數讀取或修改的前後假如echo $變數,也可在後面使用exit退出指令碼,這樣可以不用註釋後邊程式碼
(3)利用bash的引數除錯
sh [-nvx]
-n:不會執行該指令碼,僅查詢指令碼語法是否有問題,並給出錯誤提示。可用於生產伺服器那些只能執行一次不可逆的指令碼。
-v:在執行指令碼時,先將指令碼的內容輸出到螢幕上然後執行指令碼,如果有錯誤,也會給出錯誤提示。(一般不用)
-x:將執行的指令碼內容及輸出顯示到螢幕上,常用
1)要記得首先用dos2unix對指令碼格式化
2)直接執行指令碼根據報錯來除錯,有時報錯不準確。
3)sh -x除錯整個指令碼,顯示執行過程。
4)set -x和set +x除錯部分指令碼(在指令碼中設定)
5)echo輸出變數及相關內容,然後緊跟著exit退出,不執行後面程式的方式,一步步跟蹤指令碼,對於邏輯錯誤比較好用。
ping -c 1 ip 只ping一次
#!/bin/bash
for ip in 192.168.214.{1..254}
do
ping -c 1 $ip &> /dev/null
if [$? -eq 0 ];then
echo "$ip正常狀態"
fi
done