1. 程式人生 > >Shell腳本基礎入門

Shell腳本基礎入門

shell腳本

一、shell腳本介紹

1.1 開頭(環境使用shebang機制)

#!/bin/bash 必須寫在文件首行

符號#!用來告訴系統它後面的參數是用來執行該文件的程序。

當編輯好腳本時,如果要執行該腳本,還必須使其可執行。

要使腳本可執行:

編譯 chmod +x filename 這樣才能用./filename或source filename 來運行。

1.2 註釋

在進行shell編程時,以#開頭的句子表示註釋,直到這一行的結束。我建議在程序中使用註釋。如果使用了註釋,那麽即使相當長的時間內沒有使用該腳本,我們也能在很短的時間內明白該腳本的作用及工作原理。

1.3格式


技術分享

腳本中如果有語法錯誤可以用來: bash -n /path/to/some_script

調試跟蹤: bash -x /path/to/some_script

第一行的#!/bin/bash是聲明這個腳本使用的shell格式,因為我們使用的是bash,所以必須要以“#!/bin/bash”來告訴別人這個腳本裏的語法使用的是bash語法,這樣它在執行的時候,就能加載bash相關配置文件,使我們的命令能夠更好的執行下去。(如果不加,有可能腳本無法執行,無法判斷腳本是使用的什麽shell。當然如果默認選擇bash格式,可以不寫,但最好還是腳本第一行寫上shebang機制,還有就是創建文本的時候,後綴加上.sh為結尾。

二、變量

2.1 變量定義

所有的變量都由字符串組成,用一個字符串,替代更多更復雜的內容 ,並且您不需要對變量進行聲明。在變量使用命令時需要加` ` 或者$( ).

賦值給一個變量,並打印出內容如下:


技術分享

有時候變量名很容易與其他文字混淆,比如:

num=2

echo “this is the $numnd”

這並不會打印出“this is the 2nd”,而僅僅打印”this is the “,因為shell會去搜索變量numnd的值,但是這個變量時沒有值的。可以使用花括號來告訴shell我們要打印的是num變量:echo “this is the ${num}nd” 這將打印:this is the 2nd

註意:變量名外面的花括號是可選的,加不加都行,加花括號是為了幫助解釋器識別變量的邊界

2.2 變量格式

1、必須以字母開頭後面可以使用下劃線,數字

2、中間不能有空格,不能使用標點符號,不能使用程序中的保留字如:if,for。

3、駝峰語法:首個字母小寫,其余開頭字母大寫如 mageJiaoYu

2.3變量分類

本地變量,環境變量,位置變量,特殊變量

本地變量:生效範圍為當前shell進程;對當前shell之外的其它shell進程,包括當前shell的子shell進程均無效。

變量賦值:name=‘value’

可以使用引用value:

(1)可以是直接字串; name=“root”

(2)變量引用:name=”$USER”

(3)命令引用:name=`COMMAND`, name=$(COMMAND)

變量引用:${name}, $name

" ":弱引用,其中的變量引用會被替換為變量值”;

例:[[email protected] ~]# var1=1

[[email protected] ~]# echo "$var1 * ‘‘ \$a\\

1 * $a\

(雙引號比起單引號比較有人情味,也比較聰明,它能識別裏面的變量,不會屏蔽\和$這兩個字符的含義,如果需要屏蔽這些字符含義,除了用單引號外,還可以在前面加個\符號。)

‘ ‘:強引用,其中的變量引用不會被替換為變量值,而保持原字符串;

例:[[email protected] ~]# var1=2

[[email protected] ~]# echo ‘$var1 * \$a\\‘

$var1 * \$a\\

(單引號可以屏蔽全部字符的特殊意義,但是不能表示一個字符:(單引號‘)。由於她沒有轉義字符,所以不能表示她本身。強引用)

顯示已定義的所有變量:set

刪除變量:unset name 腳本運行完畢要釋放變量。

