shell腳本編程初步入門
說到shell腳本編程,那我們就來先看下shell,shell既是一種命令語言,又是一種程序設計語言。作為命令語言,它交互式地解釋和執行用戶輸入的命令;作為程序設計語言,它定義了各種變量和參數,并提供了許多在高級語言中才具有的控制結構,包括循環和分支。它雖然不是Linux系統內核的一部分,但它調用了系統核心的大部分功能來執行程序、建立文件并以并行的方式協調各個程序的運行。因此,對于用戶來說,shell是最重要的實用程序,深入了解和熟練掌握shell的特性極其使用方法,是用好Linux系統的關鍵。
Shell有兩種執行命令的方式:
交互式:解釋執行用戶的命令,用戶輸入一條命令,Shell就解釋執行一條。
批處理:用戶事先寫一個Shell腳本(Script),其中有很多條命令,讓Shell一次把這些命令執行完,而不必一條一條地敲命令。
大體上,可以將程序設計語言可以分為兩類:編譯型語言和解釋型語言。
編譯:高級語言–>編譯器–>目標代碼 例如:java,C#
解釋:高級語言–>解釋器–>機器代碼 例如:shell, perl, python
Linux上常見的Shell有bash、sh、csh、ksh等,bash是Linux標準默認的shell,我們這里講到的就是現階段運用最多的bash。
shell腳本是包含一些命令或聲明,并符合一定格式的文本文件。
編寫shell腳本:
1、格式要求:首行shebang機制
#!/bin/bash
#!/usr/bin/python
#!/usr/bin/perl
打開文本編輯器,新建一個文件,擴展名為sh(sh代表shell),擴展名并不影響腳本執行。看下面的例子:
#!/bin/bash
echo "Hello World !"
“#!” 是一個約定的標記,它告訴系統這個腳本需要什么解釋器來執行,即使用哪一種Shell。echo命令用于向窗口輸出文本。
2、運行腳本
給予執行權限,在命令行上指定腳本的絕對或相對路徑,將上面的代碼保存為test.sh,并 cd 到相應目錄;
chmod +x ./test.sh 使腳本具有執行權限
./test.sh 執行腳本
直接運行解釋器,將腳本作為解釋器程序的參數運行
bash /bin/test.sh
腳本調試:
bash -n /path/to/some_script 檢測腳本中的語法錯誤
bash -x /path/to/some_script 調試執行
變量
變量:命名的內存空間。
變量類型:
強類型:定義變量時必須指定類型、參與運算必須符合類型要求;調用未聲明變量會產生錯誤如:java,python
弱類型:無須指定類型,默認均為字符型;參與運算會自動進行隱式類型轉換;變量無須事先定義可直接調用如:bash不支持浮點數
變量命名法則:
1、不能使程序中的保留字:例如if, for;
2、只能使用數字、字母及下劃線,且不能以數字開頭
3、見名知義
4、統一命名規則:駝峰命名法。
bash中變量的種類:
環境變量:導出一個變量,作用域為當前shell進程及其子進程
export NAME=VALUE;declare -x name=VALUE
變量引用:$name, ${name}
顯示所有環境變量: export env printenv
刪除:unset name
本地變量:NAME=VALUE:作用域為整個bash進程
變量賦值:name=‘value’
可以使用引用value:
(1) 可以是直接字串;name=“root"
(2) 變量引用:name="$USER"
(3) 命令引用:name=`COMMAND`, name=$(COMMAND)
變量引用:${name}, $name
"":弱引用,其中的變量引用會被替換為變量值
'':強引用,其中的變量引用不會被替換為變量值,而保持原字符串。
顯示已定義的所有變量:set
刪除變量:unset name
局部變量:作用域為當前代碼段
只讀變量:只能聲時,但不能修改和刪除
readonly name
declare -r name
Shell特殊變量:$0, $#, $*, $@, $?
$1, $2, …:對應第1、第2等參數,shift [n]換位置
$0: 命令本身
$*: 傳遞給腳本的所有參數,全部參數合為一個字符串
$@: 傳遞給腳本的所有參數,每個參數為獨立字符串
($@ $* 只在被雙引號包起來的時候才會有差異)
$#: 傳遞給腳本的參數的個數
$?:上個命令的退出狀態,或函數的返回值。
看下面的例子:
[root@centos7 ~]# ./test.sh zhl zanghl File Name: ./test.sh First : zhl Second : zanghl Quoted Values: zhl zanghl Quoted Values: zhl zanghl Total Number of Parameters : 2 [root@centos7 ~]# cat test.sh #!/bin/bash echo "File Name: $0" echo "First : $1" echo "Second : $2" echo "Quoted Values: $@" echo "Quoted Values: $*" echo "Total Number of Parameters : $#"
$* 和 $@ 的區別
$* 和 $@ 都表示傳遞給函數或腳本的所有參數,不被雙引號"" 包含時,都以"$1" "$2" … "$n" 的形式輸出所有參數。但是當它們被雙引號""包含時,"$*" 會將所有的參數作為一個整體,以"$1 $2 … $n"的形式輸出所有參數;"$@" 會將各個參數分開,以"$1" "$2" … "$n" 的形式輸出所有參數。
這里我們引用網上一個資料中的例子來看一下:
程序是這樣的:
#!/bin/bash echo "\$*=" $* echo "\"\$*\"=" "$*" echo "\$@=" $@ echo "\"\$@\"=" "$@" echo "print each param from \$*" for var in $* do echo "$var" done echo "print each param from \$@" for var in $@ do echo "$var" done echo "print each param from \"\$*\"" for var in "$*" do echo "$var" done echo "print each param from \"\$@\"" for var in "$@" do echo "$var" done 執行 ./test.sh "a" "b" "c" "d",看到下面的結果: $*= a b c d "$*"= a b c d $@= a b c d "$@"= a b c d print each param from $* a b c d print each param from $@ a b c d print each param from "$*" a b c d print each param from "$@" a b c d
邏輯運算:
true 1, false 0
與: 1 與 1 = 1
1 與 0 = 0
0 與 1 = 0
0 與 0 = 0
或:
1 或 1 = 1
1 或 0 = 1
0 或 1 = 1
0 或 0 = 0
非:!
! 1 = 0
! 0 = 1
短路運算:
短路與:第一個為0,結果必定為0;第一個為1,第二個必須要參與運算;
短路或:第一個為1,結果必定為1;第一個為0,第二個必須要參與運算;
異或:^ 異或的兩個值,相同為假,不同為真
條件測試:
判斷某需求是否滿足,需要由測試機制來實現;
專用的測試表達式需要由測試命令輔助完成測試過程;若真,則返回0 若假,則返回1
測試命令:
test EXPRESSION
[ EXPRESSION ]
[[ EXPRESSION ]]
注意:EXPRESSION前后必須有空白字符
條件性的執行操作符
&& 代表條件性的 AND THEN
|| 代表條件性的 OR ELSE
test命令
長格式的例子:test "$A" == "$B" && echo "Strings are equal" $ test “$A” -eq “$B” && echo "Integers are equal"
簡寫格式的例子:[ "$A" == "$B" ] && echo "Strings are equal" $ [ "$A" -eq "$B" ] && echo "Integers are equal"
bash的測試類型
數值測試:
-gt: 是否大于;
-ge: 是否大于等于;
-eq: 是否等于;
-ne: 是否不等于;
-lt: 是否小于;
-le: 是否小于等于;
字符串測試:
==:是否等于;
>: ascii碼是否大于ascii碼
<: 是否小于
!=: 是否不等于
=~: 左側字符串是否能夠被右側的PATTERN所匹配 注意: 此表達式一般用于[[ ]]中;
-z "STRING":字符串是否為空,空為真,不空為假
-n "STRING":字符串是否不空,不空為真,空為假
文件測試:
-a FILE:同-e
-e FILE: 文件存在性測試,存在為真,否則為假;
存在性及類別測試:
-b FILE:是否存在且為塊設備文件
-c FILE:是否存在且為字符設備文件;
-d FILE:是否存在且為目錄文件;
-f FILE:是否存在且為普通文件;
-h FILE 或 -L FILE:存在且為符號鏈接文件;
-p FILE:是否存在且為命名管道文件;
-S FILE:是否存在且為套接字文件;
文件權限測試:
-r FILE:是否存在且可讀
-w FILE: 是否存在且可寫
-x FILE: 是否存在且可執行
文件特殊權限測試:
-g FILE:是否存在且擁有sgid權限;
-u FILE:是否存在且擁有suid權限;
-k FILE:是否存在且擁有sticky權限;
下面是一些編程的示例習題:
編寫腳本/root/bin/systeminfo.sh,顯示當前主機系統信息,包括主機名,IPv4地址,操作系統版本,內核版本,CPU型號,內存大小,硬盤大小。
[root@centos7 bin]# bash systeminfo.sh hostname is centos7.zang IPV4:10.1.252.189 version informationcat: CentOS Linux release 7.2.1511 (Core) kernel information: 3.10.0-327.el7.x86_64 cpu information: Intel(R) Core(TM) i7-4710MQ CPU @ 2.50GHz memory :3.7G Disk : 214.7 GB [root@centos7 ~]# cat /bin/systeminfo.sh #!/bin/bash echo "hostname is `hostname`" echo "IPV4:`ifconfig | egrep -A1 'eno'| tail -1 | tr -s ' '| cut -d' ' -f3`" echo "version informationcat: `cat /etc/redhat-release`" echo "kernel information: `uname -r`" echo "cpu information: `lscpu | sed -n '/Model name/p'| tr -s ' '|cut -d: -f2`" echo "memory :`free -h | sed -n '/Mem/p' |tr -s ' ' | cut -d' ' -f2`" echo "Disk :`fdisk -l | egrep 'Disk /dev/' | cut -d',' -f1 | cut -d: -f2`"
編寫腳本/root/bin/backup.sh,可實現每日將/etc/目錄備份到/root/etcYYYY-mm-dd中
[root@centos7 bin]# bash backup.sh backup is the /etc is success [root@centos7 ~]# ll total 20 -rw-------. 1 root root 1354 Jul 20 17:49 anaconda-ks.cfg drwxr-xr-x. 2 root root 6 Jul 20 17:53 Desktop drwxr-xr-x. 2 root root 6 Jul 20 17:53 Documents drwxr-xr-x. 2 root root 6 Jul 20 17:53 Downloads drwxr-xr-x. 129 root root 8192 Aug 11 19:41 etc2016-08-11 -rw-------. 1 root root 1402 Jul 20 17:52 initial-setup-ks.cfg
編寫腳本/root/bin/disk.sh,顯示當前硬盤分區中空間利用率最大的值
[root@centos7 bin]# bash disk.sh 當前硬盤分區中空間利用率最大的值 :71 [root@centos7 bin]# cat disk.sh #!/bin/bash echo "當前硬盤分區中空間利用率最大的值 :`df | egrep '/dev/sd.*' | tr -s ' ' | cut -d' ' -f5 | tr -d '%'| sort |tail -1`"
編寫腳本/root/bin/links.sh,顯示正連接本主機的每個遠程主機的IPv4地址和連接數,并按連接數從大到小排序
[root@centos7 bin]# bash links.sh 3 10.1.250.72 [root@centos7 bin]# cat links.sh #!/bin/bash netstat -nt | tr -s ' '|cut -d' ' -f5 | egrep -v '[[:alpha:]]'|cut -d: -f1|sort|uniq -c| sort -nr
寫一個腳本/root/bin/sumid.sh,計算/etc/passwd文件中的第10個用戶和第20用戶的ID之和
[root@centos7 bin]# bash sumid.sh 70 [root@centos7 bin]# cat sumid.sh #!/bin/bash s1=`sed -n '10p' /etc/passwd | cut -d: -f3` s2=`sed -n '20p' /etc/passwd | cut -d: -f3` let sum=$s1+$s2 echo $sum
寫一個腳本/root/bin/sumspace.sh,傳遞兩個文件路徑作為參數給腳本,計算這兩個文件中所有空白行之和
[root@centos7 bin]# cat /etc/fstab ; cat /etc/issue # # /etc/fstab # Created by anaconda on Wed Jul 20 14:39:10 2016 # # Accessible filesystems, by reference, are maintained under '/dev/disk' # See man pages fstab(5), findfs(8), mount(8) and/or blkid(8) for more info # UUID=030d27aa-d817-4330-8de8-90f87c7d0e1f / xfs defaults 0 0 UUID=15819ecc-8ae9-4cc3-8f3f-54b7be0ac610 /boot xfs defaults 0 0 UUID=14cd35c6-b195-4ec6-a202-781d2a850aad /testdir xfs defaults 0 0 UUID=f13adfd3-a620-4ec6-b1c3-d9b997b72fca swap swap defaults 0 0 \S Kernel \r on an \m [root@centos7 bin]# bash sumspace.sh /etc/fstab /etc/issue 2 [root@centos7 bin]# cat sumspace.sh #!/bin/bash f1=`sed -n '/^$/p' $1 | wc -l` f2=`sed -n '/^$/p' $2 | wc -l` let f=$f1+$f2 echo $f
寫一個腳本/root/bin/sumfile.sh,統計/etc, /var, /usr目錄中共有多少個一級子目錄和文件
[root@centos7 bin]# bash sumfile.sh /etc下一級子目錄和文件個數:262 /var下一級子目錄和文件個數:22 /usr下一級子目錄和文件個數:12 [root@centos7 bin]# cat sumfile.sh #!/bin/bash f1=`ls -A /etc/ | wc -l` f2=`ls -A /var/ | wc -l` f3=`ls -A /usr/ | wc -l` echo "/etc下一級子目錄和文件個數:$f1" echo "/var下一級子目錄和文件個數:$f2" echo "/usr下一級子目錄和文件個數:$f3"
寫一個腳本/root/bin/argsnum.sh,接受一個文件路徑作為參數;如果參數個數小于1,則提示用戶“至少應該給一個參數”,并立即退出;如果參數個數不小于1,則顯示第一個參數所指向的文件中的空白行數
[root@centos7 bin]# cat argsnum.sh #!/bin/bash [[ $# -lt 1 ]] && echo "至少給一個路經作為參數"|| egrep '^$' $1 | wc -l [root@centos7 bin]# bash argsnum.sh /etc/issue 1 [root@centos7 bin]# cat argsnum.sh #!/bin/bash [[ $# -lt 1 ]] && echo "至少給一個路經作為參數"|| egrep '^$' $1 | wc -l
寫一個腳本/root/bin/hostping.sh,接受一個主機的IPv4地址做為參數,測試是否可連通。如果能ping通,則提示用戶“該IP地址可訪問”;如果不可ping通,則提示用戶“該IP地址不可訪問”
[root@centos7 bin]# cat hostping.sh #!/bin/bash ping -c1 -w1 $1 > /dev/null [[ $? == 0 ]] && echo "該IP可以訪問" || echo "該IP不可訪問" [root@centos7 bin]# bash /bin/hostping.sh 192.168.1.99 該IP不可訪問 [root@centos7 bin]# bash /bin/hostping.sh 192.168.1.1 該IP可以訪問
chmod -rw /tmp/file1,編寫腳本/root/bin/per.sh,判斷當前用戶對/tmp/fiile1文件是否不可讀且不可寫
[root@centos7 bin]# cat per.sh #!/bin/bash z1=`[ -r /tmp/file1 ]; echo $?` z2=`[ -w /tmp/file1 ]; echo $?` [ $z1 == 1 ] && [ $z2 == 1 ] && echo "對此文件不可讀寫" || echo "`ls -l /tmp/file1`" [root@centos7 bin]# ./per.sh ----------. 1 zzz root 20 Aug 12 11:44 /tmp/file1 [zang@centos7 bin]$ ./per.sh 對此文件不可讀寫 [zzz@centos7 bin]$ ./per.sh -rw-------. 1 zzz root 20 Aug 12 11:44 /tmp/file1
編寫腳本/root/bin/nologin.sh和login.sh,實現禁止和充許普通用戶登錄系統。
[root@centos7 bin]# cat nologin.sh #!/bin/bash [ -f /etc/nologin ] && echo "用戶已經禁止登陸" || touch /etc/nologin ; echo "用戶已經禁止登陸" [root@centos7 bin]# bash login.sh 用戶已經可以登陸 [root@centos7 bin]# cat login.sh #!/bin/bash [ -f /etc/nologin ] && (rm -f /etc/nologin ; echo "用戶已經可以登陸") || echo "用戶已經可以登陸"
原創文章,作者:zanghonglei,如若轉載,請注明出處:http://www.www58058.com/35549
態度端正,完成的不錯,再接再勵