Shell(1):基礎
0、常用
規則
1)運算符和變量之間需要有空格
2)if、中括號、表達式間均需要空格
3)開頭:#!/bin/bash,標注你的shell類型
4)路徑寫絕對路徑,不然很容易混淆
5)與寫在sh腳本中的bash語句相比,如果將指令直接寫在命令行中,那么行與行之間用分號區別(例如for循環那一句都要用分號);
6)如果用到了 管道 |,那么需要把前半句話用括號包括(相當于管道的數據來源是個整體),不然可能導致預期之外的輸出;
算數運算:
整數
- val = $[$a 運算符 $b](個人覺得最好用)
- val =` expr $a 運算符 $b`
- val = $(expr $a 運算符 $b)
val=$[2 + 2] val=$(expr 2 + 2) val=`expr 2 + 2`
自增/減:let
n=1 let n++ echo $n 2 let n-- echo $n 1
小數:bc
echo " 2 * 0.5 " | bc 1.0 #設置精度(小數點后的位數) echo " scale=2; 1.74 / 3 "| bc .58 echo " scale=3; 1.74 / 3 "| bc .580
在語句中傳入表達式運算
- ` 表達式 `
- $(表達式)
例如把某個文件作為for循環的循環體:
for i in `cat test.txt`
for i in $(cat test.txt)
do
xxx
done
數組
A=(A B C D) #輸出全部 echo ${A[@]} echo ${A[*]} #輸出長度 echo ${#A[@]} echo ${#A[*]} #輸出單個元素,下標從0開始 echo ${A[1]} #B #for循環 for i in ${A[@]} do echo $i done
for循環
#循環輸出當前目錄下所有文件的行數、單詞數、字符數 for i in $(ls) do wc $i done 5795 72282 447054 redis_6379.log 38 485 3849 sentinel.log
#配合{}實現自定義循環內容
#這里構造了兩個前半截相同,最后一節為11和12的IP地址進行循環輸出
for i in 192.168.1.1{1,2}
do
echo $i
done
192.168.1.11
192.168.1.12
while死循環(注意while后的冒號)
while : do 循環體 #每60s執行一次 sleep 60 done
if
#判斷某個指令的執行結果是否為空 if [ `grep abc test.txt`] then echo "OK" fi #如果grep結果中包含空格,則可以寫為 if [ `grep abc test.txt | awk '{print $1}'` ] then echo "OK" fi
- wc:統計
wc [選項] 文件名 選項: -l:只統計行數 -w:只統計單詞數 -m:只統計字符數
-L:最長行的長度
- 將一個文件內容清空
true > 1.txt
變量運算
val =$[2 + 2] val =$[$val1+$val2]
重啟nginx
if [ 條件 ] then /usr/local/nginx/sbin/nginx -s reload 2> /dev/null
fi
將某個需要執行的指令用 2> /dev/null即可,見/dev/null
2)一些特殊字符
符號 |
說明 |
用法 |
| ; | 多條命令順序執行,命令間不互相干擾 | 命令1 ; 命令2 |
| | |
①正則表達式中表示或; ②用于命令間,表示用前邊命令的輸出作為后邊命令的輸入 |
①ab(c|d) ②命令1 | 命令2 |
| & |
①把命令放在后臺執行 ②標準輸出和標準錯誤輸出,具體見輸入輸出重定向一節 |
①java -jar xxx.jar & |
| && | 前邊指令執行成功才執行后邊指令 |
1)如果用在if條件中,可以當做或、與使用 if [ $a -eq 00 ] || [ $a -eq 12 ] 2)單獨使用時,可以實現if的邏輯(||代表if [ 公式 ] false,&&代表if [公式] true) #輸入一個數字 #1)如果比當前最小值小,就把當前最小值更改為該數字; #2)如果比當前最大值大,就把當前最大值更改為該數字。 MIN=0 MAX=100 read -p "輸入一個數字:" INT [$MIN -lt $INT] || MIN=$INT [$MAX -gt $INT] || MAX=$INT
|
| || |
前邊指令執行失敗才執行后邊指令 |
|
| # |
①注釋 ② $#:命令行參數個數 ③${#變量}:變量(字符串)長度 ④${#數組[@]}:數組長度 |
|
| $ |
變量: ①$a:變量的值 命令行: $0:當前腳本程序名稱 $1-$n:第n個命令行參數 $*:所有命令行參數,整體輸出 $@:所有命令行參數,分別輸出 $#:命令行所有參數的個數 進程 $?:最后一次執行狀態 $$:當前進程的進程號pid $!:后臺運行的最后一個進程的進程號 awk $0:某一行的全部字段數據 $1-$n:某一行的第n個字段數據 |
|
| ~ |
字符串是否包含某個子串 |
awk '$2 ~ /Sc/' 第二字段中是否包含"Sc"字符 |
| {} |
正則:表示范圍 循環:指定循環范圍 匿名函數 命令:{}中的命令,是在當前shell執行 |
192.168.1.1{1,2}代表192.168.1.11和192.168.1.12 |
| := |
${var:="defaultvalue"} 當變量var為空(未定義時),為它賦一個默認值 |
event=${event:="on"} 如果event存在,就用存在的值 如果event不存在,就用"on"當它的值 |
|
|
||
| #* | 匹配最左側的符號 | |
裁剪字符
學習自:shell中 #*, ##*, %*, %%*的含義及用法_shell #*-CSDN博客
用法:變量名|裁剪指令|匹配符號
這里的分隔符|,只是為了方便區分,實際上三者之間并無任何分隔符。
1)#*、##*
說明
#*:最小匹配,并移除匹配位置左邊的所有字符
##*:最大匹配,并移除匹配位置左邊的所有字符
例子
VAR="abc/opt/software/hadoop/a.txt"
VAR=${VAR#*/}#最短匹配,匹配第一個斜杠/,即abc/opt,此時abc/都會被刪除
echo $VAR
opt/……/a.txt
VAR="abc/opt/software/hadoop/a.txt"
VAR=${VAR##*/} #最長匹配,匹配最后一個斜杠/,即……doop/a.txt,此時……doop/都會被刪除
echo $VAR
a.txt
2)%*、%%*
說明
%*:最小匹配,從右起匹配第一個字符并移除該字符右側所有字符
%%*:最大匹配,從右往左匹配最后一個字符并移除該字符右側所有字符
例子
VAR="/opt/software/hadoop/a.txt"
VAR=${VAR%*/} #從右往左找到第一個斜杠,并刪除它及它左邊所有字符
echo $VAR
/opt/……/hadoop
VAR="opt/software/hadoop/a.txt"
VAR=${VAR%%*/} #從右往左找到最后一個斜杠,并刪除它及它左邊所有字符
echo $VAR
opt
$
變量 |
作用 |
| $n |
n為數字 $0:當前腳本程序名稱 $1-9:第1-9個參數 ${10}:10以上的參數需要用大括號包含 |
| $* | 命令行中的所有參數,以一個整體輸出 |
| $@ | 命令行中的所有參數,不同參數用@加以區分 |
| $# | 命令行中所有參數的個數 |
預定義變量 |
作用 |
| $? |
最后一次執行的命令的返回狀態: 0:正確執行 非0:不正確 |
| $$ | 當前進程的進程號PID |
| $! | 后臺運行的最后一個進程的進程號 |
1、什么是Shell?
Shell是用C編寫的程序,它是用戶使用Linux的橋梁,Shell既是一種命令語言,也是一種程序設計語言。
Shell為提供了一個界面,用戶通過這個界面訪問操作系統內核的服務。
2、Shell分類
在Linux中輸入指令:
cat /etc/shells #查看全部SHELL
echo $SHELL #默認SHELL

Linux中默認的Shell是/bash/bash,流行的有ash、bash、ksh、csh、zsh。
3、Shell編寫規范
注意Shell腳本與Shell指令的區別:
Shell指令:其實就是Linux指令。
Shell腳本:按照一定語法規則編寫的Shell文件。
shell腳本文件執行
首先編寫一個Shell腳本
[... ~]$vim test.sh
#!/bin/bash
echo "hello world"
兩種執行方式:
①給文件增加執行權限
[root@localhost ~]$chmod u+x test.sh
[root@localhost ~]$ ./test.sh #絕對路徑或者相對路徑執行
②通過bash調用腳本
bash test.sh
或者
bash "test.sh"
1)文件頭
#!/bin/bash [告知系統該腳本所使用的的shell解釋器]
shell指令
2)命名規范
以.sh結尾,.sh是Linux下bash shell的默認后綴。
3)Bash常用快捷鍵(在Linux命令行模式下使用)
快捷鍵 |
命令 |
| CTRL+A |
將光標移動至命令行開頭 |
| CTRL+E | 將光標移動至命令行結尾 |
| CTRL+C | 強制終止當前命令 |
| CTRL+L | 清屏,相當于clear |
| CTRL+U | 刪除/剪切光標之前的所有字符 |
| CTRL+K | 刪除/剪切光標之后的所有字符 |
| CTRL+Y | 粘貼 |
| CTRL+R |
在歷史命令中搜索,按下CTRL+R就會出現搜索頁面。 在其中輸入搜索內容就可以從歷史命令中搜索。 |
| CTRL+D | 退出終端 |
| CTRL+Z | 暫停,并放入后臺 |
| CTRL+S |
暫停輸出 |
| CTRL+Q | 恢復輸出 |
4)輸入輸出重定向
①標準輸入輸出
| 設備 | 設備名 | 文件描述符 | 類型 |
|---|---|---|---|
| 鍵盤 | /dev/stdin | 0 | 標準輸入 |
| 顯示器 | /dev/stdout | 1 | 標準輸出 |
| 顯示器 | /dev/stderr | 2 | 標準錯誤輸出 |
②輸入重定向
- 輸入重定向:不使用標準輸入端口(也就是①中所說的鍵盤)輸入文件,而是使用指定的文件作為標準輸入設備。
- 修改:使用"<"符來修改標準輸入設備。
| 類型 | 符號(語法) | 功能 |
|---|---|---|
| 標準輸入 | 命令<文件1 | 命令把文件1的內容作為標準輸入設備 |
| 標識符限定輸入 | 命令<<標識符 | 命令把標準輸入中讀入內容,直到遇到“標識符”分解符為止 |
| 輸入輸出重定向(同時使用) | 命令< 文件1 >文件2 | 命令把文件1的內容作為標準輸入,把文件2作為標準輸出。 |
③輸出重定向
- 輸出重定向:把輸出信息寫入到文件中,而非控制臺(顯示屏)。如果沒有重定向,那么默認的輸出設備是控制臺。
類型 |
指令 |
效果 |
| 標準輸出重定向 | 命令 > 文件 | 覆蓋,把命令的正確輸出內容輸出到指定的文件 |
| 命令 >> 文件 | 追加,把命令的正確輸出內容輸出到指定的文件 | |
| 標準錯誤輸出重定向 | 錯誤命令2 > 文件 | 覆蓋,把命令的錯誤輸出內容輸出到指定的文件 |
| 錯誤命令2 >> 文件 | 追加,把命令的錯誤輸出內容輸出到指定的文件 | |
| 正確輸出和錯誤輸出同時保存 | 命令 > 文件 2>&1 | 覆蓋,把正確輸出和錯誤輸出都保存到同一個文件 |
| 命令 >> 文件 2>&1 | 追加,把正確輸出和錯誤輸出都保存到同一個文件 | |
| 命令 &> 文件 | 覆蓋,把正確輸出和錯誤輸出都保存到同一個文件 | |
| 命令 &>> 文件 | 追加,把正確輸出和錯誤輸出都保存到同一個文件 | |
| 命令 >> 文件1 2>>文件2 | 追加,正確輸出追加到文件1中,錯誤輸出追加到文件2 |
用法
command > file 將輸出重定向到 file。 command < file 將輸入重定向到 file。 command >> file 將輸出以追加的方式重定向到 file。 n > file 將文件描述符為 n 的文件重定向到 file。 n >> file 將文件描述符為 n 的文件以追加的方式重定向到 file。 n >& m 將輸出文件 m 和 n 合并。 n <& m 將輸入文件 m 和 n 合并。 << tag 將開始標記 tag 和結束標記 tag 之間的內容作為輸入。
例子
echo "hello world" > test7.sh
5)指令執行流程
標志 |
用法 |
效果 |
| ; | 命令1;命令2 | 順序執行 |
| && | 命令1&&命令2 | 只有當命令1正確執行,才會執行命令2 |
| || | 命令1||命令2 | 只有當命令1不正確執行,才會執行命令2 |
6)全局規則
set +/-[選項]
其中 -代表設定某選項,+代表取消該選項,單set可以查看所有變量。
選項有:
- u:調用未聲明變量是否報錯(默認無反應);
- x:命令執行前,先把命令輸出一次(即輸出命令和命令結果)
[root@localhost ~]$ set -u #未定義變量報錯
[root@localhost ~]$ echo $file
-bash: file: unbound variable
[root@localhost ~]$ set -x #指令重現
[root@localhost ~]$ ls
+ls --color=auto
test.sh
四、shell變量
1)何為shell變量
在一個腳本周期內,其值可以發生改變的量就是變量。
shell變量在其定義之前就使用,比如在100行用賦值號創建了某個變量,但是你可以在第10行就用echo把這個變量輸出出來(這一點和Java、C、Python語言都大不相同)。
另外,如果使用了一個錯誤的或者未定義的變量,那么實際sh執行過程中不會報錯,但是由于該變量不存在,所以會把該$變量自動替換為空(空字符串)。
2)變量命名
- 賦值號左右兩側不能有空格;變量的值如果有空格,那么該值要用單引號或雙引號包括。例如test=“hello world!”。
- 環境變量名大寫;
- 如果要進行占位賦值,可以用"$變量名"、"${變量名}"、${變量名}、$變量名
帶雙引號輸出的作用:安全,如果輸出的內容中包含空格,使用雙引號會正確識別空格并完整輸出。
test=123 test="$test"456 test=${test}456 #以上兩種方式分開執行的話 #結果都是123456
示例
name=sc #①單引號 echo '$name' $name #②雙引號 echo "${name}" sc echo "$name" sc #③其他一些輸出 echo ${name} echo $name sc
在輸出單個變量時,加不加雙引號效果一樣,這點跟Python一樣靈活,相當于$就是通知了一個占位符。
echo
-
用法:類似python的print,可以把內容輸出到屏幕上
以下代碼的輸出結果相同
echo "${name}" echo "$name" echo ${name} echo $name
-
關于單雙引號
- 雙引號能①識別變量,實現②轉義功能(類似于轉義斜杠“\”)
- 單引號不能識別變量,也不能轉義,只會原樣輸出。
- 如果反引號要執行某段指令,那么其中的變量$i必須用雙引號包括,才能夠正確解析出來,例如:
echo `grep "$i" /tmp/test.txt`
當i=/data/123.txt時,上文會執行指令grep /data/123.txt /tmp/test.txt并將結果輸出,
如果用單引號:
echo `grep '$i' /tmp/test.txt`
則是執行指令grep '/data/123.txt' /tmp/test.txt,會把這個單引號也引入grep中進行執行。
所以反引號中的變量,要用雙引號包括。
如果只是用于顯示無疑義的字符串,那么在用echo輸出時,加不加引號的效果是一樣的:
echo "hello world" echo hello world #效果相同
-
轉義
echo "\"hello world\"" "hello world"#兩個""被轉義并被正常輸出
-
換行
echo -e "hello\n" #-e開啟轉義
hello
#多出來一個空行
-
不換行
echo -e "abc! \c" #\c 不換行 abc! #不換行 接下來的命令將繼續在"abc!"這一行輸入
特殊符號
符號 |
效果 |
| 單引號 '' | 單引號內的所有字符都視為普通字符 |
| 雙引號 "" |
雙引號內的部分字符視為普通字符,除了$、反引號`、\ $:調用變量的值(類似C語言中的&) `:引用命令 \:轉義 |
| 反引號 `` |
反引號內代表系統命令(可以視為執行引號內的指令返回的內容),而不是普通變量。比如`date`就代表系統時間 但不常用,通常為了避免眼花,常用$()代替: echo `date` echo $(date) |
| $() |
1、效果同反引號,用來引用系統命令(常用) 2、如果要在shell腳本中使用Linux指令,需要用$()包括 |
| () | 用于一串命令執行,()中的命令會在子shell中運行 |
| {} |
1、用于一串命令執行,{}中的命令會在當前shell中執行; 2、利用${變量}或$變量實現變量占位輸出 |
| [ ] |
1、用于變量測試 2、返回true或false的運算 |
| # | 注釋 |
| $ | 獲取變量值,用法${變量}或$變量 |
| \ | 轉義字符,跟在其后的字符將變為普通字符 |
3)變量的類別
- 用戶自定義變量:我們自定義的變量,最常見;
- 環境變量:和系統環境相關的變量;分為用戶自定義環境變量和系統自帶環境變量。
- 位置參數變量:用于向腳本中傳遞參數或數據,變量名和作用都是固定的;
- 預定義變量:Bash中定義好的變量,變量名和作用都是固定的;
①用戶自定義變量
-
聲明:變量名=變量值
-
調用:echo $name或echo ${name}
- 變量刪除:unset 變量名
-
規則設置:set +/-[選項]
其中 -代表設定某選項,+代表取消該選項,單set可以查看所有變量。
選項有:
-
- u:調用未聲明變量是否報錯(默認無反應);
- x:命令執行前,先把命令輸出一次
[root@localhost ~]$ set -u #未定義變量報錯 [root@localhost ~]$ echo $file -bash: file: unbound variable [root@localhost ~]$ set -x #指令重現 [root@localhost ~]$ ls +ls --color=auto test.sh
只讀變量
-
聲明:
變量名=變量值 readonly 變量 #不寫變量名會默認上一個 -
作用:接下來這個變量就無法修改了(再賦值)
②環境變量
- 聲明: export 變量名=變量值
- 查詢:env [變量名](set可以查看所有變量,env只能查看環境變量,不加變量名可以查看所有環境變量)
- 刪除:unset 變量名
[root@localhost ~]$ env HOSTNAME=localhost.localdomain #主機名 SHELL=/bin/bash #當前的shell TERM=linux #終端環境 HISTSIZE=1000 #歷史命令條數 SSH_CLIENT=192.168.4.1594824 22 #當前操作環境是用ssh連接的,這里記錄客戶端ip SSH_TTY=/dev/pts/1 #ssh連接的終端時pts/1 USER=root #當前登錄的用戶
- 用法:$變量名
和常規變量的用法一樣,只是無需聲明,可以直接在shell腳本中用$環境變量名輸出。
echo $NGINX_VERSION 1.16.1
③位置參數/命令行參數
變量名、用法、作用都是固定的
變量 |
作用 |
| $n |
n為數字 $0:當前腳本程序名稱 $1-9:第1-9個參數 ${10}:10以上的參數需要用大括號包含 |
| $* | 命令行中的所有參數,以一個整體輸出 |
| $@ | 命令行中的所有參數,不同參數用@加以區分 |
| $# | 命令行中所有參數的個數 |
命令行參數如何設置和傳入呢?
在用bash xxx.sh調用bash文件時,通過羅列傳入:
bash Test.sh p1 p2
這樣,$0就代表Test.sh,$1代表第一個參數,即p1,$2代表第二個參數,即p2。
$*與$@的區別
二者都是輸出命令行所有參數,但是$*將這些參數作為一個整體輸出,而$@則分別輸出這些參數。
[root@localhost sh]$ vi parameter2.sh #!/bin/bash for i in"$*" #$*中的所有參數是一個整體,所以這個for循環只會循環一次 do echo "The parameters is: $i" done x=1 for y in"$@" #$@中的每個參數都是獨立的,所以“$@”中有幾個參數,就會循環幾次 do echo "The parameter$x is: $y" #輸出變量y的值 x=$(( $x +1 )) #然變量x每次循環都加1,為了輸出時看的更清楚 done
④預定義變量
名稱固定,作用固定
預定義變量 |
作用 |
| $? |
最后一次執行的命令的返回狀態: 0:正確執行 非0:不正確 |
| $$ | 當前進程的進程號PID |
| $! | 后臺運行的最后一個進程的進程號 |
用法
$?
[root@localhost sh]$ ls count.sh hello.sh parameter2.sh parameter.sh #ls命令正確執行 [root@localhost sh]$ echo $? 0 #預定義變量“$?”的值是0,證明上一個命令執行正確 [root@localhost sh]$ ls install.log ls:無法訪問install.log:沒有那個文件或目錄 #當前目錄中沒有install.log文件,所以ls命令報錯了 [root@localhost sh]$ echo $? 2 #變量“$?”返回一個非0的值,證明上一個命令沒有正確執行 #至于錯誤的返回值到底是多少,是在編寫ls命令時定義好的,如果碰到文件不存在就返回數值2
$$與$!
[root@localhost sh]$ vi variable.sh #!/bin/bash echo "The current process is $$" #輸出當前進程的PID. #這個PID就是variable.sh這個腳本執行時,生成的進程的PID find /root -name hello.sh & #使用find命令在root目錄下查找hello.sh文件 #符號&的意思是把命令放入后臺執行,工作管理我們在系統管理章節會詳細介紹 echo "The last one Daemon process is $!" #輸出這個后臺執行命令的進程的PID,也就是輸出find命令的PID號
5)從鍵盤輸入:read
用法:read [選項名 選項值] [變量名]
-
選項:
- -a:后跟一個變量,該變量會被視為一個數組,然后為其賦值,默認以空格為分隔符;
- -p:提示信息:在等待輸入時,給出提示信息;
- -t:秒數:最多等待指定時間;
- -n:數字:輸入指定字符數量后執行;
- -s:隱藏輸入數據(就像Linux登錄時輸入密碼那樣)
- -d:開啟標志符,當輸入該字符后結束。
- -e:開啟命令補全功能
-
變量名
- 變量名自定義,如果沒有則默認為REPLY
- 如果只有單個變量,則把整行輸入賦予該變量;
- 如果有一個以上的變量,則輸入行會進行分割,一個個賦予,最后一個變量會獲得剩余全部字符
例子:
vi read.sh #!/bin/bash read -t 30 -p "Please input your name: " name #①提示“Please ……” #②等待30 秒 #③存入變量name echo "Name is $name" read -s -t 30 -p "Please enter your age: " age #與上一個相比,隱藏輸入 echo "Age is $age" read -n 1 -t 30 -p "Please select your gender[M/F]:" gender #與上一個相比,加入了選項 -n 1,代表只接收一個輸入字符就會執行(都不用輸入回車) echo "Sex is $gender"
五、運算符
1)算術運算符
bash不支持使用+-*/直接進行運算,相關操作要依賴指令awk、expr實現,其中expr最常用。
例子
用一個算術表達式給變量賦值:
val=$(expr 2 + 2) 或 val=`expr 2 + 2`
或
val=$[2 + 2]
需要注意的是數字和運算符之間需要空格,2 + 2而非2+2。
運算符
乘的符號為*,有的版本的shell為\*
運算 |
運算符 |
說明 |
| 加 | + |
用法:$(expr $a 運算符 $b)或`expr $a 運算符 $b` 注意運算符與數字間的空格 val=$(expr 2 + 2) 或 val=`expr 2 + 2`
|
| 減 | - | |
| 乘 | * | |
| 除 | / | |
| 取余 | % | |
| 賦值 | = | |
| 相等 | == |
1、條件判斷要放在[ ]之內 if [ $a == $b ] then echo 'a等于b' else echo 'a不等于b' fi 2、返回true或false |
| 不等 | != |
2)關系運算符
關系運算符只支持數字和純數字字符串,不支持其他字符串。
用法:
[$a 運算符 $b]
運算符之前還有-
運算符:
運算 |
運算符 |
說明 |
| == | -eq |
真:true 假:false |
| != | -ne | |
| > | -gt | |
| < | -lt | |
| ≥ | -ge | |
| ≤ | -le |
可以通過$?查詢最后一次運算結果:
- 0:true
- 非0:false
例子
判斷當前輸入的用戶是否存在。如果存在則提示“用戶存在”,否則提示“用戶不存在”。
注:如果要在shell腳本使用linux命令,可以使用$()或者反引號包裹命令;
[root@localhost ~]$ vim demo.sh #!/bin/bash #接受用戶的輸入 read -p '請輸入需要查詢的用戶名:' username #獲取指定用戶名在passwd文件中出現的次數 count=$(cat /etc/passwd | grep $username | wc -l) #判斷出現的次數,如果次數=0則用戶不存在,反之存在 if [ $count == 0 ] then echo '用戶不存在' else echo '用戶存在' fi
3)邏輯運算符(或與非)
運算 |
運算符 |
說明 |
用法注意[ ]與左右表達式之間的空格 |
| 或 | -o |
o代表or [表達式1 -o 表達式2] 1 2其中有一個為true才返回true |
[ $a -lt 20 -o $b -gt 100 ] |
| 與 | -a |
a代表and [表達式1 -a 表達式2] 1 2都為true才返回true |
[ $a -lt 20 -a $b -gt 100 ] |
| 非 | ! | [! 邏輯運算] | [ ! false ]返回true |
4)字符串運算符
運算符 |
說明 |
例子注意①[ ]與左右表達式之間的空格 ②表達式 與 運算符之間的空格 |
| = | 兩個字符串是否相等,是則返回true | [ $a = $b ] |
| != | 兩個字符串是否相等,否則返回true | [ $a != $b ] |
| -z | 字符串長是否為0,是則返回true | [ -z $a ] |
| -n | 字符串長是否為0,否則返回true | [ -n $a ] |
|
str (無運算符) |
字符串是否為空,不為空則返回true | [ $a ] |
5)文件測試運算符
用于檢測Unix/Linux文件的各種屬性
運算符用法都是:test 運算符 file |
說明都是檢測是否是某種文件 |
| -b | 塊設備文件 |
| -c | 字符設備文件 |
| -d | 目錄 |
| -f | 普通文件(既非目錄,亦非設備) |
| -g | SGID |
| -k | 粘著位 |
| -p | 有名管道 |
| -u | SUID |
| -r | 可讀 |
| -w | 可寫 |
| -x | 可執行 |
| -s | 空(文件大小是否大于0) |
| -e | 存在 |
用法
if test -e ./test1.sh then echo '文件已存在!' else echo '文件不存在!' fi
六、流程語句
下文所有的條件表達式中的運算符,在第五節運算符中有說明
1)if條件
用法
①
if [ 條件 ] then 表達式1 else 表達式2 fi ② if [ 條件1 ] then 表達式1 elif[ 條件2 ] then 表達式2 ... else 所有條件都不成立時,執行該表達式 fi
注意:
1、if和中括號、中括號和內部條件之間均有不能省略的空格!!
if [ 表達式 ];then 表達式省略空格會導致
-
錯誤的替換/bad subsitution
-
未預期的符號 `then' 附近有語法錯誤
2、then之后的表達式不能省略,即不能寫一個空if語句,不然會報錯
-
未預期的符號 `fi' 附近有語法錯誤
例子
①統計根分區使用率
[root@localhost ~]$ vi sh/if1.sh #!/bin/bash #統計根分區使用率 rate=$(df -h | grep "/dev/sda2" | awk '{print $5}’| cut -d "%"-f1) #把根分區使用率作為變量值賦予變量rate if [ $rate -ge 80 ] #判斷rate的值如果大于等于80,則執行then程序 then echo "Warning!/dev/sda3 is fu11!!" #打印警告信息。在實際工作中,也可以向管理員發送郵件。 fi
②創建目錄
[root@localhost ~]$ vi sh/add_dir.sh #!/bin/bash #創建目錄,判斷是否存在,存在就結束,反之創建 echo "當前腳本名稱為$0" DIR="/media/cdrom" if [ ! -e $DIR ] then mkdir -p $DIR fi echo "$DIR 創建成功"
③備份mysql數據庫
[root@localhost ~]$ vi sh/bakmysql.sh #!/bin/bash #備份mysql數據庫。 ntpdate asia.pool.ntp.org &>/dev/null #同步系統時間 date=$(date +%y%m%d) #把當前系統時間按照“年月日”格式賦子變量date size=$(du -sh/var/lib/mysql) #統計mysql數據庫的大小,并把大小賦予size變量 if [ -d /tmp/dbbak ] #判斷備份目錄是否存在,是否為目錄 then #如果判斷為真,執行以下腳本 echo "Date : $date!" > /tmp/dbbak/dbinfo.txt #把當前日期寫入臨時文件 echo "Data size : $size" >> /tmp/dbbak/dbinfo.txt #把數據庫大小寫入臨時文件 cd/tmp/dbbak #進入備份目錄 tar -zcf mysql-lib-$date.tar.gz /var/lib/mysql dbinfo.txt &> /dev/null #打包壓縮數據庫與臨時文件,把所有輸出丟入垃圾箱(不想看到任何輸出) rm -rf /tmp/dbbak/dbinfo.txt #刪除臨時文件 else mkdir /tmp/dbbak #如果判斷為假,則建立備份目錄 echo "Date : $date!" > /tmp/dbbak/dbinfo.txt echo "Data size : $size" >> /tmp/dbbak/dbinfo.txt #把日期和數據庫大小保存如臨時文件 cd /tmp/dbbak tar -zcf mysql-lib-$date.tar. gz dbinfo.txt /var/lib/mysql &> /dev/null #壓縮備份數據庫與臨時文件 rm -rf/tmp/dbbak/dbinfo.txt #刪除臨時文件 fi
④判斷apache是否啟動,如果沒有啟動則自動啟動
[root@localhost ~]$ vi sh/autostart.sh #!/bin/bash #判斷apache是否啟動,如果沒有啟動則自動啟動 port=$(nmap -sT 192.168.4.210 | grep tcp | grep http | awk '{print $2}’) #使用nmap命令掃描服務器,并截取 apache服務的狀態,賦予變量port #只要狀態是open,就證明正常啟動 if [ "$port" == "open"] #如果變量port的值是“open” then echo "$(date) httpd is ok!” >> /tmp/autostart-acc.log #則證明apache 正常啟動,在正常日志中寫入一句話即可 else /etc/rc.d/init.d/httpd start &>/dev/null #否則證明apache沒有啟動,自動啟動apache echo "$(date) restart httpd !!" >> /tmp/autostart-err.log #并在錯誤日志中記錄自動啟動apche 的時間 fi
⑤判斷用戶輸入的是什么文件
[root@localhost ~]$ vi sh/if-elif.sh #!/bin/bash #判斷用戶輸入的是什么文件 read -p "Please input a filename: " file #接收鍵盤的輸入,并賦予變量file if [ -z "$file” ] #判斷file變量是否為空 then echo "Error, please input a filename" #如果為空,執行程序1,也就是輸出報錯信息 exit 1 #退出程序,并返回值為Ⅰ(把返回值賦予變量$P) elif [ ! -e "$file” ] #判斷file的值是否存在 then echo "Your input is not a file!" #如1果不存在,則執行程序2 exit 2 #退出程序,把并定義返回值為2 elif [ -f "$file” ] #判斷file的值是否為普通文件 then echo "$file is a regulare file!” #如果是普通文件,則執行程序3 elif [ -d "$file” ] #到斷file的值是否為目錄文件 then echo "$file is a directory!" #如果是目錄文件,網執行程序4 else echo "$file is an other file!” #如果以上判斷都不是,則執行程序5 fi
2)case條件(類似C中的switch case)
用法
case $變量名 in 值1|值2|值3) 程序1;; 值n) 程序2;; #其他分支... *) 若果以上分支都沒命中,則執行該程序(相當于default);; esac
注意事項:
- 分支程序末尾的兩個分號;;
- in后邊判斷值時,只有右半括號),沒有左半括號;
- 以case開始,以esac結束。
- 多個并列的值用 | 分隔。
3)for循環
用法
①直接把每次循環時的值都給出了(類似python中的for i in ...)
for 變量 in 值1 值2 值3 ... do 程序 done
for 變量 in { 起始值1..終值n }
do
程序
done
注意:
- 值與值間用空格分隔;
- 以上多個值可以用一個List或者文件等具有多個值的對象代替;
例子
- 打印一些值
for time in morning noon afternoon evening do echo "This time is $time!" done
- 打印從1到100
for i in {1..100} do echo $i done
- 批量解壓縮腳本
for i in $(cat ls.log) ` #或者這樣寫for i in `cat ls.log` #讀取ls.log文件的內容,文件中有多少個值,就會循環多少次,每次循環把文件名賦予變量i do tar -zxf $i &>/dev/nulldone rm -rf /lamp/ls.log
②自增式循環(類似C中for(i=1;i<=100;i++))
for (( 初始值;循環控制條件;變化 )) do 程序 done
例子
- 從1加到100
s=0 for (( i=1;i<=100;i=i+1 )) do s=$(( $s+$i )) done echo "The sum of 1+2+...+100 is : $s"
注意
- for后邊跟兩個括號(())
- 值、括號、運算符之間兩兩空格(這一項還不確定,因為我發下現沒空格也能運行)
③用for循環讀取文件中的值
學習自:bash如何使用for循環按行讀入文本文件 - 簡書
以空格為分隔符
for i in $`cat t.txt` 或 for i in $(cat t.txt) do echo $i done
以換行符為空格符
IFS=$'\n' for i in $''`cat t.txt` do echo $i done
4)while循環
用法
while [ 條件1 ] do 程序 done
例子
- 從1加到100
[root@localhost ~]$ vi sh/addnum.sh #!/bin/bash #從1加到100 i=1 s=0 #給變量i和變量s賦值 while [ $i -le 100 ] #如果變量i的值小于等于100,則執行循環 do s=$(( $s+$i )) i=$(( $i+1 )) done echo "The sum is: $s"
5)until循環
與while的區別在于until是在條件不成立時才進行循環
用法
until [ 條件1 ] do 程序 done
例子
- 從1加到100
[root@localhost ~]$ vi sh/until.sh #!/bin/bash #從1加到100 i=1 s=0 #t給變量i和變量s賦值 until [ $i -gt 100 ] #循環直到變量i的值大于100,就停止循環 do s=$(( $s+$i )) i=$(( $i+1 )) done echo "The sum is: $s"
6)流程控制(跳出循環):break與continue,
用途和其他語言的中的相同,break——跳出循環,continue——繼續下次循環
[root@localhost ~]$ vi sh/break.sh #!/bin/bash #演示continue for (( i=1;i<=10;i=i+1 )) #循環十次 do if ["$i" -eq 4 ] #如果變量i的值等于4 then continue #退出換成continue fi echo $i #輸出變量i的值 done
[root@localhost ~]$ vi sh/continue.sh #!/bin/bash #演示continue for (( i=1;i<=10;i=i+1 )) #循環十次 do if ["$i" -eq 4 ] #如果變量i的值等于4 then continue #退出換成continue fi echo $i #輸出變量i的值 done
7)函數
用法
#定義 function 函數名 () { 程序 } #調用 函數名 $參數1 $參數2
例子
接收用戶輸入的數字,然后從1加到這個數字
1 [root@localhost ~]$ vi sh/function.sh 2 #!/bin/bash 3 #接收用戶輸入的數字,然后從1加到這個數字 4 5 function sum () { 6 #定義函數sum 7 s=0 8 for (( i=0; i<=$num;i=i+1 )) 9 #循環直到i大于$1為止。$1是函數sum 的第一個參數 10 #在函數中也可以使用位置參數變量,不過這里的$1指的是函數的第一個參數 11 do 12 s=$(( $i+$s )) 13 done 14 echo "The sum of 1+2+3...+$1 is :$s" 15 #輸出1加到$1的和 16 } 17 18 read -p "Please input a number: " -t 30 num 19 #接收用戶輸入的數字,并把值賦予變量num 20 y=$(echo $num | sed 's/[0-9]//g') 21 #把變量num的值替換為空,并賦予變量y 22 23 if [ -z "$y"] 24 #判斷變量y是否為空,以確定變量num中是否為數字 25 then 26 sum $num 27 #調用sum函數,并把變量num的值作為第一個參數傳遞給sum函數 28 else 29 echo "Error!! Please input a number!" 30 #如果變量num 的值不是數字,則輸出報錯信息 31 fi
注意
- 8行的$num,這個num是外部已經命名的變量num,參數是沒有名字的,只以$1、$2……加以分辨;
- 14行的$1代表的才是傳入參數,$數字的具體用法參照預定義變量一節。
七、字符串
1)正則表達式
元字符 |
說明 |
例子 |
| \ | 轉義符,將特殊字符轉為一般字符 | a\.b 只能匹配 a.b |
| ^ | 匹配開頭 | ^tux 匹配以tux開頭的行 |
| $ | 匹配結尾 | tux$ 匹配以tux結尾的行 |
| . | 除換行符\n外的任意單個字符 | ab.匹配任意ab+單字符 |
| [ ] | 匹配[ ]中的任意單字符 |
coo[kl]匹配cook和cool |
| [^] | 匹配除了[^ ]列出的單字符 | 123[^45]匹配123x,x是除了4和5外的其他任意字符 |
| [-] | 匹配某個范圍內的字符(必須遞增) | [0-9]可以匹配0-9的任意單個數字 |
| ? | 匹配之前的項0或1次 | colou?r可以匹配0或1次u,即color或colour |
| + | 匹配之前的項1或多次 | sa-6+可以匹配多個6,即sa-6、sa-666 |
| * | 匹配之前的項人一次 | co*l可以匹配任意個o,即col、cool、coool |
| () | 匹配某個子串 | ma(trix)?可以匹配matrix和ma |
| {n} | 匹配之前的項n次 | [0-9]{3}可以匹配任意三位數 |
| {n,} | 至少匹配之前的項n次 | [0-9]{2,}匹配一個兩位或以上的數 |
| +n,m} | 最少匹配n次,最多匹配m次 | [0-9]{2,5}匹配一個2至5位數 |
| | | 或 | ab(c|d)匹配c或d,即abc和abd均可 |
2)字符截取、替換
①cut——列提取
用法
cut [選項] 文件名
選項
選項 |
說明 |
| -f |
列號;提取第幾列 多列用逗號,分隔,如cut -f 2,3 |
| -d | 分隔符;以指定分隔符分割列 |
| -n | 取消分割多字節字符 |
| -c |
字符范圍();不依賴分隔符來區分列,而是通過字符范圍(從第幾個字符開始分割)進行字符提取: n-:從第n個字符到行尾; n-m:從第n到第m個字符; -m:從第一個到第m個字符; |
| --complement | 補全被選擇的字符或字段 |
| --out-delimiter | 輸出內容為字段分隔符 |
說明:
- 默認分隔符為制表符,即TAB。
例子
-
創建文件
[root@localhost ~]$ vi student.txt id name gender mark 1 liming m 86 2 sc m 67 3 tg n 90
-
分割
[root@localhost ~]$ vi student.txt id name gender mark 1 liming m 86 2 sc m 67 3 tg n 90 [root@localhost ~]$ cut -f 2 student.txt #提取第二列內容 [root@localhost ~]$ cut -f 2,3 student.txt #提取第2和3列的內容
[root@localhost ~]$ cut -c 8- student.txt #每行從第八個字符開始提取到行尾,好像很亂啊,那是因為每行的字符個數不相等啊
8-:提取到行尾
8:提取第8個
-8:從行首提取到第8個 [root@localhost ~]$ cut -d ":" -f 1,3 /etc/passwd #以“:”作為分隔符,提取/etc/passwd_文件的第一列和第三列
十、字符處理命令
1)sort排序命令
-
用法
sort [選項] 文件名 -
選項
選項
說明
-f 忽略大小寫 -b 忽略每行前邊的空白部分 -n 以數值型排序,默認用字符串型排序 -r 反向排序 -u 刪除重復行。同uniq命令 -t 指定分隔符,默認TAB -k n[,m] 按照指定的字段范圍排序。從第n字段開始,m字段結束(默認到行尾) - 例子
默認是用每行開頭第一個字符來進行排序的:[root@localhost~]$ sort /etc/passwd #排序用戶信息文件
反向排序用-r選項:
[root@localhost~]$ sort -r/etc/passwd #反向排序
如果要指定排序的字段,需要用"-t"選項指定分割符,并使用"-k"選項指定字段號。假設我們要用UID字段排序/etc/passwd文件:
[root@localhost~]$ sort -t ":" -k 3,3 /etc/passwd #指定分隔符是“:”,用第三字段開頭,第三字段結尾排序,就是只用第三字段排序
如果寫為-k 3,代表從第三字段到行尾排序。
2)uniq取消重復行
-
用法
[root@localhost~]$ uniq [選項] 文件名 選項: -i:忽略大小寫
3)wc統計命令
wc [選項] 文件名 選項: -l:只統計行數 -w:只統計單詞數 -m:只統計字符數
十一、一些函數
1、eval
學習自:shell 中的 eval_shell eval-CSDN博客
用法:eval 指令字符串(不加用引號)
說明:執行eval后的指令字符串,相當于我們直接在控制臺輸入指令字符串并按回車,
通常配合$變量來實現靈活、批量對指令中的某個字段的替換例子
1)指令字符串中只有一個指令關鍵字,相當于直接執行后邊的指令
echo $NAME eval echo $NAME
此時二者是等價的。
2)指令字符串中有兩個指令關鍵字,相當于先執行外層指令,再執行外層指令執行完畢后輸出的指令
myfile="cat test.txt" eval echo $myfile #先執行echo $myfile,輸出cat test.txt #再執行eval cat test.txt,輸出test.txt的內容
3)允許嵌套變量,此時在要嵌套的變量之前加/$
#輸出最后一個參數 echo "Last argument is $(eval echo \$$#)"

浙公網安備 33010602011771號