環境變量:生效範圍為當前shell進程及其子進程

變量聲明、賦值:

export name=VALUE

declare -x name=VALUE

變量引用:$name, ${name}

刪除變量:unset name

顯示所有環境變量(環境變量的查詢)

env

printenv

export

declare -x

#env列出環境下所有環境變量與其內容

#set可查看所有的變量(含環境變量與本地變量)

bash內建的環境變量:PATH SHELL UID HOME PWD LANG MAIL HOSTNAME HISTSIZE PS1等

例:[[email protected]centos7 ~ ]# echo $HOME

[root@centos7 ~]# /root

局部變量:生效範圍為當前shell進程中某代碼片斷(通常指函數)

位置變量:$1, $2, …來表示,用於讓腳本在腳本代碼中調用通過命令行傳遞給它的參數。

$1, $2, …:對應第1、第2等參數,shift [n]換位置。當位置參數遇到10及10以上的要用{ };例:echo 10st 結果為arg is {10}。

$0:命令本身,是用於取腳本本身的名字

[[email protected]centos7 tmp]# vim file1

#!/bin/bash

dirname $0

basename $0

[[email protected]centos7 tmp]# bash ./file1

.

file1

[[email protected]centos7 tmp]# bash /tmp/file1

/tmp

file1

$#:傳遞給腳本的參數的個數,一般用於控制參數個數。

[[email protected]centos7 tmp]#vim file2

#!/bin/bash

echo $#

[[email protected]centos7 tmp]# bash file2 123

3

[[email protected]centos7 tmp]# bash file2 123456

6

$?:判斷上個命令腳本,函數是否執行成功,命令執行的返回值,0表示沒有錯誤,其他表示有錯誤,代表上個命令執行失敗

[[email protected]centos7 tmp] # ls

file1 file2

[[email protected]centos7 tmp] # echo $?

0

[[email protected]centos7 ~] # lls

bash:lls:command not found...

[[email protected]centos7 ~] # echo $?

1

$*:傳遞給腳本的所有參數,全部參數合為一個字符串

[email protected]:傳遞給腳本的所有參數,每個參數為獨立字符串

[email protected] $*只在被雙引號包起來的時候才會有差異

set —清空所有位置變量



技術分享

小總結:變量內容若有空格符可使用雙引號" "或者單引號‘ ‘將變量內容結合起來:

a.雙引號內的特殊字符如$等,可以保持原本的特性,如:


技術分享

b.單引號內的特殊字符則僅為一般字符(純文本),如:


技術分享

c.可用轉義字符“\”將特殊符號(如$、\、!)變成一般字符。如


技術分享

d.在一串命令中,還需要通過其他的命令提供的信息,可以使用反單引號“`命令`”或者“$(命令)”,如:


技術分享

e.若該變量為了增加變量內容時,則可用“$變量名稱”或${變量}累加內容,如:給變量PATH增加內容,不能直接用”PATH=內容“,這樣會覆蓋掉原本的變量值,應該用“PATH=$PATH:內容”.


技術分享

f.若該變量需要在其他子進程(子進程:在目前這個shell的情況下,去打開另一個新的shell,新的shell就是子進程)執行,則需要以export來使變量變成環境變量。如:


技術分享

g.取消變量的方法為使用“unset變量名稱”,如:


技術分享

三、bash的配置文件

3.1按生效範圍劃分,存在兩類:

