一、語法
數據類型
字符串
如果沒有特殊說明,數據類型默認都是字符串。常見字符串操作有:
已知變量str=abcabc
1、切片
echo ${str:1:3} #${varName:offset:size},分頁取子串
echo ${str:2} #${varName:offset},偏移offset個字符取至末端
echo ${str: -2} #${varName: -number},取末端number個字符,冒號后必須跟空格
2、查找刪除
${var#pattern} #從左邊開始搜索命中pattern最短的串(匹配最簡單),并刪除
${var##pattern} #從左邊開始搜索命中pattern最長的串(匹配最貪婪),并刪除
${var%pattern} #從右邊開始搜索命中pattern最短的串(匹配最簡單),并刪除
${var%%pattern} #從右邊開始搜索命中pattern最長的串(匹配最貪婪),并刪除
3、查找替換
echo ${str/bc/d} #${varName/pattern/substi},查找變量中第一次匹配到的pattern字符串替換為substi
echo ${str//bc/d} #${varName//pattern/substi},查找變量中所有匹配到的pattern字符串替換為substi
如果d為空,則意味著查找刪除(和前面的刪除不一樣,此時刪除的內容可以不連續)
echo ${str/bc/} #${varName/pattern/},查找變量中第一次匹配到的pattern字符串并刪除
echo ${str//bc/} #${varName//pattern/},查找變量中所有匹配到的pattern字符串全部刪除
假設我們定義了一個變量為 file=/dir1/dir2/dir3/my.file.txt
${file#*/}:刪掉第一個 / 及其左邊的字符串:dir1/dir2/dir3/my.file.txt
${file##*/}:刪掉最后一個 / 及其左邊的字符串:my.file.txt
${file#*.}:刪掉第一個 . 及其左邊的字符串:file.txt
${file##*.}:刪掉最后一個 . 及其左邊的字符串:txt
${file%/*}:刪掉最后一個 / 及其右邊的字符串:/dir1/dir2/dir3
${file%%/*}:刪掉第一個 / 及其右邊的字符串:(空值)
${file%.*}:刪掉最后一個 . 及其右邊的字符串:/dir1/dir2/dir3/my.file
${file%%.*}:刪掉第一個 . 及其右邊的字符串:/dir1/dir2/dir3/my
5、空/非空處理
${varName:-value} #如果varName為空則返回value,并不會修改變量本身,輸出的value是命令執行結果
${varName:=value} #如果varName為空,則返回value并賦值給varName,不為空則返回其本省值,等同于varName=${varName:-value}
${varName:+value} #如果varName為空不做處理,不為空則返回value,varName本身不變
${varName:?errorInfo} #varName如果為空則返回errorInfo,并會輸出到錯誤輸出中,不為空則返回其原值
6、其他
${varName^^} #將字符串中的小寫字母轉為大寫字母
${varName,,} #將字符串中的大寫字母轉為小寫字母
整數
declare -i varName=value
數組
- 定義
declare -a arrayName #定義數組
- 賦值
index從0開始(至少/bin/bash是這樣)
可以直接賦值并定義
arrayName[IDX]=value #單個元素賦值
arrayName=([IDX]=value [IDX]=value) #部分元素賦值
arrayName=(value1 value2 value3) #所有元素完全賦值
對于 arrayName=(/dir/file)
, value是文件目錄時會讀取文件列表輸入到數組中, 以文件名進行排序
- 引用
${arrayName[IDX]} #引用單個元素,不指定下標則表示引用第0個元素
${arrayName[@]} #引用所有元素,也可用${arrayNanme[*]}
${#arrayName[@]} #返回數組元素個數,也可用${#arrayName[*]}
${#ARRAY_NAME[INDEX]} #表示此位置的變量的字符個數
- 切片
${arrayName[@]:offset:number} #從offset開始偏移number個元素
${arrayName[@]:offset} #從offset開始偏移至結尾
- 撤銷、追加元素
arrayName[${#arrayName[@]}]=value #在數組最后追加一個元素并賦值
unset arrayName[IDX] #撤銷某個元素,不會將其后面的元素下標向前推進1,但是在使用${#arrName[@]}時,數組元素個數會減1,即變為稀疏數組
- 遍歷
for e in $arr; doecho $e
done
關聯數組
低版本/bin/bash不支持
關聯數組使用字符串作為下標,而不是整數。關聯數組也稱“鍵值對(key-value)”數組,key為下標,value為元素值
- 聲明
declare -A 數組名
(必須先聲明才能賦值)
- 賦值
#一次多個賦值
declare -A map=([jack]="18" [amy]="16" [tom]="20")
#單個賦值
map[john]="18"
- 查詢/取值
echo ${map[key]} #訪問“key”對應的value值
echo ${map[@]} #獲取關聯數組的所有元素值
echo ${!數組名[@]} #獲取關聯數組的所有下標值
echo ${#數組名[@]} #獲取關聯數組元素的個數
運算通用
echo $((a+=1))
echo $((a-=1))
echo $((a*=2))
echo $((a/=2))
echo $((a%=2))
echo $((a<<=2))
echo $((a>>=2))
echo $((a&=2))
echo $((a|=2))
echo $((a^=2))
算術運算
通過表達式來運算,兩種方式。方式1: 通過$((表達式))
,方式2: echo "表達式" | bc
,示例:
#加減乘除
echo "$((2+6/10))" #結果為2,先算除法最后截斷取整
echo "2+6/10" |bc #結果一致
#取模
echo "100%3" | bc
echo "$((100%3))"
#冪運算
echo "10^3" | bc
位運算
echo $((16<<2)) #左移
echo $((16>>2)) #右移
echo $((3&2)) #與運算
echo $((4|2)) #或運算
echo $((~2)) #非運算
echo $((6^2)) #異或
邏輯運算
對于一個命令執行,$?
如果返回0則為true,否則為false
對于一個表達式,如果為true結果則為1,否則為0
- 比較表達式
echo $((1<0)) #小于
echo $((1<=0)) #小于等于
echo $((1>0)) #大于
echo $((1>=0)) #大于等于
echo $((1==0)) #相等
echo $((1!=0)) #不相等
- 邏輯表達式
echo $((1>0 && 2>0)) #邏輯與
echo $((1>0 || 2<0)) #邏輯或
echo $((!1>0)) #邏輯非
echo $((1>0?2:3)) #三元
條件測試
格式為[ 條件表達式 ]
(注意前后有空格)
- 整數比較
[ $num1 -gt $num2 ]
-gt: 大于
-lt: 小于
-ge: 大于等于
-le: 小于等于
-eq: 等于
-ne: 不等于
- 字符串比較
比較
[[ "$str1" > "$str2" ]]
>: 大于則為真
<: 小于則為真
>=:大于等于則為真
<=:小于等于則為真
==:等于則為真
!=:不等于則為真
空判斷
-n String: 是否不空,不空則為真,空則為假
-z String: 是否為空,空則為真,不空則假
文件判斷
-a FILE:存在則為真;否則則為假;
-e FILE:存在則為真;否則則為假;
-f FILE: 存在并且為普通文件,則為真;否則為假;
-d FILE: 存在并且為目錄文件,則為真;否則為假;
-L/-h FILE: 存在并且為符號鏈接文件,則為真;否則為假;
-b: 存在并且為塊設備,則為真;否則為假;
-c: 存在并且為字符設備,則為真;否則為假
-S: 存在并且為套接字文件,則為真;否則為假
-p: 存在并且為命名管道,則為真;否則為假
-s FILE: 存在并且為非空文件則為值,否則為假;
-r FILE:文件可讀為真,否則為假
-w FILE:文件可寫為真,否則為假
-x FILE:文件可執行為真,否則為假
file1 -nt file2: file1的mtime新于file2則為真,否則為假;
file1 -ot file2:file1的mtime舊于file2則為真,否則為假;
組合條件測試
與:[ condition1 -a condition2 ]condition1 && condition2
或:[ condition1 -o condition2 ]condition1 || condition2
非:[ -not condition ]! condition
與:COMMAND1 && COMMAND2
COMMAND1如果為假,則COMMAND2不執行
或:COMMAND1 || COMMAND2
COMMAND1如果為真,則COMMAND2不執行
非:! COMMAND
如果只是簡單的單分支或ifelse則可以直接用test命令+&&||等來執行命令
,test命令不要加方框
read -p "Input a fileName: " fileName
test -z $fileName && echo "You must input a fileName" && exit 0
test ! -e $fileName && echo "file not exist" && exit 0
test -f $fileName && fileType="regulare file"
test -d $fileName && fileType="directory"
簡單命令執行流
順序執行
:單行執行多個語句時,用分號;
隔開即可。如:ls;ls;ls
&&執行
:前一個命令執行成功(即$?
為0),后面的才執行。如:ls /tmp/abc && touch /tmp/abc/111.txt
||執行
:前一個命令執行成功后一個命令不執行,前一個命令執行失敗后一個命令會執行。如:mkdir /tmp/abc && mkdir /tmp/abc
簡單的ifelse執行流:ls /tmp/abc || mkdir /tmp/abc && touch /tmp/abc/111.txt
條件執行
- if
#1. if語句之單分支
語句結構:
if 測試條件;then選擇分支
fi#2、if語句之雙分支
語句結構:
if 測試條件;then選擇分支1
else選擇分支2
fi#3、if語句之多分支
語句結構:
if 條件1;then分支1
elif 條件2;then分支2
elif 條件3;then分支3...
else分支n
fi
- case語法
case 變量引用 in
PATTERN1)分支1
;;
PATTERN2)分支2
;;
...
*)分支n
;;
esac
循環
- for循環
# 1、for語句格式一
for 變量名 in 列表; do循環體
donedeclare -i sum=0
for i in {1..100}; dolet sum+=$i
done
echo $sum# 2、for語句格式二
for ((初始條件;測試條件;修改表達式)); do循環體
donedeclare -i sum=0
for ((counter=1;$counter <= 100; counter++)); dolet sum+=$counter
done
echo $sum
- while循環
# 1. 語句結構
while 測試條件; do循環體
donedeclare -i counter=1
declare -i sum=0
while [ $counter -le 100 ]; doif [ $[$counter%2] -eq 0 ]; thenlet sum+=$counterfilet counter++done
echo $sum# 2. while特殊用法:遍歷文本文件
語句結構:
while read 變量名; do循環體
done < /path/to/somefile
變量名,每循環一次,記憶了文件中一行文本while read line; douserID=`echo $line | cut -d: -f3`groupID=`echo $line | cut -d: -f4`if [ $[$userID%2] -eq 0 -a $userID -eq $groupID ]; thenecho $line | cut -d: -f1,3,7fi
done < /etc/passwd
- until語句
# 1.語句結構:
until 測試條件; do循環體
done
測試條件為假,進入循環;測試條件為真,退出循環例:求100以內所有偶數之和,要求使用取模方法(until實現)
#!/bin/bash
declare -i counter=1
declare -i sum=0
until [ $counter -gt 100 ]; doif [ $[$counter%2] -eq 0 ]; thenlet sum+=$counterfilet counter++
done
echo $sum
- 循環控制
1)break:提前退出循環;
2)break [N]: 退出N層循環;N省略時表示退出break語句所在的循環;
3)continue: 提前結束本輪循環,而直接進入下輪循環;
4)continue [N]:提前第N層的循環的本輪循環,而直接進入下輪循環;
- 一直循環的語法
#1.while體
while true; do循環體
done
#2. until體
until false; do循環體
done
重定向
標準輸入:<
和<<
標準輸出:>
和>>
(覆蓋和追加)
標準錯誤輸出:2>
和2>>
(覆蓋和追加)
- 常用場景
標準輸出和標準錯誤輸出,輸出到不同位置:ls / > right.txt 2> error.txt
標準輸出和標準錯誤輸出,輸出到同一位置:ls / > right.txt 2>&1
或 ls / &> right.txt
忽略標準輸出和標準錯誤(即輸出到/dev/null:ls / > /dev/null 2>&1
或 ls / &> /dev/null
通過鍵盤輸入來創建文件: cat > cat2.txt
通過文件來代替鍵盤輸入: cat > cat2.txt < existFile.txt
正則表達式
和常規正則比較,需要注意
支持
\w
、\d
等,但常規的\d+
時需要用\d\+
搜索
\d{2,5}
這種指定出現次數時,好括號也要轉義\d\{2,5\}
用到正則的分組功能時,定義分組時小括號也要轉義。如
\(\d\+\)
函數
- 函數定義
格式 function functionName(){command1;command2;}
,其中function可省略
函數默認返回值為0,通過$?訪問,也可以返回特定值
#標準函數(無返回值,或者說默認返回值為0)
function my_func1(){echo "無返回值函數,參數1:$1"echo "無返回值函數,參數2:$2"
}
my_func1 "hello" "world"
echo -e "返回值是$?\n"#省略function
my_func2(){echo "省略function函數"
}
my_func2
echo -e "返回值是$?\n"#有特定返回值
function sum(){echo "有特定返回值"local a=$1local b=$2return $(($a+$b))
}
sum 1 3
echo -e "返回值是$?"
- 調用其他shell的函數
定義函數99_source_function_define.sh
#僅定義函數不調用,如果在當前腳本里調用,則被其他shell腳本source時同時會觸發調用
function call_by_other_shell(){echo "我正在被其他的shell腳本調用"
}function hello(){echo "因為在方法定義腳本里call函數了,雖然沒被調用,但被source時同樣也會被執行調用"
}
hello
調用上面shell函數代碼
#!/bin/bash
#調用其他shell腳本的函數前,先source。(會將source的shell文件全部先執行一遍)
source ./99_source_function_define.sh
call_by_other_shell
- 如何定義全局工具函數
將要用到的函數分類定義在一個文件里,要用的腳本里直接source后就能生效
引入其他腳本
通過source引入即可
命令的執行
- 絕對路徑:/usr/local/tomcat/start.sh param1 param2
- 相對路徑:./bin/start.sh
- 命令已添加到PATH:直接start.sh運行
- bash start.sh或sh start.sh
- source執行命令(是在父進程中執行)
- 調試shell:sh -x start.sh
- sh -n start.sh僅檢查是否有語法問題
- sh -v start.sh 在執行前先將script輸出到屏幕上
變量
變量分類
本地變量
vname=value #作用域為整個bash進程可以使用
局部變量
local varname=value #作用域為當前代碼段(一般在定義函數內使用)
環境變量
export varname=value #作用域為當前shell進程及其后代進程,不能影響到其父進程
varname=value ; export varname
特殊變量
$1,$2,$3
#shell命令的參數,從1開始
$?
#上一個命令執行狀態碼(0為成功)
$0
#獲取當前執行的shell腳本的文件名
$#
#當前shell命令參數的總個數
$*
#所有變量
有類型變量
格式:declare [選項] 變量名
-r 將變量設置為只讀屬性
-i 將變量定義為整型數
-a 將變量定義為數組
-A 將變量定義為關聯數組
-f 顯示此腳本前定義過的所有函數名及其內容
-F 僅顯示此腳本前定義過的所有函數名
-x 將變量聲明為環境變量
-l 將變量值轉為小寫字母 declare –l var=UPPER
-u 將變量值轉為大寫字母 declare –u var=lower
變量引用
‘${name}’ #單引號變量不會被替換
“${name}” #雙引號變量會被替換
如何定義全局變量
將要用到的變量全部定義在一個文件里,要用的腳本里直接source后就能生效
二、常用內置命令
- alias
設置別名: alias ll="ls -lat"
, 如果要永久生效,一般放在.bash_profile里
- source
讀取指定文件里定義好的環境變量到當前進程,該文件可以不用以#!/bin/bash
開頭
讀取指定文件里定義好的函數到當前進程,該文件可以不用以#!/bin/bash
開頭
- bash/exit
bash是開始子進程,exit是結束子進程
- env/export查看環境變量
不帶參數,兩者結果是一樣的。set除了查看環境變量還查看進程內的變量
- RANDOM
是一個特殊的內置環境變量,返回0~32767的隨機整數。echo $RANDOM
如果要0-n范圍內,例如: echo "$RANDOM*100/32767" |bc
- PS1
特殊的環境變量,用來設置shell命令欄的格式
略
- read
變量來自于輸入流: read -p
-p prompt
: 顯示提示信息,提示內容為 prompt
-t seconds
: 設置超時時間秒。指定時間內沒有輸入完成 將會返回一個非 0 的退出狀態,讀取失敗。
-s
: 靜默模式(Silent mode),不會在屏幕上顯示輸入的字符。當輸入密碼和其它確認信息的時候,這是很有必要的
-d delimiter
: 用字符串 delimiter 指定讀取結束的位置,而不是一個換行符(讀取到的數據不包括 delimiter)
read variable < file1.txt
: 讀取文件作為變量
-a array
: 把讀取的數據賦值給數組 array,從下標 0 開始。
- bc
根據表達式進行算術計算(只能算整數):echo "expression" > | bc
echo "3+5/6" | bc
- history
history n
: 列出最近n條
history -c
: 清除history(內存中)
history -r file
: 從文件中讀取history到shell中(內存),一般file為~目錄下shell對應的history文件。如~/.bash_history
history -w file
: 將shell中(內存)的history寫到文件中(會覆蓋)
!number
:重新執行history對應number
的命令
history存儲的條數由環境變量$HISTSIZE
決定,一般在/etc/profile
中被定義
執行報錯的命令,也會被記錄下來。用戶注銷時會將shell中的history刷到文件
- cut
對每行字符串進行切割,取出想要的部分
cut -d'分隔符'
:指定分隔符
cut -f 切割后的某些段
:cut -f2
取第2段,cut -f1,3,5
取某幾段,cut -f1-3
取連續1段。(index從1開始)
cut -c 基于字符切割的某些字符
:cut -c2
取第2個字符,cut -c1,3,5
取1/3/5個字符,cut -f1-3
取連續1段。(index從1開始)
cut對多空個相連的數據比較不好處理
- grep
如果某行中有我們需要的信息,就把這行拿出來
grep -i
: 忽略大小寫
grep -n
: 輸出行號
grep -v
: 反選,輸出沒有命中內容的行
grep -An
: 同時輸出命中行的后n行(after)
grep -Bn
: 同時輸出命中行的前n行(before)
grep --color=AUTo
:可以高亮關鍵字,也可以設置為grep的alias
- sort
對行進行排序
-f
: 忽略大小寫差異
-b
: 忽略最前面的空格符部分
-r
:反向排序
-u
:就是uniq去重,相同數據僅出現一行
-n
:按數字排序(默認按文字),一般如純數字列可能有該需求(如122和2對比)
-t'分隔符' -kn
: 將當前行按分隔符切割后,指定切割后的第n段進行排序
- wc
參數源為一個文件或輸入流
-l
: 行數(實際按$\n的個數,如果最后一行不換行可能會漏統計1行)
-w
: 字符個數(英文單字,不包含)
-m
: 多個字符
如果不帶任何參數,就是輸入-l -w -m
- tee
能同時將標準輸出流,同時輸出到屏幕和文件
-a
: 輸出到文件時追加,默認覆蓋
示例:last |tee last1.txt|cut -d' ' -f1
追加:cat /etc/passwd | cut -d':' -f1 |tee -a last1.txt
- paste
格式:paste file...
,將多個文件的對應行拼接到一起,第一行(file1、file2…的第一行拼接)
-d
: 指定分隔符,默認為tab
最后一個file執行為-
:用在管道后面,-
表示管道的輸入
示例1:paste /etc/passwd /etc/shadow
示例2:cat productIds.txt | paste itemIds.txt 01_productName.txt - | head -3
- expand
格式:expand file
,將文件中的tab鍵換成8個空格
-t n
: 指定一個tab鍵替換成n個空格
unexpand作為正好相反,將空格替換成tab鍵
- cat
格式:cat file...
,依次將多個文件打印
-t
: 能看到特殊的格式,如tab為^I
利用將多個文件合成一個:cat file... >>targeFile
- split
split [-bl] file PREFIX
:將一個文件切割成多個文件,PREFIX表示切割后文件名的前綴。
split -b 20k
:按指定大小切割
split -l file
:按指定行數切割
- |
管道,管道前的輸出作為管道后命令的輸入
如果管道后的命令參數可以接受多個文件或文件參數不是在命令最后,則需顯式加上輸入流代參-
如ls / |split -l 5 - lsroot
和ls /etc/passwd | cat /etc/group -
- xargs
將輸入流的參數,以空格或換行符進行分隔得到參數列表。每個參數都執行xargs后面的命令
xargs最大的用處是,某些命令不支持管道,都通過xargs可以實現類似管道的效果
-E
:分析輸入流的參數截止標記,默認為EOF
-p
:每個參數執行xargs后面的命令時都詢問
-n
:每次執行xargs后面的命令時需要用到幾個參數,默認為1
如果xargs后面沒有命令,默認為echo每個參數
示例:cut -d':' -f1 /etc/passwd|tail -5 | xargs finger
ls subItemIda* | xargs -p -n1 cat
:每次都詢問,每次都只用一個參數
cut -d':' -f1 /etc/passwd| xargs -E'lp' finger
- awk
如果每行字段精細化處理,可以用awk
語法:awk '條件類型1 {command1} 條件類型2 {command2} ...' file
執行開始前對應條件為BEGIN
,執行結束后對應條件為END
如果每行都處理,可以省略條件直接command。awk的處理流程如下:
- 讀入第一行,并將第一行的數據按空格或tab分隔填入
$0
(總段數),$1
(第1段),$2
(第2段)等變量中- 依據條件類型的限制,依次判斷是否需要進行后面的動作
- 做完所有的動作與條件類型
- 若還有后續的行數據,則重復1-3步驟,直到讀完所有的數據
awk語句中能用到的內置變量(引用時不需要$)有:
NF:當前行的擁有的字段總數(同$0)
NR:目前awk所處理的行index(從1開始)
FS;目前行的分隔字符,默認是空格鍵
全局設置換行符:awd 'BEGIN {FS=":"}
- diff
格式:diff [-bBi] from-file to-file
,比較兩個文件的差異
-b
:忽略僅連續空格數量的差別,如love you
和love you
的區別
-B
:忽略空白行的差別
-i
:忽略大小寫的差別
- date
獲取指定格式的日期:date [+format] 支持%Y%y%m%w%d%H%M%S等變量
日期偏移:date -v[+|-]val[ymwdHMS]
- printf
%n 換行 相當于 \n
%c 單個字符
%d 十進制整數
%u 無符號十進制數
%f 十進制浮點數
%o 八進制數
%x 十六進制數
%s 字符串
%% 輸出百分號
“%8.2f”: 其中8表示總共打印8個字符(不夠往前充填空格),精度為小數點后2位
“%08f.2”: 其中8表示總共打印8個字符(不夠往前充填0),精度為小數點后2位
“%02d”: 打印整數, 不夠3三位左補零
- eval
將變量作為命令執行
express="cat tmp2.txt"
echo $express
eval "$express"