全局配置:/etc/profile /etc/profile.d/*.sh /etc/bashrc

個人配置:~/.bash_profile ~/.bashrc

3.2按功能劃分,存在兩類:

profile類和bashrc類

profile類:為交互式登錄的shell提供配置

全局:/etc/profile, /etc/profile.d/*.sh

個人:~/.bash_profile

功用:(1)用於定義環境變量 (2)運行命令或腳本

bashrc類:為非交互式和交互式登錄的shell提供配置

全局:/etc/bashrc

個人:~/.bashrc

功用:(1)定義命令別名和函數 (2)定義本地變量

3.3shell登錄兩種方式

交互式登錄:(不能繼承上一個shell)

(1)直接通過終端輸入賬號和密碼登錄

(2)使用“su -l UserName 或su - UserName”切換的用戶

執行順序:/etc/profile –> /etc/profile.d/*.sh –> ~/.bash_profile–> ~/.bashrc–> /etc/bashrc

非交互式登錄:(繼承上一個shell)

(1)su UserName

(2)圖形界面下打開的終端,執行腳本

執行順序:~/.bashrc–> /etc/bashrc–> /etc/profile.d/*.sh

例子:在這幾個文件分別設置環境變量A,B,C,D,E


技術分享

沒有重新登陸時,這幾個變量就不會生效,echo $A $B $C $D $E,什麽都不顯示。重新登陸後,這幾個變量就會生效。


技術分享

用非交互式登陸guanyu用戶,就會繼承上一個shell,所以echo 5個變量都會顯示。而用交互式登陸guanyu用戶,不能繼承上一個shell,就只能讀取/etc/profile,/etc/profile.d/*.sh,~/.bash_profile,~/.bashrc,/etc/bashrc這幾個文件,而變量C,D都是在用戶root的家目錄設置的,只能讀取到變量A,B,E,所以echo這幾個變量只顯示變量A,B,E的值。


技術分享

在root用戶下,將變量A的值改為A6,變量B的值改為B7,變量D的值改為D9,變量E的值改為E10


技術分享

然後不退出重新登陸,直接echo這幾個變量,當然不變,因為這幾個變量沒有生效,此時用非交互式登陸切到guanyu用戶下,變量A的值不變,盡管,變量A的值變為A6,但是用非交互式登陸,繼承了上一個shell變量A的值A1,但是不讀取/etc/profile這個文件,所以不更新變量A的值,變量B的值變為B7,是因為繼承了上一個shell變量B的值然後讀取/etc/profile.d/mage.sh這個文件,更新變量B的值。變量C3的值不變,變量D的值為D4,是因為繼承了上一個shell的變量D的值D4,又因為變量D在root用戶的家目錄裏,所以不讀取,不更新變量D的值。變量E5的值為E10,是因為繼承了上一個shell的變量D的值,又讀取/etc/bashrc所以更新變量E的值,所以變量E的值為E10.


技術分享


技術分享

在用交互式登陸guanyu用戶,echo這幾個變量,不繼承上一個shell,只讀取文件,因為是交互式登陸,所以/etc/profile,/etc/profile.d/mage.sh,/etc/bashrc這幾個文件都能讀取,所以變量A,B,E的值為A6,B7,E10,又因為變量C,D都在root用戶的家目錄裏,所以不能讀取,變量C,D的值為空。


技術分享

3.4 read

變量的定義—read讀入 默認存儲位置$REPLY

如果傳的參數多於賦值,最後一個值會全部接收

–p增加提示

[[email protected] ~]# read -p "輸入密碼" e (必須有空格)

輸入密碼123456

[[email protected] ~]# echo $e

123456

–s 隱藏輸入

[[email protected] ~]# read -s -p "輸入密碼" e (將密碼隱藏)

輸入密碼

[[email protected] ~]# echo $e

123456

-t 設置超時時間

[[email protected] ~]# read -t5 -p "輸入密碼" e (表示5秒後直接退出當前操作)

輸入密碼[[email protected]~]#

四、邏輯判斷語句

4.1條件判斷

[[email protected] ~]# [ -f /etc/hosts ] 判斷是否是文件

[[email protected]~]# echo $?

0

4.2 && ||

&&表示前面執行成功,執行後面.

||表示前面執行成功,不執行後面,前面執行失敗,執行後面

[[email protected] ~]# [ -f /etc/hosts ]&& echo "文件存在" || echo "文件不存在" 相當於返回值0和1

文件存在

[[email protected] ~]# [ -f /etc/host ]&& echo "文件存在" || echo "文件不存在"

文件不存在

4.3 測試文件符號(test)


技術分享

4.4 測試字符串


技術分享

特別註意:字符串必須要用雙引號引起來。字符串比較,比較符號兩端必須有空格。

4.5測試大小

[[email protected] ~]# [ 1 -eq 1 ] && echo "表達式成立" || echo "表達式不成立"

表達式成立

[[email protected] ~]# [ 1 -eq 2 ] && echo "表達式成立" || echo "表達式不成立"

表達式不成立

[[email protected] ~]# [ 1 -ne 2 ] && echo "表達式成立" || echo "表達式不成立"

表達式成立

[[email protected] ~]# [ 1 -gt 2 ] && echo "表達式成立" || echo "表達式不成立"

表達式不成立

[[email protected] ~]# [ 1 -gt 0 ] && echo "表達式成立" || echo "表達式不成立"

表達式成立

[[email protected] ~]# [ 1 -gt 1 ] && echo "表達式成立" || echo "表達式不成立"

表達式不成立

[[email protected] ~]# [ 1 -ge 1 ] && echo "表達式成立" || echo "表達式不成立"

表達式成立

[[email protected] ~]# [ 1 -le 1 ] && echo "表達式成立" || echo "表達式不成立"

表達式成立

[[email protected] ~]# [ 1 -lt 1 ] && echo "表達式成立" || echo "表達式不成立"

表達式不成立

[[email protected] ~]# [ 1 -lt 2 ] && echo "表達式成立" || echo "表達式不成立"

表達式成立

[[email protected] ~]# [ 1 = 1 ] && echo "表達式成立" || echo "表達式不成立"

表達式成立

[[email protected] ~]# [ 1 = 2 ] && echo "表達式成立" || echo "表達式不成立"

表達式不成立

[[email protected] ~]# [ "1" = "2" ] && echo "表達式成立" || echo "表達式不成立"

表達式不成立

[[email protected] ~]# [ "1" != "2" ] && echo "表達式成立" || echo "表達式不成立"

表達式成立

[[email protected] ~]# [ "1" > "2" ] && echo "表達式成立" || echo "表達式不成立"

表達式成立

[[email protected] ~]# [ "1" \> "2" ] && echo "表達式成立" || echo "表達式不成立"

表達式不成立

[[email protected] ~]# [ "1" \< "2" ] && echo "表達式成立" || echo "表達式不成立"

表達式成立

[[email protected] ~]# [ "1" \<= "2" ] && echo "表達式成立" || echo "表達式不成立"

-bash: [: <=: binary operator expected

表達式不成立

[[email protected] ~]# [ "1" \<\= "2" ] && echo "表達式成立" || echo "表達式不成立"

-bash: [: <=: binary operator expected

表達式不成立

4.6 與或非 -a -o !

-a 前一個條件成立,後一個條件成立,整個表達式才成立,有一個是錯的都不會成立

-o 只要任意一個條件成立,表達式就成立,只有倆個都不成立,才不成立,則表達式不成立

[[email protected] ~]# [ "1" = "1" -a "2" = "2" ] && echo "表達式成立" || echo "表達式不成立"

表達式成立

[[email protected] ~]# [ "1" = "1" -a "2" = "3" ] && echo "表達式成立" || echo "表達式不成立"

表達式不成立

[[email protected] ~]# [ "1" = "1" -o "2" = "3" ] && echo "表達式成立" || echo "表達式不成立"

表達式成立

[[email protected] ~]# [ "1" = "2" -o "2" = "3" ] && echo "表達式成立" || echo "表達式不成立"

表達式不成立

[[email protected] ~]# [ "1" = "1" -a !"2" = "3" ] && echo "表達式成立" || echo "表達式不成立"

-bash: !"2": event not found

[[email protected] ~]# [ "1" = "2" -o ! "2" = "3" ] && echo "表達式成立" || echo "表達式不成立"

表達式成立

五、循環語句

if 語句 單分支語句

語法格式:

如果if [條件語句]

然後 then

執行命令

fi

實例:編寫一個腳本/root/bin/createuser.sh,腳本的執行語法必須是:createuser.sh -u username -m password,選項與參數間可支持多空格,但不能順序顛倒。

當未指定正確的選項或參數時,以錯誤輸出方式提示“createuser.sh -u username -m password ”後退出腳本。

用戶名必須以字母開頭,可包括數字和_。否則不合法。以錯誤輸出提示用戶"用戶名僅包含字母數據和下劃線"

當用戶名檢測合法後,判斷用戶名是否已存在,若存在,再判斷用戶是否已設置過密碼,若設置過密碼,直接退出,未設置,則將密碼設置為所指定的密碼後以正確輸出方式顯示“username 密碼已更新後退出”

當用戶名不存在,則創建用戶,並為該用戶設置所指定的密碼後以正確輸出方式顯示“用戶username已創建並更新密碼”

要求腳本執行過程中不能有非要求的其他輸出結果出現。腳本在非正確方式退出時應反回給?參數非0值。


#判斷用戶名是否規範,並且給出返回值

decide=`(echo $2|grep "^[[:alpha:]]\([[:alpha:]]\|[[:digit:]]\|_\)*$" &>/dev/null;echo $?)`

#判斷用戶是否存在,並給出返回值

id=`id $2 &>/dev/null;echo $?`

#截取用戶密碼位

mima=`getent shadow $2 |cut -d: -f 2`

#判斷第一個參數

if [ "$1" != "-u" ];then

echo "createuser.sh -u username -m password"

exit 1

fi

#判斷第三個參數

if [ "$3" != "-m" ];then

echo "createuser.sh -u username -m password"

exit 1

fi

#判斷參數個數

if [ $# -ne 4 ];then

echo " createuser.sh -u username -m password"

exit 1

fi

#判斷第二個參數

if [ $decide -ne 0 ];then

echo "用戶僅包含字母數字和下劃線"

exit 1

else

if [ $id -eq 0 ];then

if [ "$mima" == "!!" -o "$mima" == "" ];then

`echo "$4" |passwd --stdin $2 &>/dev/null`

echo "$2密碼已更新"

exit 1

else

exit 1

fi

else

`useradd $2`

`echo $4 |passwd --stdin $2 &>/dev/null`

echo "用戶$2已創建並更新密碼"

fi

fi

#刪除變量

unset decide

unset id

unset mima

解析:先定義變量decide,id,mima,再對變量進行測試。

練習:

1.寫一個腳本名為jiaozuoyexx.sh 當執行該腳本時如jiaozuoyeXX.sh testXX.sh,就會自動將該testXX.sh傳給教師機,

路徑是scp testXX.sh [email protected]:~/scripts 密碼為mage26

1、vim jiaozuoye.sh

#!/bin/bash

--------------------------

# Filename:

# Revision:

# Date:

# Author:

# Email:

# Website:

# Description:

# ------------------------------------------

scp $1 [email protected]:~/scripts

2、chmod +x createshXX.sh 添加權限

3、./jiaozuoyeXX.sh jiaozuoyeXX.sh 查看結果

思路:這裏的$1實際是個參數,代表腳本名稱。

2.寫一個能夠創建新腳本的Shell script,如名為createshXX.sh 當執行時createsh /root/bin/test1.sh

則會自動創建並打開/root/bin/test1.sh,且其中包含以下內容。

1、vim createshXX.sh

2、#!/bin/bash

# ------------------------------------------

# Filename:

# Revision:

# Date:

# Author:

# Email:

# Website:

# Description:

# ------------------------------------------

echo "#!/bin/bash

# ------------------------------------------

# Filename:

# Revision:

# Date:

# Author:

# Email:

# Website:

# Description:

# ------------------------------------------

" >>$1

chmod +x createshXX.sh

vim $1

3、修改腳本要進入vim createshXX.sh

4、./jiaozuoyeXX.sh createshXX.sh

此處>>表示追加

3、編寫腳本/root/bin/sumid.sh,計算/etc/passwd文件中的第10個用戶和第20用戶的ID之和

1、./createsh36.sh(相當於一個命令) /root/bin/sumid.sh 進入編輯界面

2、#!/bin/bash

#--------------------------------------------------

# Filename:/root/bin/sumid.sh

# Version:1.0

# Date:‘date "+%F %T"‘

# Author:Lemon.

# Description:ID之和

#--------------------------------------------------

echo "$[$(cat /etc/passwd|head -10|tail -1|cut -d: -f3)+$(cat /etc/passwd|head -20|tail -1|cut -d: -f3)]"

chmod +x /root/bin/sumid.sh

4、cd /root/bin/ 需要進入腳本目錄才能查看結果

5、./sumid.sh 查看文件結果

或uid=10 uid=20

#!/bin/bash

Uid1=`head -$1 /etc/passwd|tail -1|cut -d: -f3`

Uid2=`head -$2 /etc/passwd|tail -1|cut -d: -f3`

Sumid=$[Uid1+Uid2]

echo "Sumid is $Sumid"

unset Uid1 Uid2 Sumid

分析:分別表示出第10個用戶和第20個用戶

4、編寫腳本/root/bin/sumfile.sh,統計/etc, /var, /usr目錄中共有多少個一級子目錄和文件

1、./createsh36.sh sumfile.sh

2、#!/bin/bash

file1=`ls -A /etc|wc -l`

file2=`ls -A /var|wc -l`

file3=`ls -A /usr|wc -l`

let sum=$file1+$file2+$file3

echo "$sum"

unset file1 file2 file3 sum

chmod +x sumfile.sh

3、 ./sumspace.sh /etc /var /usr

註意:要想引用一串命令需要用` `和$( )引用。

5、編寫腳本/root/bin/checkdisk.sh,檢查磁盤分區空間和inode使用率,如果超過80%,就發廣播警告空間將滿

diskused_max=`df | grep "/dev/sd"|sort -nr -k5|head -1|tr -s ‘ ‘ %|cut -d% -f5`

inodeused_max=`df -i| grep "/dev/sd"|sort -nr -k5|head -1|tr -s ‘ ‘ %|cut -d% -f5`

[ "$diskused_max" -gt "80" ] && wall "空間即將滿"||echo "空間使用率不超過80%"

[ "$inodeused_max" -gt "80" ] && wall "inode即將滿"||echo "inode使用率不超過80%"

unset diskused_max inodeused_max

解釋:搜索到磁盤分區中的分區/dev/sd,sort -nr -k5表示取第5列然後按數字從大到小排列,tr -s ‘ ‘ %將空格替換成%,再剪切,隨後和80比較。

-----------------------答案2---------------------------------------------------

dev=`df|grep "/dev/sd"|egrep -o "[0-9]{1,3}%"|sort -n|tail -n 1|cut -d% -f1`

ino=`df -i|egrep -o "[0-9]{1,3}%" |sort -n|tail -n 1|cut -d% -f1`

[[ "$dev" -gt 80 ]] || [[ "$ino" -gt 80 ]] && echo $(wall 磁盤已滿)||echo 還有很多利用空

間呦

unset dev

unset ino

解釋:egrep -o 只顯示被模式匹配的字串,[0-9]{1,3}%表示在空間使用率這裏前面的字符匹配至少1次,最多3次,隨後剪切,排序。這裏的比較用或表示,只要有一個滿了,另一個不用比較,就能發出警報。


Shell腳本基礎入門