Shell提供了很多字符串和文件處理的命令,如awk、expr、grep、sed等命令,還有文件的排序、合並和分割等一系列的操作命令。下面重點總結下Shell字符串處理、文本處理以及各類命令及函數用法。
先從expr命令開始梳理,expr 引出通用求值表達式,可以實現算術操作、比較操作、字符串操作和邏輯操作等功能。
1) 計算字符串長度
字符串名為string,可以使用命令 ${#string} 或 expr length ${string} 兩種方法來計算字符串的長度。
若string中包括空格,則expr計算命令中需用雙引號引起來,即expr length "${string}"。(${#string}方法對於有無空格的字符串均可使用)
需要注意:expr length后面只能跟一個參數,string有空格會當作多個參數處理。
[root@kevin ~]# string="kevinisgood"
[root@kevin ~]# expr length ${string}
11
[root@kevin ~]# expr length "${string}"
11
[root@kevin ~]# echo ${#string} # 使用自帶shell函數讀取字符串長度
11
如果string字符串中有空格
[root@kevin ~]# string="kevin is good"
[root@kevin ~]# expr length ${string}
expr: syntax error
[root@kevin ~]# expr length "${string}"
13
[root@kevin ~]# echo ${#string}
13
注意:這里說到如果string字符串中有空格,則需要雙引號引起來。但如果是單引號引起來呢?
如果string字符串用單引號引起來,則統計出來的是"字符串去重和去掉空格"后的長度
[root@kevin ~]# echo ${string}
kevin is good
[root@kevin ~]# expr length '${string}' #使用單引號的話,統計的是"字符去重以及去掉空格"之后的長度
9
[root@kevin ~]# string="kevinisgood"
[root@kevin ~]# echo ${string}
kevinisgood
[root@kevin ~]# expr length '${string}'
9
2)匹配字符串長度
即匹配字符串開頭子串的長度!!使用命令 expr match ${string} $substring,表示在string字符串的開頭匹配substring字符串,返回匹配到的substring字符串的長度。若string開頭匹配不到則返回0,其中substring可以是字符串也可以是正則表達式。
expr匹配字符串長度的兩種格式:
# expr match "$string" '$substring'
# expr "$string" : '$substring'
[root@kevin ~]# string="zhongguo hao"
[root@kevin ~]# expr length "${string}"
12
[root@kevin ~]# expr match "${string}" "z.*"
12
[root@kevin ~]# expr match "${string}" "zho"
3
[root@kevin ~]# expr match "${string}" "hao"
0
注意:
"hao"盡管在string字符串中出現,但是未出現在string的開頭處,因此返回0!
所以說,expr match匹配的一定是字符串開頭子串的長度!!
后面的$substring可以是正則, $substring兩邊是單引號或雙引號無所謂。
[root@kevin ~]# string="KEvin IS good"
[root@kevin ~]# expr match "${string}" "[A-Z]*"
2
[root@kevin ~]# expr match "${string}" '[A-Z]*'
2
[root@kevin ~]# expr "${string}" : "[A-Z]*" #注意這種方法中就沒有了"match"參數
2
[root@kevin ~]# expr "${string}" : '[A-Z]*[a-z]*'
5
3)匹配字符串索引
expr的索引命令格式為:expr index ${string} $substring。
在字符串$string上匹配$substring中字符最早第一次出現的位置(從左到右,從1開始),匹配不到,expr index返回0。
簡單來說,就是找出子串在字符串中最早第一次出現的單個字符的位置!!
[root@kevin ~]# string="love beijing"
[root@kevin ~]# echo ${string}
love beijing
[root@kevin ~]# expr index ${string} "bei"
expr: syntax error
[root@kevin ~]# expr index "${string}" "bei" #注意string字符串中有空格,需要加雙引號
4
[root@kevin ~]# expr index "${string}" "jing"
8
[root@kevin ~]# expr index "${string}" "haa"
0
注意:
expr index命令中返回的是后面$substring中"子串字符中"最早第一次出現的位置。
"bei"子串中在${string}字符串中最早第一次出現的是"e",第4位置
"jing"子串中在${string}字符串中最早第一次出現的是"i",第4位置
"haa"匹配不到,所以返回0!
=== Bash Shell命令 ===
4)抽取字符串的子串
Shell提供兩種命令和expr實現抽取子串功能。
正着抽取(即從左到右)有兩種格式。(左邊默認從0開始標號)
格式一:{string:position} 從名稱為${string}的字符串的第$position個位置開始抽取子串,從0開始標號。
格式二:{string:position:length} 增加$length變量,表示從${string}字符串的第$position個位置開始抽取長度為$length的子串。
需要注意:都是從string的左邊開始計數抽取子串。
示例:
[root@kevin ~]# string="hello world wang"
[root@kevin ~]# echo ${string}
hello world wang
[root@kevin ~]# echo ${string:0} #從標號為0(即從第一個字母)開始截取,截取到結尾。
hello world wang
[root@kevin ~]# echo ${string:6} #從標號為6的字符開始截取,截取到結尾。
world wang
[root@kevin ~]# echo ${string:6:4} #從標號為6的字符開始截取,截取長度為4。
worl
[root@kevin ~]# echo ${string:6:0} #從標號為6的字符開始截取,截取長度為0。
[root@kevin ~]#
反着抽取(即從右到左)有兩種格式。(右邊默認從-1開始標號)
格式一:{string: -position}。需要謹記:冒號與橫杠間有一個空格!!!!
格式二:{string:(position)}。如果加了括號,則冒號后面加不加空格是一樣的效果!!!
[root@kevin ~]# echo ${string:-2} #冒號與"-"之間必要要有空格,否則截取無效!
hello world wang
[root@kevin ~]# echo ${string: -2}
ng
[root@kevin ~]# echo ${string:(-2)}
ng
[root@kevin ~]# echo ${string: (-2)}
ng
如果要想實現從右邊第幾個字符開始截取,截取長度為多少,則方法為:
{string:m-n:x} 表示從右邊第"m-n"個字符開始截取,截取長度為x
[root@kevin ~]# echo ${string:1-7:3}
d w
[root@kevin ~]# echo ${string:3-9:3}
d w
expr substr也能夠實現抽取子串功能,命令格式:expr substr ${string} $position $length,
這個與上面最大不同是expr substr命令從1開始進行標號!!!!
[root@kevin ~]# echo ${string}
hello world wang
[root@kevin ~]# echo ${string:3:5} #從0開始進行標號
lo wo
[root@kevin ~]# expr substr "${string}" 3 5 #從1開始標號
llo w
還可以使用正則表達式抽取子串的命令,但只能抽取string開頭處或結尾處的子串。
抽取字符串開頭處的子串:
格式一:expr match $string ''
格式二:expr $string : ''。 注意:其中冒號前后都有一個空格。
抽取字符串結尾處的子串:
格式一:expr match $string '.*'
格式二:expr $string : '.*'。 注意:.*表示任意字符的任意重復。一個.表示一個字符。
[root@kevin ~]# string="20181112hello WORld Good"
[root@kevin ~]# echo ${string}
20181112hello WORld Good
[root@kevin ~]# expr match "$string" '\([0-9]*\)' #這里的${string}最好使用雙引號引起來,因為字符串可能中有空格!如果沒有空格,就可以不用使用雙引號。
20181112
[root@kevin ~]# expr match "$string" "\([0-9]*\)"
20181112
[root@kevin ~]# expr "$string" : '\([0-9]*\)'
20181112
[root@kevin ~]# expr match "$string" '.*\(.\)'
d
[root@kevin ~]# expr match "$string" '.*\(..\)'
od
[root@kevin ~]# expr match "$string" '.*\(...\)'
ood
[root@kevin ~]# expr match "$string" '.*\(.....\)'
Good
[root@kevin ~]# expr "$string" : '.*\(.....\)'
Good
[root@kevin ~]# expr "$string" : '.*\(.........\)'
ORld Good
[root@kevin ~]# string="heLLO2018 world"
[root@kevin ~]# expr match "$string" '\([a-z]*\)'
he
[root@kevin ~]# expr match "$string" '\([a-Z]*\)'
heLLO
[root@kevin ~]# expr match "$string" '\([a-Z]*[0-9]*\)'
heLLO2018
[root@kevin ~]# expr match "$string" '\(.[a-Z]*[0-9]*\)'
heLLO2018
[root@kevin ~]# expr "$string" : '.*\(.........\)'
018 world
5)刪除字符串的子串
刪除字串是指將原字符串中符合條件的子串刪除。
從string開頭處刪除子串:
格式一:${string#substring} 刪除開頭處與substring匹配的最短子串。
格式二:${string##substring} 刪除開頭處與substring匹配的最長子串。其中substring並非是正則表達式而是通配符。
[root@kevin ~]# string="china IS niuBIlity2018"
[root@kevin ~]# echo ${string#c*i} #刪除c開頭到a的最短子串
na IS niuBIlity2018
[root@kevin ~]# echo ${string##c*i} #刪除c開頭到a的最長子串
ty2018
[root@kevin ~]# echo ${string#c*n}
a IS niuBIlity2018
[root@kevin ~]# echo ${string##c*n}
iuBIlity2018
[root@kevin ~]# echo ${string#ch*n} #刪除ch開頭到a的最短子串
a IS niuBIlity2018
[root@kevin ~]# echo ${string##ch*n} #刪除ch開頭到a的最長子串
iuBIlity2018
上面#或##后面的字符必須是${string}字符串的開頭子串!否則刪除子串就無效了!
[root@kevin ~]# echo ${string#i*n} #i不是開頭字符,所以刪除無效
china IS niuBIlity2018
[root@kevin ~]# echo ${string##i*n} #i不是開頭字符,所以刪除無效
china IS niuBIlity2018
另外:可以使用下面方式進行刪除:
${string#*substring} 刪除${string}字符串中第一個$substring及其之前的字符
${string##*substring} 刪除${string}字符串中最后一個$substring及其之前的字符
[root@kevin ~]# string="china IS niuBIlity2018"
[root@kevin ~]# echo ${string#*i} #刪除第一個i及其之前的字符
na IS niuBIlity2018
[root@kevin ~]# echo ${string##*i} #刪除最后一個i及其之前的字符
ty2018
也可以使用下面方法進行刪除
格式一:${string%substring*} 刪除${string}字符串中最后一個$substring及其之后的字符
格式二:${string%%substring*} 刪除${string}字符串中第一個$substring及其之后的字符
[root@kevin ~]# echo ${string}
china IS niuBIlity2018
[root@kevin ~]# echo ${string%i*}
china IS niuBIl
[root@kevin ~]# echo ${string%%i*}
ch
[root@kevin ~]# echo ${string%c*}
[root@kevin ~]# echo ${string%%c*}
[root@kevin ~]#
6)字符串替換
替換子串命令可以在任意處、開頭處、結尾處替換滿足條件的子串,其中的substring都不是正則表達式而是通配符。
在任意處替換子串命令:
格式一:${string/substring/replacement},僅替換第一次與substring相匹配的子串。
格式二:${string//substring/replacement},替換所有與substring相匹配的子串。
[root@kevin ~]# string="china IS niuBIlity2018"
[root@kevin ~]# echo ${string/i/#}
ch#na IS niuBIlity2018
[root@kevin ~]# echo ${string//i/#}
ch#na IS n#uBIl#ty2018
[root@kevin ~]# echo ${string}
china IS niuBIlity2018
[root@kevin ~]# echo ${string/ /--} #替換空格
china--IS niuBIlity2018
[root@kevin ~]# echo ${string// /--}
china--IS--niuBIlity2018
在開頭處替換與substring相匹配的子串,格式為:${string/#substring/replacement}。
在結尾除替換與substring相匹配的子串,格式為:${string/%substring/replacement}。
[root@kevin ~]# echo ${string}
china IS niuBIlity2018
[root@kevin ~]# echo ${string/#ch/he}
heina IS niuBIlity2018
[root@kevin ~]# echo ${string/#china/anhui}
anhui IS niuBIlity2018
[root@kevin ~]# echo ${string/#niu/he} #注意這里#后面的字符必須是${string}字符串中開頭的字符
china IS niuBIlity2018
[root@kevin ~]# echo ${string/%2018/2020}
china IS niuBIlity2020
[root@kevin ~]# echo ${string/%lity2018/hehehe}
china IS niuBIhehehe
7)${!varprefix*} 和 ${!varprefix@}
[root@kevin ~]# test="bobo"
[root@kevin ~]# test1="bobo1"
[root@kevin ~]# test2="bobo2"
[root@kevin ~]# test4="bobo4"
[root@kevin ~]# echo ${!test*}
test test1 test2 test4
[root@kevin ~]# echo ${!test@}
test test1 test2 test4
8)參數替換
從string開頭處刪除子串:
格式一:${string#substring} 刪除開頭處與substring匹配的最短子串。
格式二:${string##substring} 刪除開頭處與substring匹配的最長子串。其中substring並非是正則表達式而是通配符。
[root@kevin ~]# string="china IS niuBIlity2018"
[root@kevin ~]# echo ${string#c*i} #刪除c開頭到a的最短子串
na IS niuBIlity2018
[root@kevin ~]# echo ${string##c*i} #刪除c開頭到a的最長子串
ty2018
[root@kevin ~]# echo ${string#c*n}
a IS niuBIlity2018
[root@kevin ~]# echo ${string##c*n}
iuBIlity2018
[root@kevin ~]# echo ${string#ch*n} #刪除ch開頭到a的最短子串
a IS niuBIlity2018
[root@kevin ~]# echo ${string##ch*n} #刪除ch開頭到a的最長子串
iuBIlity2018
上面#或##后面的字符必須是${string}字符串的開頭子串!否則刪除子串就無效了!
[root@kevin ~]# echo ${string#i*n} #i不是開頭字符,所以刪除無效
china IS niuBIlity2018
[root@kevin ~]# echo ${string##i*n} #i不是開頭字符,所以刪除無效
china IS niuBIlity2018
另外:可以使用下面方式進行刪除:
${string#*substring} 刪除${string}字符串中第一個$substring及其之前的字符
${string##*substring} 刪除${string}字符串中最后一個$substring及其之前的字符
[root@kevin ~]# string="china IS niuBIlity2018"
[root@kevin ~]# echo ${string#*i} #刪除第一個i及其之前的字符
na IS niuBIlity2018
[root@kevin ~]# echo ${string##*i} #刪除最后一個i及其之前的字符
ty2018
也可以使用下面方法進行刪除
格式一:${string%substring*} 刪除${string}字符串中最后一個$substring及其之后的字符
格式二:${string%%substring*} 刪除${string}字符串中第一個$substring及其之后的字符
[root@kevin ~]# echo ${string}
china IS niuBIlity2018
[root@kevin ~]# echo ${string%i*}
china IS niuBIl
[root@kevin ~]# echo ${string%%i*}
ch
[root@kevin ~]# echo ${string%c*}
[root@kevin ~]# echo ${string%%c*}
[root@kevin ~]#
-------------------------------------------------------------------
再看下面一例:
[root@kevin ~]# str=bo/www/kevin/data/test/ccd.log
[root@kevin ~]# echo ${str#*/*/}
kevin/data/test/ccd.log
[root@kevin ~]# echo ${str##*/*/}
ccd.log
[root@kevin ~]# echo ${str%/*/*}
bo/www/kevin/data
[root@kevin ~]# echo ${str%%/*/*}
bo
以上是以/*/作為匹配的字符串,即正則匹配的字符串。
9)如何判斷一個字符串是否由字母數字開頭 (grep)
1)判斷一個字符串是否由大小寫字母或數字開頭(或結尾)
[root@kevin ~]# cat test.sh
#!/bin/bash
#str="This IS a root USER, 20171aaabb"
read -p "請輸入內容:" str
if echo "${str}" | grep -q '^[A-Za-z0-9].*\+$'; then
echo -e "${str}\nok"
else
echo "invaliad"
fi
需要注意:
腳本中的echo后面只需要添加-e參數,是為了讓打印中的\n換行符生效!如果不加-e參數,則\n就被做當普通字符打印出來了!
read -p 表示"指定要顯示的提示"。如果添加-s參數,即"read -sp",則表示"靜默輸入,即隱藏輸入的數據,一般用於密碼輸入"
執行腳本:
[root@kevin ~]# sh test.sh
請輸入內容:TOOk213gg
TOOk213gg
ok
[root@kevin ~]# sh test.sh
請輸入內容:@#sadf123
invaliad
[root@VM_16_9_centos ~]# sh test.sh
請輸入內容:arTR213#$1
arTR213#$1
ok
==============================================================
[root@kevin ~]# read -p "輸入你想要輸入的內容:"
輸入你想要輸入的內容:asdfsafsaf
[root@kevin ~]# read -sp "輸入你想要輸入的內容:" #加了-s參數后,即為靜默輸入,隱藏輸入的內容
輸入你想要輸入的內容:
[root@kevin ~]#
==============================================================
為了簡化,還可以將上面腳本中的:
grep -q '^[A-Za-z0-9].*\+$'
改為
grep -q '^[A-Za-z0-9].*'
--------------------------------------
如果判斷一個字符串是否由大小寫字母或數字結尾!!!!! 則只需要將上面腳本中的:
grep -q '^[A-Za-z0-9].*\+$'
改為
grep -q '.*[A-Za-z0-9]$'
==============================================================
還要注意:
'^[A-Za-z0-9].*\+$' 表示以大寫字母或小寫字母或數字為開頭。沒有順序要求!!
'^[A-Za-z0-9].*' 可以直接去掉后面的"\+$"部分
'^[A-Z].*' 表示以大寫字母開頭
'^[a-z].*' 表示以小寫字母開頭
'^[0-9].*' 表示以數字字母開頭
下面都是最常用的
grep ^[0-9]
grep "^[0-9]"
grep ^[a-z]
grep "^[a-z]"
grep ^[A-Z]
grep "^[A-Z]"
grep ^[a-Z]
grep "^[a-Z]"
grep .*[0-9]$
grep ".*[0-9]$"
grep ".*[a-z]$"
grep ".*[a-z]$"
grep ".*[A-Z]$"
grep ".*[A-Z]$"
grep .*[a-Z]$
grep ".*[a-Z]$"
grep [0-9]G
grep [a-z]2018_data
[root@kevin ~]# cat a.txt
Good study 2018! hahahah~
good Study 2018hehehehe
2018 Good study 1wqe
2018stuDY is heht6ttt
!@#asdf
TOOk213gg asdfasdf
anhui 2018asdfjlsadfdsaff
#$$$$$
[root@kevin ~]# cat a.txt|grep '^[A-Z].*\+$'
Good study 2018! hahahah~
TOOk213gg asdfasdf
[root@kevin ~]# cat a.txt|grep '^[a-z].*\+$'
good Study 2018hehehehe
anhui 2018asdfjlsadfdsaff
[root@kevin ~]# cat a.txt|grep '^[0-9].*\+$'
2018 Good study 1wqe
2018stuDY is heht6ttt
[root@kevin ~]# cat a.txt|grep '^[A-Za-z0-9].*\+$'
Good study 2018! hahahah~
good Study 2018hehehehe
2018 Good study 1wqe
2018stuDY is heht6ttt
TOOk213gg asdfasdf
anhui 2018asdfjlsadfdsaff
[root@kevin ~]# cat a.txt|grep -v '^[A-Za-z0-9].*\+$'
!@#asdf
#$$$$$
[root@kevin ~]# cat a.txt|grep -v '^[A-Z].*\+$'|grep -v '^[a-z].*\+$'|grep -v '^[0-9].*\+$'
!@#asdf
#$$$$$
[root@kevin ~]# cat a.txt |grep -v ^[a-Z]
2018 Good study 1wqe
2018stuDY is heht6ttt
!@#asdf
#$$$$$
[root@kevin ~]# cat a.txt |grep -v ^[a-Z]|grep -v [0-9]
!@#asdf
#$$$$$
grep獲取多個條件(grep -E "條件1|條件2|條件3")
[root@kevin ~]# cat a.txt |grep -E "^[a-z]|^[0-9]"
good Study 2018hehehehe
2018 Good study 1wqe
2018stuDY is heht6ttt
anhui 2018asdfjlsadfdsaff
grep過濾多個條件(grep -v "條件1\|條件2\|條件3"),注意""里面有轉義符"\"
[root@kevin ~]# cat a.txt |grep -v "^[a-z]\|^[0-9]"
Good study 2018! hahahah~
!@#asdf
TOOk213gg asdfasdf
#$$$$$
[root@kevin ~]# cat a.txt |grep -v "[a-z]\|^[0-9]"
#$$$$$
10)刪除字符串中指定字符(tr命令、sed命令)
一、使用sed將字符串中指定的字符刪除
[root@kevin ~]# echo "2018-10-08 15:19:05"|sed 's/-//g'|sed 's#:##g'
20181008 151905
刪除字符串中的特殊字符
[root@kevin ~]# cat test.sh
#!/bin/bash
str="root!@#QWE123"
echo ${str}| sed 's/\!//g' | sed 's/\@//g' | sed 's/\#//g'
[root@kevin ~]# sh test.sh
rootQWE123
還可以使用tr -d命令進行刪除:
[root@kevin ~]# cat test.sh
#!/bin/bash
str="root!@#QWE123"
echo ${str}| tr -d "!" | tr -d "@" | tr -d "#"
[root@kevin ~]# sh test.sh
rootQWE123
-------------------------------------------------------------------------------
另外:sed也支持正則
sed -n '/[0-9]/p' filename 將文件中匹配數字的行打印出來
sed -n '/[a-z]/p' filename 將文件中匹配小寫字母的行打印出來
sed -n '/[A-Z]/p' filename 將文件中匹配大寫字母的行打印出來
sed -i '/[0-9]/d' filename 將文件中匹配數字的行刪除
sed -n '/[a-z]/d' filename 將文件中匹配小寫字母的行刪除
sed -n '/[A-Z]/d' filename 將文件中匹配大寫字母的行刪除
sed -i '/[0-9]/s/root/huoqiu/g' filename 將文件中匹配數字的行里的root替換為huoqiu
sed -i '/[a-z]/s/root/huoqiu/g' filename 將文件中匹配小寫字母的行里的root替換為huoqiu
sed -i '/[A-Z]/s/root/huoqiu/g' filename 將文件中匹配大寫字母的行里的root替換為huoqiu
===============================================================================================
二、使用tr命令刪除字符串中指定字符。tr使用-d參數可以起到刪除字符的效果。支持正則表達式
[root@kevin ~]# echo "huoqiu123GOOD"|tr -d "a-z" #刪除字符串中的小寫字母
123GOOD
[root@kevin ~]# echo "huoqiu123GOOD"|tr -d "a-zA-Z" #刪除字符串中的大寫字母
123
[root@kevin ~]# echo "huoqiu123GOOD"|tr -d "0-9" #刪除字符串中的數字
huoqiuGOOD
刪除字符串中指定的部分字符
[root@kevin ~]# echo "huoqiu123GOOD"|tr -d "k"|tr -d "G"
evin123OOD
[root@kevin ~]# echo "huoqiu123GOOD"|tr -d "123"
huoqiuGOOD
截取字符串中的特殊字符
[root@kevin ~]# cat test.sh
#!/bin/bash
str="root!@#QWE123"
echo ${str}| tr -d "a-z"| tr -d "A-Z"| tr -d "0-9"
[root@kevin ~]# sh test.sh
!@#
3)這里簡單介紹下tr命令的日常用法
tr命令可以對來自標准輸入的字符進行替換、壓縮和刪除。它可以將一組字符變成另一組字符,經常用來編寫優美的單行命令,作用很強大。
語法
# tr (選項) (參數)
選項
-c或——complerment: 用字符集1中的字符串替換,要求字符集為ASCII。
-d或——delete: 刪除所有屬於字符集1的字符;
-s或--squeeze-repeats: 把連續重復的字符以單獨一個字符表示。即壓縮字符,但是必須是連續重復的單個字符!
-t或--truncate-set1: 先刪除字符集1較字符集2多出的字符。
參數
字符集1:指定要轉換或刪除的原字符集。當執行轉換操作時,必須使用參數“字符集2”指定轉換的目標字符集。但執行刪除操作時,不需要參數“字符集2”;
字符集2:指定要轉換成的目標字符集。
tr用來從標准輸入中通過替換或刪除操作進行字符轉換。
tr主要用於刪除文件中控制字符或進行字符轉換。
使用tr時要轉換兩個字符串:字符串1用於查詢,字符串2用於處理各種轉換。
tr剛執行時,字符串1中的字符被映射到字符串2中的字符,然后轉換操作開始。
通過使用tr可以非常容易地實現sed的許多最基本功能,可以將tr看作為sed的(極其)簡化的變體。
它可以用一個字符來替換另一個字符,或者可以完全除去一些字符。您也可以用它來除去重復字符。這就是所有 tr 所能夠做的。
看看下面示例:
1)tr的替換命令
將輸入字符由小寫轉換為大寫:
[root@kevin ~]# echo "anhui@root"|tr "a-z" "A-Z"
ANHUI@ROOT
將輸入字符由大寫轉換為小寫:
[root@kevin ~]# echo "ANhui@ROOT"|tr "A-Z" "a-z"
anhui@root
[root@kevin ~]# echo "172.16.60.34 data-node01"|tr "." "_"
172_16_60_34 data-node01
[root@kevin ~]# echo "root \n 213"| tr " " "#"
root#\n#213
[root@kevin ~]# echo "123456@abc"|tr "0-9" "#"
######@abc
[root@kevin ~]# echo "123456@abc"|tr "123" "pas" # 替換兩個字符集的時候,分別是一個字符對應一個字符的關系
pas456@abc
[root@kevin ~]# echo "123456@abc"|tr "123" "password" # 當字符集1的字符數少於字符集2的字符數時,就取字符集2的前面對應數目的字符進行替換
pas456@abc
[root@kevin ~]# echo "123456@abc"|tr "1234" "password"
pass56@abc
[root@kevin ~]# echo "123456@abc"|tr "12345" "password"
passw6@abc
[root@kevin ~]# echo "123456@abc"|tr "123456" "password"
passwo@abc
[root@kevin ~]# echo "123456@abc"|tr "123456" "T" #當字符集1的字符數多於字符集2的字符數時,先進行兩個字符集對應數目字符的替換,剩下多余的字符集1字符就使用字符集2的最后一個字符進行重復替換
TTTTTT@abc
[root@kevin ~]# echo "123456@abc"|tr "123456" "TM"
TMMMMM@abc
[root@kevin ~]# echo "123456@abc"|tr "123456" "TMC"
TMCCCC@abc
[root@kevin ~]# echo "123456@abc"|tr "123456" "TMCH"
TMCHHH@abc
[root@kevin ~]# echo "123456@abc"|tr "123456" "TMCHR"
TMCHRR@abc
[root@kevin ~]# echo "123456@abc"|tr "123456" "TMCHRY"
TMCHRY@abc
需要注意:
'A-Z' 和 'a-z'都是集合,集合是可以自己制定的,例如:'ABD-}'、'bB.,'、'a-de-h'、'a-c0-9'都屬於集合,集合里可以使用'\n'、'\t',可以可以使用其他ASCII字符。
替換兩個字符集的時候,分別是一個字符對應一個字符的關系
當字符集1的字符數少於字符集2的字符數時,就取字符集2的前面對應數目的字符進行替換
當字符集1的字符數多於字符集2的字符數時,先進行兩個字符集對應數目字符的替換,剩下多余的字符集1字符就使用字符集2的最后一個字符進行重復替換
另外:"O*n" 表示字符O重復出現指定次數n。因此"O*2"匹配OO的字符串!!!!
[root@kevin ~]# echo "aaa123"|tr "a*3" "w" #將a和3都替換為了w
www12w
[root@kevin ~]# echo "aaa123"|tr "a*3" "xy" #將a替換為了x,3替換為了y
xxx12y
[root@kevin ~]# echo "aaa123"|tr "a3" "xy"
xxx12y
[root@kevin ~]# echo "aaaaaaaa123"|tr "a*3" "xyz"
xxxxxxxx12z
[root@kevin ~]# echo "aaa123"|tr "aaa" "w" #匹配字符集1中最后一個字符對應字符集2中的那個字符
www123
[root@kevin ~]# echo "aaa123"|tr "aaa" "wx"
xxx123
[root@kevin ~]# echo "aaa123"|tr "aaa" "wxy"
yyy123
[root@kevin ~]# echo "aaa123"|tr "aaa" "wxym"
yyy123
[root@kevin ~]# echo "aaa123"|tr "aaa" "wxymn"
yyy123
2)tr的刪除命令 (上面案例已經說明)
[root@kevin ~]# echo "hello 123 world 456" | tr -d '0-9'
hello world
[root@kevin ~]# cat test
beijing
shanghai
abcde
[root@kevin ~]# cat test|tr -d "abcji" #即凡是test文件中出現的a,b,c,j,i都會被刪除
eng
shngh
de
3)用tr壓縮字符,可以壓縮輸入中重復的字符。但是注意:必須是連續重復的單個字符!!
[root@kevin ~]# echo "thissss is a text linnnnnnne." | tr -s ' sn'
this is a text line.
[root@kevin ~]# echo "123123123"|tr -s "123"
123123123
[root@kevin ~]# echo "123333344444"|tr -s "3"|tr -s "4"
1234
4)巧妙使用tr命令進行數字相加操作:
[root@kevin ~]# echo 1 2 3 4 5 6 7 8 9 | xargs -n1 | echo $[ $(tr '\n' '+') 0 ]
45
[root@kevin ~]# echo "10 11 12 13 14"|xargs -n1|echo $[ $(tr '\n' '+') 0 ]
60
--------------------------------------------------------------------------------------
<<<<< "xargs -n[數字]"用法 >>>>>
[root@kevin ~]# echo "aa bb cc 1 2 3"|xargs
aa bb cc 1 2 3
[root@kevin ~]# echo "aa bb cc 1 2 3"|xargs -n1
aa
bb
cc
1
2
3
[root@kevin ~]# echo "aa bb cc 1 2 3"|xargs -n2
aa bb
cc 1
2 3
[root@kevin ~]# echo "aa bb cc 1 2 3"|xargs -n3
aa bb cc
1 2 3
[root@kevin ~]# echo "aa bb cc 1 2 3"|xargs -n4
aa bb cc 1
2 3
--------------------------------------------------------------------------------------
5)把文件中的數字0-9替換為a-j (都是10個字符,一一對應)
[root@kevin ~]# cat filename |tr [0-9] [a-j]
6)刪除文件file中出現的換行'\n'、制表'\t'字符
[root@kevin ~]# cat filename | tr -d "\n\t"
刪除換行符等
[root@kevin ~]# echo -e "asdf\n123"|tr -d "\n"
asdf123
[root@kevin ~]# echo -e "asdf\n\r\t123"|tr -d "\n\r\t"
asdf123
7)刪除空行
[root@kevin ~]# cat file | tr -s "\n" > new_file
[root@kevin ~]# cat test
beijing
shanghai
abcde
123123
[root@kevin ~]# cat test| tr -s "\n"
beijing
shanghai
abcde
123123
8)把路徑變量中的冒號":",替換成換行符"\n"
[root@kevin ~]# echo "a:b:c:d"|tr ":" "\n"
a
b
c
d
[root@kevin ~]# echo "/www/data/haha/html"|tr "/" ":"
:www:data:haha:html
9)字符集補集!!! (tr -d -c)
set1的補集意味着從這個集合中包含set1中沒有的所有字符。
最典型的用法就是從輸入文本中將不在補集中的所有字符全部刪除!!!!
[root@kevin ~]# echo "hello 123 world " | tr -d -c '0-9 \n'
123
[root@kevin ~]# echo "wang beijinganhui"|tr -d -c 'a-m\n'
agbeijigahi
[root@kevin ~]# echo "123WRsdf"|tr -d -c "a-z\n"
sdf
[root@kevin ~]# echo "123@#QWEanhui"|tr -d -c "anhui\n"
anhui
10) 刪除Windows文件"造成"的'^M'字符
[root@kevin ~]# cat file | tr -s "\r" "\n" > new_file
或
[root@kevin ~]# cat file | tr -d "\r" > new_file
11) 使用tr命令生成固定長度的隨機密碼!!!!
[root@kevin ~]# head /dev/urandom | tr -dc A-Za-z0-9 | head -c 20
koR3ZjLekd6Xujfeslu1
[root@kevin ~]# head /dev/urandom | tr -dc A-Za-z0-9 | head -c 20
vgOKX39zeQSWP6KD6rjd
[root@kevin ~]#
11)shell的 read 輸入用法
read命令用於接收鍵盤或其它文件描述符的輸入。
read命令接收標准輸入(鍵盤)的輸入,或其他文件描述符的輸入(后面在說)。得到輸入后,read命令將數據放入一個標准變量中。
read 命令格式如下:
#read [選項] [變量名]
選項:
-p: 指定要顯示的提示
-s: 靜默輸入,輸入的數據不顯示出來。實際上輸入數據是顯示的,只是read命令將文本顏色設置成與背景相同的顏色!!!一般用於密碼輸入。
-n: 指定輸入的字符長度最大值。如果超出了,就默認使用前面最大長度值的字符!
-d '字符': 輸入結束符,當你輸入的內容出現這個字符時,立即結束輸入
-t N: 超出N秒沒有進行輸入,則自動退出。
需要注意:
變量名可以自定義。如果不指定變量名,則會把輸入保存到默認變量REPLY中;
如果只提供了一個變量名,則將整個輸入行賦予該變量;
如果提供了一個以上的變量名,則輸入行分為若干字,一個接一個地賦予各個變量,而命令行上的最后一個變量取得剩余的所有字;
[root@kevin ~]# read -p "請輸入你的內容: " haha;echo "${haha}"
請輸入你的內容: 123456
123456
加了-s參數,即靜默輸入。輸入數據的時候看不到。默認不換行!
[root@kevin ~]# read -sp "請輸入你的內容: " haha;echo "${haha}"
請輸入你的內容: 123456
[root@kevin ~]# read -sp "請輸入你的內容: " haha;echo -e "\n${haha}"
請輸入你的內容:
123456
下面來看看下面的一些示例:
1)基本讀取
[root@kevin ~]# cat test.sh
#!/bin/bash
echo -n "Enter your name:" #參數-n的作用是不換行,echo默認是換行!
read name #從鍵盤輸入
echo "hello $name,welcome to my program" #顯示信息
exit 0 #退出shell程序。
[root@kevin ~]# sh test.sh
Enter your name:beijing
hello beijing,welcome to my program
2)使用read輸入 (read -p)
由於read命令提供了-p參數,允許在read命令行中直接指定一個提示。
上面腳本可以改進為:
[注意:read命令后必須加它自己的參數,如果不加參數,則不執行!]
[root@kevin ~]# cat test.sh
#!/bin/bash
read -p "Enter your name:" name
echo "hello $name, welcome to my program"
exit 0
[root@kevin ~]# sh test.sh
Enter your name:beijing
hello beijing, welcome to my program
需要注意:
在上面read后面的變量只有name一個,也可以有多個,多個變量使用空格隔開,這時如果輸入多個數據,則第一個數據給第一個變量,第二個數據給第二個變量;
如果輸入數據個數過多,則最后所有的值都給最后那個變量!!
如果輸入數據太少,則對應不到數據的變量為空!
在read命令行中也可以不指定變量,如果不指定變量,那么read命令會將接收到的數據放置在環境變量REPLY中。
3)read后面可以跟多個變量
[root@kevin ~]# cat test.sh
#!/bin/bash
read -p "Enter your name:" name age city
echo "hello ${name},${age},${city}, welcome to my program"
exit 0
[root@kevin ~]# sh test.sh
Enter your name:bobo 26 beijing
hello bobo,26,beijing, welcome to my program
如果輸入數據多余變量,則多余的數據都給最后那個變量!!
[root@kevin ~]# sh test.sh
Enter your name:yang 26 shanghai huoqiu haha
hello yang,26,shanghai huoqiu haha, welcome to my program
如果輸入數據少於變量,則對應不到數據的變量為空!
[root@kevin ~]# sh test.sh
Enter your name:yang 26
hello yang,26,, welcome to my program
[root@kevin ~]# sh test.sh
Enter your name:yui
hello yui,,, welcome to my program
4)不跟變量,默認使用${REPLY}
如果read后面不指定變量,則read命令會將接收到的數據放置在環境變量REPLY中!!!!
環境變量REPLY中包含輸入的所有數據,可以像使用其他變量一樣在shell腳本中使用環境變量REPLY,即${REPLY}
[root@kevin ~]# cat test.sh
#!/bin/bash
read -p "Enter your name:"
echo "${REPLY}是輸入的內容"
exit 0
[root@kevin ~]# sh test.sh
Enter your name:zhangyang is 27,very nice!!
zhangyang is 27,very nice!!是輸入的內容
5)read的計時輸入 (read -t)
使用read命令存在着潛在危險,腳本很可能會停下來一直等待用戶的輸入。
如果無論是否輸入數據腳本都必須繼續執行,那么可以使用-t選項指定一個計時器。
-t選項指定read命令等待輸入的秒數。當計時滿時,read命令返回一個非零退出狀態;
[root@kevin ~]# cat test.sh
#!/bin/bash
if read -t 5 -p "please enter your name:" name
then
echo "hello $name ,welcome to my script"
else
echo "sorry,too slow"
fi
exit 0
[root@kevin ~]# sh test.sh
please enter your name:zhangtianba
hello zhangtianba ,welcome to my script
[root@kevin ~]# sh test.sh
please enter your name:sorry,too slow
即超出5s沒有輸入內容,腳本就自動結束!
6)read的計算字符長度輸入 (read -n)
除了輸入時間計時,還可以設置read命令計數輸入的字符。當輸入的字符數目達到預定數目時,自動退出,並將輸入的數據賦值給變量。
[root@kevin ~]# cat test.sh
#!/bin/bash
read -n1 -p "Do you want to continue [Y/N]?" answer
case $answer in
Y | y)
echo -e "\nfine ,continue";;
N | n)
echo -e "\nok,good bye";;
*)
echo -e "\nerror choice";;
esac
exit 0
[root@kevin ~]# sh test.sh
Do you want to continue [Y/N]?y
fine ,continue
[root@kevin ~]# sh test.sh
Do you want to continue [Y/N]?N
ok,good bye
[root@kevin ~]# sh test.sh
Do you want to continue [Y/N]?A
error choice
注意:
該例子使用了-n選項,后接數值1,指示read命令只要接受到一個字符就退出。
只要按下一個字符進行回答,read命令立即接受輸入並將其傳給變量。無需按回車鍵。
上面腳本中要在echo語句中添加換行符"\n",否則執行腳本,在輸入內容后會將echo的內容和read提示信息放在一行!
echo需要加上參數"-e"才能將后面引號內的特殊字符"\n"生效!否則就當普通字符處理了!
如果輸入的字符超過了設定的最大字符長度,則就默認使用前面最大長度值的字符!
[root@kevin ~]# cat test.sh
#!/bin/bash
read -n5 -p "please input name:" name
echo "需要輸入的名字是:${name}"
沒有超過設定的最大字符長度,這時候會自動換行!
[root@kevin ~]# sh test.sh
please input name:bao
需要輸入的名字是:bao
超過了設定的最大字符長度。
本來輸入的是zhangzihua,但是默認使用了前面5個字符:zhang
超過后,不會自動換行!
[root@kevin ~]# sh test.sh
please input name:zhang需要輸入的名字是:zhang
改進下,解決不換行問題:
[root@kevin ~]# cat test.sh
#!/bin/bash
read -n5 -p "please input name:" name
echo -e "\n需要輸入的名字是:${name}"
這樣,輸入字符超過設定的最大字符長度時,就會自動換行了!
[root@kevin ~]# sh test.sh
please input name:zhang
需要輸入的名字是:zhang
再來改進下腳本:
[root@kevin ~]# cat test.sh
#!/bin/bash
read -n5 -p "please input name:" name
len=$(expr length "${name}")
#或者使用len=`echo ${#name}`
if [ ${len} -lt 5 ];then #注意第一個邏輯判斷中的len變量不能等於設置的最大長度值,否則下面就不會執行。
echo "輸入的名字為:${name}" #當不超過設定的字符長度時,會自動換行
else
echo -e "\n輸入的名字長度超過5,當前名字為${name}"
exit 0
fi
[root@kevin ~]# sh test.sh
please input name:bao
輸入的名字為:bao
[root@kevin ~]# sh test.sh
please input name:hangu
輸入的名字長度超過5,當前名字為hangu
7)read的靜默輸入。(read -s)
有時會需要腳本用戶輸入,但不希望輸入的數據顯示在監視器上。典型的例子就是輸入密碼,當然還有很多其他需要隱藏的數據。
-s選項能夠使read命令中輸入的數據不顯示在監視器上(實際上,數據是顯示的,只是read命令將文本顏色設置成與背景相同的顏色!!!)。
[root@kevin ~]# cat test.sh
#!/bin/bash
read -s -p "Enter your password: " pass
echo -e "\nyour password is $pass"
exit 0
靜默輸入,即輸入的數據是看不到的。
[root@kevin ~]# sh test.sh
Enter your password:
your password is 123456
8)read讀取文件!!!!(cat filename | while read line)
可以使用read命令讀取Linux系統上的文件。
每次調用read命令都會讀取文件中的"一行"文本!!
當文件沒有可讀的行時,read命令將以非零狀態退出。
讀取文件的關鍵是如何將文本中的數據傳送給read命令??
最常用的方法:對文件使用cat命令並通過管道將結果直接傳送給包含read命令的while命令
[root@kevin ~]# cat test.txt
wangbo is a boy!
beijing is good!
abc 123 sahdfksfah
asf#$$!QA
[root@kevin ~]# cat test.sh
#!/bin/bash
count=1
cat test.txt | while read line #cat命令的輸出作為read命令的輸入,read讀到的值放在line中。line為讀取文件行內容的變量
do
echo ${line}|grep -w "beijing" >/dev/null 2>&1
if [ $? -eq 0 ];then
echo "想要的是:${line}"
else
echo "Line ${count}:${line}" #讀取的內容默認為變量${line}
count=$[ ${count} + 1 ] #注意中括號中的空格。
fi
done
echo "finish"
exit 0
[root@kevin ~]# sh test.sh
Line 1:wangbo is a boy!
想要的是:beijing is good!
Line 2:abc 123 sahdfksfah
Line 3:asf#$$!QA
finish
12)shell的 case 用法
case語句還是很好理解的,在shell編程中,if語句有它的語法,函數也有它的語法,那么在shell編程中的case語句也是有它的語法的,語法格式如下:
case ${變量名} in
賦值1)
執行指令1
;; # 每一個選擇都以雙;;結束。(需要注意:;;相當於break語句)
賦值2)
執行指令2
;;
賦值3)
執行指令3
;;
*) # *未匹配到相符的其他值
執行其他指令
;;
esac
示例一
當命令行參數是 1 時,輸出 "周一", 是 2 時,就輸出"周二", 其它情況輸出 "其他"
---------------------------------------------------------------------
[root@localhost ~]# cat test.sh
#!/bin/bash
case $1 in
1)
echo "周一"
;;
2)
echo "周二"
;;
*)
echo "其他"
;;
esac
[root@localhost ~]# sh test.sh 1
周一
[root@localhost ~]# sh test.sh 2
周二
[root@localhost ~]# sh test.sh 3
其他
[root@localhost ~]# sh test.sh 4
其他
[root@localhost ~]# sh test.sh
其他
示例二
shell腳本中case選擇語句可以結合read指令實現比較好的交互應答操作,case接收到read指令傳入的一個或多個參數,然后case根據參數做選擇操作。
---------------------------------------------------------------------
1) 案例1
[root@localhost ~]# cat test.sh
#!/bin/bash
echo "Please enter A,B,C"
read letter
#上面兩句可以改進為:
#read -p "Please enter A,B,C: " letter
case $letter in
A|a)
echo "you entered A"
;;
B|b)
echo "you entered B"
;;
C|c)
echo "you entered C"
;;
*)
echo "Not in A,B,C"
;;
esac
[root@localhost ~]# sh test.sh
Please enter A,B,C
A
you entered A
[root@localhost ~]# sh test.sh
Please enter A,B,C
b
you entered B
[root@localhost ~]# sh test.sh
Please enter A,B,C
C
you entered C
[root@localhost ~]# sh test.sh
Please enter A,B,C
w3
Not in A,B,C
[root@localhost ~]# sh test.sh
2) 案例二:查看系統資源使用情況
[root@localhost ~]# cat test.sh
#!/bin/bash
echo "check system run status"
echo "show CPUinfo: C/c "
echo "show Memery used: M/m "
echo "show Disk use status: D/n "
echo "show System user login: U/n "
echo "show System load average:L/l"
echo "show System Ip address: I/i"
read_input () {
read -t 10 -p "please Input C/M/D/U/L/I : " char
}
show_status () {
case $char in
C | c )
cat /proc/cpuinfo | grep -o -i 'model name.*'
;;
M | m )
free -m
;;
D | d )
df -h
;;
U | u )
w
;;
L | l )
top | head -1 | cut -d " " -f 11-15
;;
I | i )
ifconfig | grep -o "[0-9.]\{7,\}" | head -1
;;
* )
echo "The characters you have entered are wrong. Please look at the hints"
;;
esac
}
for i in $( seq 1 10) #呼應前面"read -t 10"中的10秒鍾要輸入內容的限制
do
read_input
show_status
if [ $i -eq 10 ]; then
echo "已經到達查詢的最大次數,腳本退出;"
fi
done
[root@localhost ~]# sh test.sh
check system run status
show CPUinfo: C/c
show Memery used: M/m
show Disk use status: D/n
show System user login: U/n
show System load average:L/l
show System Ip address: I/i
please Input C/M/D/U/L/I : c
model name : Intel(R) Xeon(R) CPU E5-2640 v4 @ 2.40GHz
model name : Intel(R) Xeon(R) CPU E5-2640 v4 @ 2.40GHz
model name : Intel(R) Xeon(R) CPU E5-2640 v4 @ 2.40GHz
model name : Intel(R) Xeon(R) CPU E5-2640 v4 @ 2.40GHz
please Input C/M/D/U/L/I : m
total used free shared buff/cache available
Mem: 7815 250 5826 56 1738 5102
Swap: 2047 0 2047
please Input C/M/D/U/L/I : d
Filesystem Size Used Avail Use% Mounted on
devtmpfs 3.9G 0 3.9G 0% /dev
tmpfs 3.9G 0 3.9G 0% /dev/shm
tmpfs 3.9G 57M 3.8G 2% /run
tmpfs 3.9G 0 3.9G 0% /sys/fs/cgroup
/dev/mapper/centos-root 20G 3.2G 17G 16% /
/dev/mapper/centos-data 78G 33M 78G 1% /data
/dev/xvda1 197M 166M 32M 85% /boot
tmpfs 782M 0 782M 0% /run/user/0
please Input C/M/D/U/L/I : u
11:07:27 up 31 days, 17:24, 2 users, load average: 0.00, 0.01, 0.05
USER TTY FROM LOGIN@ IDLE JCPU PCPU WHAT
root tty1 30Oct19 31days 0.03s 0.03s -bash
root pts/0 172.16.198.22 09:57 7.00s 0.18s 0.00s w
please Input C/M/D/U/L/I : l
load average: 0.00, 0.01,
please Input C/M/D/U/L/I : i
172.16.60.236
please Input C/M/D/U/L/I : a
The characters you have entered are wrong. Please look at the hints
please Input C/M/D/U/L/I : The characters you have entered are wrong. Please look at the hints
please Input C/M/D/U/L/I : The characters you have entered are wrong. Please look at the hints
please Input C/M/D/U/L/I : The characters you have entered are wrong. Please look at the hints
已經到達查詢的最大次數,腳本退出;
案例3:
有時經常會使用比較長的if,then,else來嘗試計算一個變量的值,在一組可能的值中尋找特定值。其實這種情況如果使用case遇見就會變得簡單的多了!
使用多個if判斷來一一核對,代碼量比較多,還容易亂。用case的話,能減少代碼量,不需要再寫出所有的elif語句來不停地檢查同一個變量的值了。case命
令會采用列表格式來檢查單個變量的多個值。如下兩種方法效果一樣:
if判斷語句寫法:
[root@localhost ~]# cat test.sh
#!/bin/bash
USER=$1
if [ $USER = "kevin" ];then
echo "Welcome $USER,Please enjoy your visit"
elif [ $USER = "grace" ];then
echo "Welcome $USER,Please enjoy your visit"
elif [ $USER = "xiaoru" ];then
echo "Special testing account"
elif [ $USER = "shibo" ];then
echo "Do not forget to logout when you're done"
else
echo "Sorry, you are not allowed here"
fi
case語句寫法:
[root@localhost ~]# cat test.sh
#!/bin/bash
USER=$1
case ${USER} in
kevin|grace)
echo "Welcome $USER,Please enjoy your visit";;
xiaoru)
echo "Special testing account";;
shibo)
echo "Do not forget to logout when you're done";;
*)
echo "Sorry, you are not allowed here";;
esac
示例三:
case語句經常會使用在應用服務的一鍵部署腳本中。
比如:https://www.cnblogs.com/kevingrace/p/6086426.html
---------------------------------------------------------------------
示例四:
結合shell的"function函數+if邏輯判斷+case選擇語句
---------------------------------------------------------------------
[root@localhost ~]# cat test.sh
#!/bin/bash
function OPS(){ #定義一個OPS的函數
cat << kevin_test
1.北京
2.上海
3.深圳
kevin_test
}
OPS #調用CDAN函數
read -p "請輸入您想要去的地方: " WHERE #輸入一條提示,然后把用戶輸入的字符串賦值給變量WHERE
expr ${WHERE} + 1 >/dev/null 2>&1 #使用數值運算命令expr來確定用戶輸入的是否是數值
if [ "$?" -ne 0 ];then #如果用戶輸入的不是數值
echo "請您輸入{1|2|3}"
exit 1
fi
case ${WHERE} in
1)
echo "明天去北京!"
;;
2)
echo "明天去上海!"
;;
3)
echo "明天去深圳!"
;;
esac
[root@localhost ~]# sh test.sh
1.北京
2.上海
3.深圳
請輸入您想要去的地方: 1
明天去北京!
示例五:
再來分享一例shell的case循環用法
---------------------------------------------------------------------
要求:
輸入a|A顯示出紅色的本機IP
輸入b|B顯示出綠色的本機磁盤的剩余內存
輸入c|C顯示出黃色的系統運行時間
輸入 q|Q顯示出藍色的直接退出
[root@localhost ~]# cat test.sh
#!/bin/bash
while true
do
echo -e "
\033[31m A 顯示主機ip \033[0m
\033[32m B 顯示磁盤剩余空間 \033[0m
\033[33m C 顯示系統運行時間 \033[0m
\033[34m Q 退出系統 \033[0m
"
read -p "請輸入你的選擇:" char
case ${char} in
a|A)
echo -e "\033[31m `ifconfig eth0 | grep "netmask" | awk '{print $2}'` \033[0m"
;;
b|B)
echo -e "\033[32m `df -h | awk 'NR==2{print "剩余空間大小為:"$4}'` \033[0m"
;;
c|C)
echo -e "\033[33m `uptime | awk '{print "系統已經運行了"$3""$4""}'` \033[0m"
;;
q|Q)
exit 0
;;
*)
echo "請輸入A/B/C/Q"
;;
esac
done
[root@localhost ~]# sh test.sh
A 顯示主機ip
B 顯示磁盤剩余空間
C 顯示系統運行時間
Q 退出系統
請輸入你的選擇:a
172.16.60.238
A 顯示主機ip
B 顯示磁盤剩余空間
C 顯示系統運行時間
Q 退出系統
請輸入你的選擇:b
剩余空間大小為:3.9G
A 顯示主機ip
B 顯示磁盤剩余空間
C 顯示系統運行時間
Q 退出系統
請輸入你的選擇:c
系統已經運行了32days,
A 顯示主機ip
B 顯示磁盤剩余空間
C 顯示系統運行時間
Q 退出系統
請輸入你的選擇:d
請輸入A/B/C/Q
A 顯示主機ip
B 顯示磁盤剩余空間
C 顯示系統運行時間
Q 退出系統
請輸入你的選擇:q
[root@localhost ~]#
12)Shell的 for、case、while 循環流程控制語句用法
shell作為一種腳本編程語言,同樣包含循環、分支等其他程序控制結構,從而輕松完成更加復雜、強大的功能。
編寫腳本的思路
1. 明確腳本的功能
2. 編寫腳本時會使用到那些命令
3. 把變化的數據使用變量表示
4. 選擇適合的流程控制 (選擇 、 循環 、分支)
一、使用for循環語句
========================================================================================================
在工作中,經常遇到某項任務需要多次執行,而每次執行僅僅是處理對象不一樣,其他命令都相同。使用簡單的if語句已經難以滿足要求,
編寫全部代碼將困難重重,而for循環語句將很好的解決類似的問題。
for語句的結構
使用for循環語句時,需要指定一個變量及可能的取值列表,針對每一個不同的取值重復執行相同的命令,直到變量值用完退出循環。
for的語法結構:
for;do;done
語法格式:
for 變量名 in 列表內容
do
commands
done
或者
for 變量名 in 列表內容 ;do
commands
done
示例1):使用嵌套循環輸出99乘法表
[root@localhost ~]# cat test.sh
#!/bin/bash
for i in `seq 9`
do
for j in `seq 9`
do
[ $j -le $i ] && echo -n "$j x $i = `echo $(($j*$i))` " #如果j 小與等於i才會打印式子。注意后面雙引號后面要有一個空格,表示下面執行時各列間的空格。
done
echo ""
done
注意:外層循環循環行,內層循環循環列。$(())返回的是里面運算結果;echo -n表示不換行
規律: 內層循環的變量<=外層循環的變量
[root@localhost ~]# sh test.sh
1 x 1 = 1
2 x 1 = 2 2 x 2 = 4
3 x 1 = 3 3 x 2 = 6 3 x 3 = 9
4 x 1 = 4 4 x 2 = 8 4 x 3 = 12 4 x 4 = 16
5 x 1 = 5 5 x 2 = 10 5 x 3 = 15 5 x 4 = 20 5 x 5 = 25
6 x 1 = 6 6 x 2 = 12 6 x 3 = 18 6 x 4 = 24 6 x 5 = 30 6 x 6 = 36
7 x 1 = 7 7 x 2 = 14 7 x 3 = 21 7 x 4 = 28 7 x 5 = 35 7 x 6 = 42 7 x 7 = 49
8 x 1 = 8 8 x 2 = 16 8 x 3 = 24 8 x 4 = 32 8 x 5 = 40 8 x 6 = 48 8 x 7 = 56 8 x 8 = 64
9 x 1 = 9 9 x 2 = 18 9 x 3 = 27 9 x 4 = 36 9 x 5 = 45 9 x 6 = 54 9 x 7 = 63 9 x 8 = 72 9 x 9 = 81
示例2):根據IP地址檢查主機狀態
[root@localhost ~]# vim test.sh
#!/bin/bash
for NUM in $(seq 1 254) #或者直接"seq 254" 或者 "seq 10 30"用於一段ip
do
IP=172.16.60.${NUM}
# -c表示ping的次數,-i表示時間間隔(秒),
ping -c 3 -i 0.2 $IP &> /dev/null
if [ $? -eq 0 ];then
echo "$IP is up"
else
echo "$IP id down"
fi
done
示例3):文件列表循環
[root@localhost ~]# vim test.sh
#!/bin/bash
cd /etc/
for a in `ls /etc/`
do
if [ -d $a ]
then
ls -d $a
fi
done
二、使用while循環語句
========================================================================================================
for語句適用於列表對象無規律,且列表來源以固定的場合。而對於要求控制循環次數、操作對象按數字順序編號、按特定的條件重復操作等情況,則更適合於while循環語句。
while循環:重復測試某個條件,只要條件成立,就重復執行命令,條件不成立,立即退出,自帶判斷;
while語句的結構
使用while循環語句時,可以根據特定的條件反復執行一個命令序列,直到該條件不在滿足為止。
需要注意:要避免出現while ture 的死循環!!!
語法格式如下:
while 測試命令
do
命令
done
退出while循環體的三種方式:
1. 條件為假退出循環體,繼續執行循環體以外的命令;
2. exit退出腳本,循環體外的命令不會執行;
3. break退出腳本中的循環體,繼續執行循環體外的命令;
特殊條件表達式:
1. true :當條件表達式為true時,那么代表條件表達式永遠成立,為真;
2. false:當條件表達式為false時,那么條件表達式永遠為假;
示例1):降序輸出10到1
[root@localhost ~]# cat test.sh
#!/bin/bash
num=10
while [ ${num} -gt 0 ]
do
echo "${num}"
num=$[${num}-1]
#或者使用下面的表達式也可以,$(())或$[]都表示返回運算結果
num=$((${num}-1))
done
[root@localhost ~]# sh test.sh
10
9
8
7
6
5
4
3
2
1
示例2):批量添加用戶
用戶名稱以kevin_開頭,按照數字順序進行編號
添加10個用戶,即kevin_1、kevin_2、...、kevin_10
初始密碼均設為123456
[root@VM_16_9_centos ~]# cat test.sh
#!/bin/bash
UR="kevin_"
NUM=1
while [ ${NUM} -le 10 ]
do
USER=${UR}${NUM}
useradd ${USER}
echo "123456"|passwd --stdin ${USER}
NUM=$((${NUM}+1))
#或者使用let NUM++,效果等同於NUM=$((${NUM}+1))或者NUM=$[${NUM}+1]
done
示例3)判斷輸入的數要是數字
[root@ss-server ~]# cat test.sh
#!/bin/bash
while :
do
read -p "Please input a number: " n
if [ -z "$n" ];then #空串為真
echo "you need input sth."
continue
fi
n1=`echo $n|sed 's/[0-9]//g'`
if [ -n "$n1" ];then #非空串為真
echo "you just only input numbers."
continue
fi
break
done
echo $n
[root@ss-server ~]# sh test.sh
Please input a number: 2
2
[root@ss-server ~]# sh test.sh
Please input a number: 2a
you just only input numbers.
Please input a number:
you need input sth.
Please input a number: 5
5
三、使用case分支語句
========================================================================================================
case語句主要適用於以下情況:
某個變量存在多種取值,需要對其中的每一種取值分別執行不同的命令序列。與多分支if語句相識,只是if語句需要判斷多個不同的條件,而case只是判斷一個變量的不同取值
1) case語句的結構如下:
case 變量或表達式 in
變量或表達式1)
命令序列1
;;
變量或表達式2)
命令序列2
;;
......
*)
默認命令序列
;;
esac
2) case執行流程
1. 首先使用"變量或表達式"的值與值1進行比較,若取值相同則執行值1后的命令序列,直到遇見雙分號";;"后跳轉至esac,表示分支結束;
2. 若與值1不相匹配,則繼續與值2進行比較,若取值相同則執行值2后的命令序列,直到遇見雙分號";;"后跳轉至esac,表示結束分支;
3. 依次類推,若找不到任何匹配的值,則執行默認模式"*)"后的命令序列,直到遇見esac后結束分支。
3) case執行流程注意事項
1. "變量或表達式"后面必須為單詞in,每一個"變量或表達式"的值必須以右括號結束。取值可以為變量或常數。匹配發現取值符合某一模式后,其間所有命令開始執行直至;;
2. 匹配中的值可以是多個值,通過"|"來分隔。
3. 匹配中的值可以是正則。
示例1):編寫一個備份,拷貝的交互式腳本
[root@localhost ~]# cat test.sh
#!/bin/bash
cat <<eof
*****************
**1. backup
**2. copy
**3. quit
*****************
eof
read -p "input your choose: " OP
case $OP in
1|backup)
echo "Backup..."
;;
2|copy)
echo "Copy..."
;;
3|quit)
exit
;;
*)
echo input error
esac
[root@localhost ~]# sh test.sh
*****************
**1. backup
**2. copy
**3. quit
*****************
input your choose: 1
Backup...
[root@localhost ~]# sh test.sh
*****************
**1. backup
**2. copy
**3. quit
*****************
input your choose: 2
Copy...
示例2):提示用戶輸入一個字符,判斷出該字符是字母、數字
[root@localhost ~]# cat test.sh
#!/bin/bash
#read后面跟的變量必須要空格隔開,否則變量無效!
read -p "請輸入一個字符: " star
case ${star} in
[a-z]|[A-Z])
echo "輸入的是一個字母"
;;
[0-9])
echo "輸入的是一個數字"
;;
*)
echo "請輸入字母或數字"
;;
esac
[root@localhost ~]# sh test.sh
請輸入一個字符: a
輸入的是一個字母
[root@localhost ~]# sh test.sh
請輸入一個字符: 3
輸入的是一個數字
13)Shell的 function 函數用法
簡單的說,Shell函數的作用就是將程序里面多次被調用的代碼組合起來,稱為函數體,並取一個名字稱為(函數名),當需要用到這段代碼的時候,就可以直接來調用函數名。
Shell函數是一個腳本代碼塊,可以對它進行自定義命名,並且可以在腳本中任意位置使用這個函數。
如果想要這個函數,只要調用這個函數的名稱就可以了。使用函數的好處在於模塊化以及代碼可讀性強。
一、Shell函數的創建語法
================================================================================================
在shell中 if語句有它的語法,for循環也有它的語法,那么shell中的函數,那肯定也有它的語法有以下三種:
函數的創建方法一:
function 函數名 () {
指令...
return -n
}
函數的創建方法二:
function 函數名 {
指令...
return -n
}
函數的創建方法三:
函數名 () {
指令...
return -n
}
需要注意:
1. 在以上三種函數語法中,前面的funcation 表示聲明一個函數! 可以不寫 return -n 是指退出函數!
2. 上面最后兩種函數創建方法的聲明方式效果等價(即函數的創建方法二和函數的創建方法三的效果是一樣的)!!!!!!!
3. 如果函數名后面沒有跟(),則函數名和"{"之間必須有空格!shell對空格變態的敏感。如果函數名后面有(),則兩者之間可以有空格,也可沒有空格。
4. 不得聲明形式參數。
5. 必須在調用前聲明。
6. 無法重載。
7. 后來的聲明會覆蓋之前的聲明。即如果存在相同名稱的函數,以最后一個為准!
另外注意:函數名稱在當前腳本必須唯一。
二、Shell函數調用的方法
================================================================================================
調用方法1:直接指定函數名即可。但一定要注意在聲明之后才可以調用函數!!格式如下:
函數名稱
調用方法2:調用函數時可以傳遞參數,函數內部中使用$1、$2......來引用傳遞的參數。格式如下:
函數名稱 參數1 參數2 ......
需要注意:
1. 其實函數被調用時會被當作一個小腳本來看待,調用時也可以在函數名后跟參數。
2. Shell函數在調用時都不可以加()
$1 #調用第一個參數
$2 #調用第二個參數
...
$n #調用第n個參數
$# #返回參數個數n
$0 #當前腳本文件名
三、Shell函數的返回值
================================================================================================
Shell函數運行結束后會有一個退出狀態碼,可以用$?變量來顯示上一條命令/函數執行結束的退出狀態碼。
當然,shell也為我們提供了return,像其他語言函數中return 一樣,不過(整形)返回值必須在0~255之間。
四、Shell函數創建庫
================================================================================================
與c的頭文件類似,在Shell中,也可以定義"庫文件",然后再另一個文件中導入。庫文件沒有特殊聲明或者定義,也是腳本文件.sh。
使用庫函數的關鍵在於導入庫文件。用source來導入,source實際上會在當前shell上下文中執行命令,從而達到導入效果。
注意:使用"source"或點符號"."都可以導入庫文件!(如下示例7)
五、在"~/.bashrc"文件中定義Shell函數
================================================================================================
在使用函數的庫文件時,如果每次都需要自己去導入定義的庫文件會顯得很麻煩!那么,我們可不導入直接使用呢?答案是肯定可以的!!!
方法就是在Shell的配置文件的.bashrc中聲明該函數,因為每次啟動shell都會載入.bashrc文件,所以就實現了"自動導入庫文件"!!!(如下示例8)
六、Shell函數使用實例
================================================================================================
示例1:直接在調用函數時進行參數傳遞!可以直接傳遞具體的變量值!
[root@ss-server ~]# cat test.sh
#!/bin/bash
function kevin(){
echo "welcome to anhui!"
}
function shibo(){
echo "$1+$2"
}
grace(){
echo $(($1+$2+$3))
}
kevin
shibo hello world
grace 2 4 5
[root@ss-server ~]# sh test.sh
welcome to anhui!
hello+world
11
再看一例:調用函數時,也可以直接傳遞變量。在腳本執行的時候再賦予變量具體的值!
[root@ss-server ~]# cat test.sh
#!/bin/bash
function kevin () {
echo "老子想要的是:$1 $2"
}
kevin $1 $2
[root@ss-server ~]# sh test.sh 房子 車子
老子想要的是:房子 車子
示例2:如果存在相同名稱的函數,以最后一個為准!即后來的函數聲明會覆蓋之前的聲明!
[root@ss-server ~]# cat test.sh
#!/bin/bash
function kevin(){
echo "welcome to anhui!"
}
kevin(){
echo "hello world"
}
kevin
[root@ss-server ~]# sh test.sh
hello world
示例3:return返回值
使用return命令來退出函數並返回特定的退出碼($?)
[root@ss-server ~]# cat test.sh
#!/bin/bash
function kevin(){
echo "welcome to anhui!"
return 2
echo "why reruen"
}
kevin
[root@ss-server ~]# sh test.sh
welcome to anhui!
需要注意:
return一般是在函數的最后一行,因為一旦執行return命令,該函數后面的命令就不執行了。
return與exit的區別:return和exit都可以返回退出碼,但是不同的是,return是退出函數,而exit是退出整個腳本。
示例4:函數值賦給變量
如下方實例中顯示,此時的函數就相當於一個命令,需要使用$()或``調用。
[root@ss-server ~]# cat test.sh
#!/bin/bash
function kevin(){
read -p "請輸入內容: " str
echo ${str}
}
bo=$(kevin) #也可以是bo=`kevin`
echo "測試結果為${bo}"
[root@ss-server ~]# sh test.sh
請輸入內容: xinzhongguo
測試結果為xinzhongguo
示例5:外部參數傳入函數
前面已經提到過,調用函數可以在后面跟隨參數,函數內部可以使用$n的形式調用。
[root@ss-server ~]# cat test.sh
#!/bin/bash
function kevin_1(){
echo "this is $1"
}
function kevin_2 {
echo "this is $1+$2"
echo $#
}
kevin_3() {
echo "this is $1!!"
}
kevin_1 安徽
kevin_2 上海 北京
kevin_3 /root/pass.list
[root@ss-server ~]# sh test.sh
this is 安徽
this is 上海+北京
2
this is /root/pass.list!!
示例6:函數的參數
在一個Shell腳本當中:
函數外的參數,函數可以直接調用;
函數內的參數,只要運行過函數,外部也可以直接調用。
[root@ss-server ~]# cat test.sh
#!/bin/bash
str="hello world"
function kevin(){
bo="${str} is very nice!!"
}
kevin
echo "${bo}, 666~"
[root@ss-server ~]# sh test.sh
hello world is very nice!!, 666~
示例7:導入庫文件
說白了,就是在一個shell腳本中導入另一個腳本,導入腳本中的變量在新腳本中同樣有效可用!
[root@ss-server ~]# cat /root/haha.sh
#!/bin/bash
USER=$1
ADDRESS=$2
AGE=$3
function PER(){
if [ ${AGE} -gt 30 ];then
echo "來自${ADDRESS}的${USER}是一個大叔!"
fi
}
[root@ss-server ~]# cat test.sh
#!/bin/bash
#可以使用soirce或.導入庫文件
#source /root/haha.sh
. /root/haha.sh
function TES {
YOU=`PER`
echo "告訴你!${YOU}"
}
TES
[root@ss-server ~]# sh test.sh 李楠 霍城 38
告訴你!來自霍城的李楠是一個大叔!
示例8:在"~/.bashrc"文件中定義Shell函數
[root@ss-server ~]# cat ~/.bashrc
........
#定義PER函數
USER=$1
ADDRESS=$2
AGE=$3
function PER(){
if [ ${AGE} -gt 30 ];then
echo "來自${ADDRESS}的${USER}是一個大叔!"
fi
}
[root@ss-server ~]# cat test.sh
#!/bin/bash
source ~/.bashrc
function TES {
YOU=`PER`
echo "告訴你!${YOU}"
}
TES
[root@ss-server ~]# sh test.sh 李楠 霍城 38
告訴你!來自霍城的李楠是一個大叔!
--------------------------------
或者:
[root@ss-server ~]# cat ~/.bashrc
......
source /root/haha.sh
[root@ss-server ~]# cat /root/haha.sh
#!/bin/bash
USER=$1
ADDRESS=$2
AGE=$3
function PER(){
if [ ${AGE} -gt 30 ];then
echo "來自${ADDRESS}的${USER}是一個大叔!"
fi
}
[root@ss-server ~]# cat test.sh
#!/bin/bash
source ~/.bashrc
function TES {
YOU=`PER`
echo "告訴你!${YOU}"
}
TES
[root@ss-server ~]# sh test.sh 李楠 霍城 38
告訴你!來自霍城的李楠是一個大叔!
14)Shell獲取隨機數 的 random 用法
1)使用$RANDOM 需要系統支持,通過echo來檢測, 打印出一個隨機數字,證明當前環境支持$RANDOM,反之為空不支持: [root@bz3aomsmsap1002 ~]# echo $RANDOM 1525 [root@bz3aomsmsap1002 ~]# echo $RANDOM 16218 2)使用/dev/urandom + tr [root@bz3aomsmsap1002 ~]# tr -cd 0-9 </dev/urandom | head -c 8 #取8位隨機種子 67128612 [root@bz3aomsmsap1002 ~]# tr -cd 0-9 </dev/urandom | head -c 16 #取12位隨機種子 8398375834495017 隨機生成密碼(如下兩種方式都可以,生成16位長度的隨機密碼) [root@bz3aomsmsap1002 ~]# head /dev/urandom | tr -dc A-Za-z0-9 | head -c 16 gXeLJ5XLAaV26tKJ [root@bz3aomsmsap1002 ~]# tr -dc A-Za-z0-9 </dev/urandom | head -c 16 0PUw4BHWQw8oQ3ai
15)快速去除文件或字符串中的空格(使用sed或tr)
1. 刪除字符串中的空格(使用sed或tr) [root@ss-server ~]# echo "aa dd"|sed 's/ //g' aadd [root@ss-server ~]# echo "aa dd"|tr -d " " aadd [root@ss-server ~]# echo " aa b t "|tr -d " " aabt [root@ss-server ~]# echo " root, bin, hook"|tr -d " " root,bin,hook 2. 刪除文件中行首空格的命令 # sed 's/^[ \t]*//g' filename #不加-i參數,表示僅僅在當前終端顯示命令執行效果。 # sed -i 's/^[ \t]*//g' filename #加-i參數,表示命令執行效果直接為文件生效,即直接在文件中刪除行首空格。 命令解釋: 第一個/的左邊是s表示替換,即將空格替換為空。 第一個/的右邊是中括號表示"或",空格或tab中的任意一種。這是正則表達式的規范。 中括號右邊是*,表示一個或多個。 第二個和第三個\中間沒有東西,表示空 g表示替換原來buffer(緩沖區)中的,sed在處理字符串的時候並不對源文件進行直接處理,先創建一個buffer,但是加g表示對原buffer進行替換 整體的意思是:用空字符去替換一個或多個用空格或tab開頭的本體字符串 示例: [root@ss-server ~]# cat test aa bb 123 aa sadf 12313 yy 45 [root@ss-server ~]# cat test|sed 's/^[ \t]*//g' test aa bb 123 aa sadf 12313 yy 45 [root@ss-server ~]# cat test|sed -i 's/^[ \t]*//g' test [root@ss-server ~]# cat test aa bb 123 aa sadf 12313 yy 45 3. 刪除文件中行尾空格的命令 # sed 's/[ \t]*$//g' filename #僅僅在當前終端顯示執行效果。 # sed -i 's/[ \t]*$//g' filename #直接刪除文件中行尾空格。 命令解釋: 和上面稍微有些不同是前面刪除了^符,在后面加上了美元符。表示刪除行尾空格。 4. 刪除文件中所有的空格的命令 # sed s/[[:space:]]//g filename # sed 's/[[:space:]]//g' haha # sed -i 's/[[:space:]]//g' haha # cat filename|tr -d " " #使用tr刪除所有空格,只是在終端顯示里生效,文件里默認並不會生效 示例: [root@ss-server ~]# cat haha 12 3 4 ads ss 12 3123 asdf ok jk 1 [root@ss-server ~]# sed 's/[[:space:]]//g' haha 1234 adsss 123123asdf okjk1 [root@ss-server ~]# cat haha 12 3 4 ads ss 12 3123 asdf ok jk 1 [root@ss-server ~]# sed -i 's/[[:space:]]//g' haha [root@ss-server ~]# cat haha 1234 adsss 123123asdf okjk1 [root@ss-server ~]# cat haha 12 3 4 ads ss 12 3123 asdf ok jk 1 [root@ss-server ~]# cat haha|tr -d " " 1234 adsss 123123asdf okjk1 [root@ss-server ~]# cat haha 12 3 4 ads ss 12 3123 asdf ok jk 1 [root@ss-server ~]# cat haha|tr -d " " > haha.txt && \cp -f haha.txt haha && rm -f haha.txt [root@ss-server ~]# cat haha 1234 adsss 123123asdf okjk1
16)如何根據截取某個字段多少位進行去重?以及連續重復字符去重
一、tr命令加-s參數,表示把連續重復的字符以單獨一個字符表示。即壓縮字符,但是必須是連續重復的單個字符!注意是連續出現的"單個"字符!!
[root@ss-server ~]# echo "111222342133"|tr -s "1"
1222342133
[root@ss-server ~]# echo "111222342133"|tr -s "12"
12342133
[root@ss-server ~]# echo "111222342133"|tr -s "123"
1234213
[root@ss-server ~]# echo "aa966ha34jj9"|tr -s "a"
a966ha34jj9
[root@ss-server ~]# echo "aa966ha34jj9"|tr -s "a96"
a96ha34jj9
還可以使用正則
[root@ss-server ~]# echo "aaAAF7889HHjkk09"|tr -s "a-zA-Z0-9"
aAF789Hjk09
二、根據下面test文件的第一個字段、第二個字段截取前8位進行排序去重
[root@ss-server ~]# cat /root/test
25ds51dd225d86af,20180725115911,22570,20443,120.17138,30.119047
002a51dd225d86af,20180725120017,22570,184680195,120.176506,30.11527
002a51dd225d86af,20180725120058,22290,80489347,120.1810786,30.10003
002a51dd225d86af,20180725120149,22290,80489345,120.1810786,30.10003
002a51dd225d86af,20180725120209,22290,189880577,120.18859,30.093405
102a51dd225d86af,20180725120239,22290,155606668,120.1990471,30.0961
002a51dd225d86af,20180725120303,22290,155606666,120.1990468,30.0961
002a51dd225d86af,20180725120434,22290,193501442,120.220661,30.09723
002a79ded185cb04,20180725125428,22570,185263107,120.1576002,30.1293
002a79ded185cb04,20180726125649,22290,80489347,120.1810786,30.10003
運用的技巧:
echo ${var:0:8} 表示從${var}變量的第1個字符(左邊的下標從0開始)開始截取,截取的總個數為8!
xargs -n2 表示將前面命令的結果按照每行2列顯示(從第一行開始算,往下每行2列,直至分配完為止,不夠的就是一列)
sort|uniq 表示去重,僅僅去掉連續出現的相同記錄
1)腳本1:打印test文件的第一個和第二個字段以及自個截取的前面八位字符。
[root@ss-server ~]# cat test_0.sh
#!/bin/bash
file=$(cat /root/test)
while read line
do
#輸出每一行
echo line=${line}
#截取一行的第一列
column1=`echo ${line}| cut -d "," -f1`
echo ${column1}
#輸出前8個字符
echo ${column1:0:8}
#截取一行的第二列
column2=`echo ${line}| cut -d "," -f2`
echo ${column2}
#輸出前8個字符
echo ${column2:0:8}
done <<EOF
${file}
EOF
[root@ss-server ~]# sh test_0.sh
line=25ds51dd225d86af,20180725115911,22570,20443,120.17138,30.119047
25ds51dd225d86af
25ds51dd
20180725115911
20180725
line=002a51dd225d86af,20180725120017,22570,184680195,120.176506,30.11527
002a51dd225d86af
002a51dd
20180725120017
20180725
line=002a51dd225d86af,20180725120058,22290,80489347,120.1810786,30.10003
002a51dd225d86af
002a51dd
20180725120058
20180725
line=002a51dd225d86af,20180725120149,22290,80489345,120.1810786,30.10003
002a51dd225d86af
002a51dd
20180725120149
20180725
line=002a51dd225d86af,20180725120209,22290,189880577,120.18859,30.093405
002a51dd225d86af
002a51dd
20180725120209
20180725
line=102a51dd225d86af,20180725120239,22290,155606668,120.1990471,30.0961
102a51dd225d86af
102a51dd
20180725120239
20180725
line=002a51dd225d86af,20180725120303,22290,155606666,120.1990468,30.0961
002a51dd225d86af
002a51dd
20180725120303
20180725
line=002a51dd225d86af,20180725120434,22290,193501442,120.220661,30.09723
002a51dd225d86af
002a51dd
20180725120434
20180725
line=002a79ded185cb04,20180725125428,22570,185263107,120.1576002,30.1293
002a79ded185cb04
002a79de
20180725125428
20180725
line=002a79ded185cb04,20180726125649,22290,80489347,120.1810786,30.10003
002a79ded185cb04
002a79de
20180726125649
20180726
2)腳本2: 改進下,僅僅獲取第一個和第二個字段的前面8位字符
[root@ss-server ~]# cat test_1.sh
#!/bin/bash
file=$(cat /root/test)
while read line
do
#輸出每一行
#echo line=${line}
#截取一行的第一列
column1=`echo ${line}| cut -d "," -f1`
#echo ${column1}
#輸出前8個字符
echo ${column1:0:8}
#截取一行的第二列
column2=`echo ${line}| cut -d "," -f2`
#echo ${column2}
#輸出前8個字符
echo ${column2:0:8}
done <<EOF
${file}
EOF
[root@ss-server ~]# sh test_1.sh
25ds51dd
20180725
002a51dd
20180725
002a51dd
20180725
002a51dd
20180725
002a51dd
20180725
102a51dd
20180725
002a51dd
20180725
002a51dd
20180725
002a79de
20180725
002a79de
20180726
3)接着繼續改進,獲取第一個和第二個字段的前面8位字符,並排序去重
[root@ss-server ~]# sh test_1.sh |xargs -n2
25ds51dd 20180725
002a51dd 20180725
002a51dd 20180725
002a51dd 20180725
002a51dd 20180725
102a51dd 20180725
002a51dd 20180725
002a51dd 20180725
002a79de 20180725
002a79de 20180726
[root@ss-server ~]# sh test_1.sh |xargs -n2|sort|uniq
002a51dd 20180725
002a79de 20180725
002a79de 20180726
102a51dd 20180725
25ds51dd 20180725
[root@ss-server ~]# sh test_1.sh |xargs -n2|sort|uniq -u|sed 's/ /,/g'
002a51dd,20180725
002a79de,20180725
002a79de,20180726
102a51dd,20180725
25ds51dd,20180725
17)shell特殊符號:sort排序,wc統計,uniq去重,tee,split
1)shell特殊符號
* 任意個任意字符。可以是單個字符,也可以是多個字符。
? 任意一個字符。只能是單個字符
# 注釋字符
\ 脫義字符,轉義符號
$ 變量的前綴
$ 正則里面表示行尾
^ 正則里面表示行首
; 多條命令寫到一行,用;分割
~ 用戶的家目錄。正則表達式里表示匹配符
& 把命令放到后台
> 正確重定向
>> 正確追加重定向
2> 錯誤重定向
2>> 錯誤追加重定向
&> 正確錯誤重定向。 通過在一條命令后面加上">/dev/null 2>&1"表示這條命令執行后不打印任何信息,執行正確或錯誤信息都不打印!
&& 當前面的命令執行成功時,才執行后面的命令
|| 用在shell中表示或者的意思,如果第一條命令執行成功,則不執行第二條命令。如果第一條命令不成功,則執行第二條命令
[root@ss-server ~]# xx=aa
[root@ss-server ~]# yy=bb
[root@ss-server ~]# echo ${xx}${yy}
aabb
[root@ss-server ~]# echo ${xx}\${yy}
aa${yy}
[root@ss-server ~]# echo \${xx}\${yy}
${xx}${yy}
2)管道符和cut
cut 截取
-d 指定分隔符
-f 指定截取那一段
# cat filename|cut -d":" -f2 打印filename文件中以:分割的第2列
# cat filename|cut -d ":" -f 1-3 打印filename文件中以:分割的第1到3列,但是會保留分隔符!!!!!
[root@ss-server ~]# cat test|cut -d"/" -f2
b
2
[root@ss-server ~]# cat test|cut -d"/" -f1-3
a/b/c
1/2/3
[root@ss-server ~]# cat test|awk -F"/" '{print $2}'
b
2
[root@ss-server ~]# cat test|awk -F"/" '{print $1$2$3}'
abc
123
[root@ss-server ~]# cat test|awk -F"/" '{print $1"/"$2"/"$3}'
a/b/c
1/2/3
3)sort、uniq、wc、split、tee命令
===========================================
sort 排序命令
uniq 去重命令
sort -n 默認以數字去排序(默認字母和特殊符號為0,所以會排在最前面)
sort -r 反序排序,即升序。默認是降序排序
sort -kn 以第n列排序(默認降序)
sort -kn -r 以第n列降序排序
sort|uniq 排序去重
sort|uniq -c 排序去重,並打印每個出現的次數。即重復次數
sort|uniq -d 打印出交集部分,即打印出那些重復、相同的字符
sort|uniq -u 打印出除了交集之外的部分。即打印出那些去掉重復字符之后的字符
sort|uniq|sort -k3 #排序去重,並按第3列排序(降序)
sort|uniq|sort -k3 -rn #排序去重,並按第3列排序(降序)
sort|uniq|sort -k3 -r #排序去重,並按第3列排序(升序)
[root@ss-server ~]# cat test
11
ad
0
34
3
21
4
[root@ss-server ~]# cat test|sort -n
0
ad
3
4
11
21
34
[root@ss-server ~]# cat test|sort -rn
34
21
11
4
3
ad
0
[root@ss-server ~]# cat haha
aa 11 hj
2b 7 ok
23 100 jo
op 32 fg
[root@ss-server ~]# cat haha|sort -k2
23 100 jo
aa 11 hj
op 32 fg
2b 7 ok
[root@ss-server ~]# cat haha|sort -k2 -r
2b 7 ok
op 32 fg
aa 11 hj
23 100 jo
[root@ss-server ~]# cat haha|sort -k2 -rn
23 100 jo
op 32 fg
aa 11 hj
2b 7 ok
[root@ss-server ~]# cat test
wang bo
wang bo
kevin ai
han hu
han hu
wang bo
xiao ru
han hu
[root@ss-server ~]# cat test|sort|uniq
han hu
kevin ai
wang bo
xiao ru
[root@ss-server ~]# cat test|sort|uniq -d
han hu
wang bo
[root@ss-server ~]# cat test|sort|uniq -u
kevin ai
xiao ru
----------------------------------
uniq與sort -u 兩種"去重"的區別????
uniq 針對的重復是連續出現的相同記錄!
sort -u 針對的所有出現的相同記錄,包括連續出現和非連續出現的相同記錄!
sort -u 相當於 sour|uniq
[root@ss-server ~]# cat hehe
wang
wang
wang
bobo
wang
[root@ss-server ~]# cat hehe|uniq
wang
bobo
wang
[root@ss-server ~]# uniq hehe
wang
bobo
wang
[root@ss-server ~]# cat hehe|sort -u
bobo
wang
[root@ss-server ~]# sort -u hehe
bobo
wang
[root@ss-server ~]# cat hehe|sort|uniq
bobo
wang
===========================================
wc 統計
wc file 默認統計file文件的行數、單詞數,以及該文件的字節數。即默認加了-l、-w、-c
-l 統計行數
-c 統計字節數
-w 統計字符串,即統計單詞數 (默認以空白格或,為分隔符)
-m 統計字符數 (隱藏的換行符也算,用cat -A 查看隱藏符號)
-L 顯示最長行的長度
[root@ss-server ~]# cat test
wang bo
zhang heng yuan
xiao ru
zhu ge shen hou
[root@ss-server ~]# wc test
4 11 49 test
[root@ss-server ~]# cat test|wc -l -w -c
4 11 49
[root@ss-server ~]# cat test|wc -l
4
[root@ss-server ~]# cat test|wc -w
11
[root@ss-server ~]# cat test|wc -c
49
[root@ss-server ~]# cat test|wc -m
49
[root@ss-server ~]# cat test|wc -L
16
[root@ss-server ~]# cat -A test
wang bo$
zhang heng yuan $
xiao ru$
zhu ge shen hou$
[root@ss-server ~]# cat test.sh
#!/bin/bash
file=$(cat /root/test)
while read line
do
echo "${line} 這一行的字節數為: $(echo ${line}|wc -c)"
done <<EOF
${file}
EOF
[root@ss-server ~]# sh test.sh
wang bo 這一行的字節數為: 8
zhang heng yuan 這一行的字節數為: 16
xiao ru 這一行的字節數為: 8
zhu ge shen hou 這一行的字節數為: 16
===========================================
tee 和輸出重定>向有點像,但是把重定向的內容打印到屏幕上, 即打印到終端屏幕上
-a 追加,和>>相似
[root@ss-server ~]# cat /etc/passwd|head -2
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
[root@ss-server ~]# cat /etc/passwd|head -2|tee
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
[root@ss-server ~]# cat /etc/passwd|head -2|tee > aa.txt
[root@ss-server ~]# cat aa.txt
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
[root@ss-server ~]# cat /etc/passwd|head -2|tee >> aa.txt
[root@ss-server ~]# cat aa.txt
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
===========================================
split 用於將一個文件分割成數個。該指令將大文件分割成較小的文件,在默認情況下將按照每1000行切割成一個小文件。
split -b 100M filename 以文件大小切割 (可以指定文件前綴,默認是x開頭)
split -l 1000 filename 以行數切割,相當於"split -1000" filename
[root@ss-server ~]# du -sh aa
710M aa
[root@ss-server ~]# split -b 100M aa #指定按照每個小文件100M的大小來分割aa文件
[root@ss-server ~]# du -sh *
710M aa
100M xaa
100M xab
100M xac
100M xad
100M xae
100M xaf
100M xag
9.7M xah
[root@ss-server mnt]# cat bb
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
sync:x:5:0:sync:/sbin:/bin/sync
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
halt:x:7:0:halt:/sbin:/sbin/halt
[root@ss-server mnt]# cat bb|wc -l
8
[root@ss-server mnt]# split -2 bb #相當於"split -l 2 bb" 表示指定按照每個小文件2行來分割bb文件
[root@ss-server mnt]# ls
bb xaa xab xac xad
[root@ss-server mnt]# cat xaa
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
[root@ss-server mnt]# cat xab
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
[root@ss-server mnt]# cat xac
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
sync:x:5:0:sync:/sbin:/bin/sync
[root@ss-server mnt]# cat xad
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
halt:x:7:0:halt:/sbin:/sbin/halt
[root@ss-server mnt]# rm -rf x*
[root@ss-server mnt]# ls
bb
[root@ss-server mnt]# du -sh -b bb #查看bb文件的字節數
293 bb
[root@ss-server mnt]# split -b 100 bb #按照每個小文件100個字節來分割bb文件
[root@ss-server mnt]# ls
bb xaa xab xac
[root@ss-server mnt]# du -sh -b xaa
100 xaa
[root@ss-server mnt]# du -sh -b xab
100 xab
[root@ss-server mnt]# du -sh -b xac
93 xac
[root@ss-server mnt]# du -sh bb
32K bb
[root@ss-server mnt]# split -b 8K bb
[root@ss-server mnt]# ls
bb xaa xab xac xad
[root@ss-server mnt]# du -sh xaa
8.0K xaa
[root@ss-server mnt]# du -sh xab
8.0K xab
[root@ss-server mnt]# du -sh xac
8.0K xac
[root@ss-server mnt]# du -sh xad
8.0K xad
小結:
spilt -n 相當於 split -l n 表示按照多少行數來分割成小文件
split -b 按照大小分割成小文件,默認是K,至少是4.0K(即新建文件默認大小)
split -b 10K 按照每10K一個文件分割成小文件
split -b 10M 按照每10M一個文件分割成小文件
split -b 10G 按照每10G一個文件分割成小文件
split -b 100 按照每100 byte字節一個文件分割成小文件
18)按照文件中的單詞及字母去重排序
使用shell腳本實現單詞及字母去重排序
需求
1. 按單詞出現頻率降序排序!
2. 按字母出現頻率降序排序!
[root@ss-server ~]# cat test
wang shi hu shi kui shi juan wang fang fang anhui of huoqiu liuan the squid project provides a number of.
resources to assist users Newsgd.com is the premier New online source of Guangdong news and information,
fully displaying shi Guangdong through is channels including Guangdong New
[root@ss-server ~]# cat test.sh
#!/bin/bash
file=$(cat /root/test)
echo "按單詞出現頻率降序排序!"
for i in ${file}
do
echo ${i}
done|\
sort |uniq -c|sort -nk1 -r
echo "按字母出現頻率降序排序!"
echo ${file} |grep -o "[a-z]" |sort|uniq -c |sort -nk1 -r
[root@ss-server ~]# sh test.sh
按單詞出現頻率降序排序!
4 shi
3 Guangdong
2 wang
2 the
2 of
2 New
2 is
2 fang
1 users
1 to
1 through
1 squid
1 source
1 resources
1 provides
1 project
1 premier
1 online
1 of.
1 number
1 Newsgd.com
1 news
1 liuan
1 kui
1 juan
1 information,
1 including
1 huoqiu
1 hu
1 fully
1 displaying
1 channels
1 assist
1 anhui
1 and
1 a
按字母出現頻率降序排序!
25 n
21 i
20 s
18 u
17 o
17 e
16 a
14 g
12 h
11 r
9 d
7 t
7 l
7 f
6 w
6 c
4 p
4 m
2 y
2 q
2 j
1 v
1 k
1 b
如果在命令行直接操作,如下:
[root@ss-server ~]# echo "按單詞出現頻率降序排序!" && for i in $(cat /root/test);do echo ${i};done|sort |uniq -c|sort -nk1 -r
按單詞出現頻率降序排序!
4 shi
3 Guangdong
2 wang
2 the
2 of
2 New
2 is
2 fang
1 users
1 to
1 through
1 squid
1 source
1 resources
1 provides
1 project
1 premier
1 online
1 of.
1 number
1 Newsgd.com
1 news
1 liuan
1 kui
1 juan
1 information,
1 including
1 huoqiu
1 hu
1 fully
1 displaying
1 channels
1 assist
1 anhui
1 and
1 a
[root@ss-server ~]# echo "按字母出現頻率降序排序!" && echo $(cat /root/test) |grep -o "[a-z]" |sort|uniq -c |sort -nk1 -r
按字母出現頻率降序排序!
25 n
21 i
20 s
18 u
17 o
17 e
16 a
14 g
12 h
11 r
9 d
7 t
7 l
7 f
6 w
6 c
4 p
4 m
2 y
2 q
2 j
1 v
1 k
1 b
grep -o 代表的是只輸出匹配的選項
19)join 命令用法
join命令用於將兩個文件中,指定欄位內容相同的行連接起來。 注意:僅僅只能連接兩個文件!!超過兩個文件就無效! 語法格式: join [-i][-a<1或2>][-e<字符串>][-o<格式>] [-t<字符>][-v<1或2>][-1<欄位>][-2<欄位>][--help] [--version][文件1][文件2] 補充說明:找出兩個文件中,指定欄位內容相同的行,並加以合並,再輸出到標准輸出設備。 參數: -a<1或2> 除了顯示原來的輸出內容之外,還顯示指令文件中沒有相同欄位的行。 -e<字符串> 若[文件1]與[文件2]中找不到指定的欄位,則在輸出中填入選項中的字符串。 -i 比較欄位內容時,忽略大小寫的差異。 -o<格式> 按照指定的格式來顯示結果。 -t<字符> 使用欄位的分隔字符。 -v<1或2> 跟-a相同,但是只顯示文件中沒有相同欄位的行。 -1<欄位> 連接[文件1]指定的欄位。 -2<欄位> 連接[文件2]指定的欄位。 示例: 1)內連接(忽略不匹配的行) ============================================================================= 不指定任何參數的情況下使用join命令,就相當於數據庫中的內連接,關鍵字不匹配的行不會輸出。 不指定參數,即通過兩個文件中相同元素進行連接,如下兩個文件相同的就是第一列的1,2,3,4,5,6,其他多余的都刪除 [root@ss-server ~]# cat test1 1 wang 2 han 3 niu 4 xiao 5 ping 6 hang [root@ss-server ~]# cat test2 1 張三 2 徐思 3 劉磊 4 楊洋 5 李玉 6 王一 7 張麻 8 赫赫 [root@ss-server ~]# join test1 test2 1 wang 張三 2 han 徐思 3 niu 劉磊 4 xiao 楊洋 5 ping 李玉 6 hang 王一 [root@ss-server ~]# cat test3 1 w2 3 r4 4 y7 5 ij [root@ss-server ~]# join test3 test2 1 w2 張三 3 r4 劉磊 4 y7 楊洋 5 ij 李玉 2)左連接(又稱左外連接,顯示左邊所有記錄)。 也就是以左邊件為主!!! ============================================================================= 顯示左邊文件中的所有記錄,右邊文件中沒有匹配的顯示空白。 [root@ss-server ~]# join -a1 test1 test2 1 wang 張三 2 han 徐思 3 niu 劉磊 4 xiao 楊洋 5 ping 李玉 6 hang 王一 [root@ss-server ~]# join -a1 test3 test2 1 w2 張三 3 r4 劉磊 4 y7 楊洋 5 ij 李玉 3)右連接(又稱右外連接,顯示右邊所有記錄)。也就是以右邊文件為主!!! ============================================================================= 顯示右邊文件中的所有記錄,左邊文件中沒有匹配的顯示空白。 [root@ss-server ~]# join -a2 test1 test2 1 wang 張三 2 han 徐思 3 niu 劉磊 4 xiao 楊洋 5 ping 李玉 6 hang 王一 7 張麻 8 赫赫 [root@ss-server ~]# join -a2 test3 test2 1 w2 張三 2 徐思 3 r4 劉磊 4 y7 楊洋 5 ij 李玉 6 王一 7 張麻 8 赫赫 4)全連接(又稱全外連接,顯示左邊和右邊所有記錄) ============================================================================= [root@ss-server ~]# join -a1 -a2 test1 test2 1 wang 張三 2 han 徐思 3 niu 劉磊 4 xiao 楊洋 5 ping 李玉 6 hang 王一 7 張麻 8 赫赫 [root@ss-server ~]# join -a1 -a2 test3 test2 1 w2 張三 2 徐思 3 r4 劉磊 4 y7 楊洋 5 ij 李玉 6 王一 7 張麻 8 赫赫 5)指定輸出字段 ============================================================================= 比如參數 -o 1.1 表示只輸出第一個文件的第一個字段。 [root@ss-server ~]# join -o 1.1 test1 test2 1 2 3 4 5 6 [root@ss-server ~]# join -o 1.1 test3 test2 1 3 4 5 -o 1.2 只輸出第一個文件的第二個字段 [root@ss-server ~]# join -o 1.2 test1 test2 wang han niu xiao ping hang [root@ss-server ~]# join -o 1.2 test3 test2 w2 r4 y7 ij [root@ss-server ~]# join -o 1.2 2.2 test1 test2 wang 張三 han 徐思 niu 劉磊 xiao 楊洋 ping 李玉 hang 王一 [root@ss-server ~]# join -o 2.2 1.2 test1 test2 張三 wang 徐思 han 劉磊 niu 楊洋 xiao 李玉 ping 王一 hang [root@ss-server ~]# join -o 1.1 2.2 test1 test2 1 張三 2 徐思 3 劉磊 4 楊洋 5 李玉 6 王一 6)指定分隔符 ============================================================================= join -t "分隔符" 注意:這個分隔符必須是兩個文件中都存在的!! [root@ss-server ~]# join -t ':' test1 test2 [root@ss-server ~]# join -t ' ' test1 test2 1 wang 張三 2 han 徐思 3 niu 劉磊 4 xiao 楊洋 5 ping 李玉 6 hang 王一 [root@ss-server ~]# cat haha1 a:11:aa:111:aaa b:22:aff:5t c:33:hji:o0p:p8u [root@ss-server ~]# cat haha2 a:nj:5:c b:ok:90 c:yh8 d:p:0:9 [root@ss-server ~]# join -t ":" haha1 haha2 a:11:aa:111:aaa:nj:5:c b:22:aff:5t:ok:90 c:33:hji:o0p:p8u:yh8
20)paste 命令用法
paste 命令用於合並文件的列,會把每個文件以列對列的方式,一列列地加以合並。
語法
paste [-s][-d <間隔字符>][--help][--version][文件...]
參數:
-d<間隔字符>或--delimiters=<間隔字符> : 用指定的間隔字符取代跳格字符。
-s或--serial: 串列進行而非平行處理。
--help: 在線幫助。
--version: 顯示幫助信息。
[文件…]: 指定操作的文件路徑
比如:
paste file testfile testfile1 #合並指定文件的內容
paste file testfile testfile1 -d ":" #合並指定文件的內容,並使用逗號隔開。-d后面的分隔符可以自行定義!
paste -s file1 file2 #將file1和file2文件的各自多行內容合並到各自的一行里面進行展示。使用-s參數可以將一個文件中的多行數據合並為一行進行顯示。
注意:
paste 可以針對多個文件進行合並!!也可以針對一個文件進行處理!
join 只能針對兩個文件進行連接!!!
示例如下:
[root@ss-server ~]# cat aa.txt
11
22
33
44
55
[root@ss-server ~]# cat bb.txt
aa
ab
ac
cc
cd
使用paste命令將文件進行合並
[root@ss-server ~]# paste aa.txt bb.txt
11 aa
22 ab
33 ac
44 cc
55 cd
合並后使用":"隔開
[root@ss-server ~]# paste -d":" aa.txt bb.txt
11:aa
22:ab
33:ac
44:cc
55:cd
合並后使用"-"隔開
[root@ss-server ~]# paste -d"-" aa.txt bb.txt
11-aa
22-ab
33-ac
44-cc
55-cd
paste -s 可以將一個文件中的多行內容合並為一行。例如:
[root@ss-server ~]# cat file
111
222
333
444
555
[root@ss-server ~]# cat file |paste -s
111 222 333 444 555
[root@ss-server ~]# cat file |paste -s -d":"
111:222:333:444:555
[root@ss-server ~]# cat file |paste -s -d ":"
111:222:333:444:555
[root@ss-server ~]# cat file |paste -s -d "-"
111-222-333-444-555
[root@ss-server ~]# cat file |paste -s -d "---"
111-222-333-444-555
[root@ss-server ~]# cat file |paste -s -d ","
111,222,333,444,555
看下面一個小需求:
有一個log.txt文件,第二列是ip,現在需要將log.txt文件中的ip列取出來放在一行,並用逗號隔開。
第一種做法:awk + paste
[root@ss-server ~]# cat log.txt
17:05 172.16.60.34 sadfjsafjsdf
17:14 172.16.60.35 asdfasudfasjfasjfklsafsaf
17:45 172.16.60.38 dsafkjdsajflsajfadf
[root@ss-server ~]# cat log.txt
17:05 172.16.60.34 sadfjsafjsdf
17:14 172.16.60.35 asdfasudfasjfasjfklsafsaf
17:45 172.16.60.38 dsafkjdsajflsajfadf
[root@ss-server ~]# cat log.txt |awk '{print $2}'
172.16.60.34
172.16.60.35
172.16.60.38
[root@ss-server ~]# cat log.txt |awk '{print $2}'|paste -s
172.16.60.34 172.16.60.35 172.16.60.38
[root@ss-server ~]# cat log.txt |awk '{print $2}'|paste -s -d","
172.16.60.34,172.16.60.35,172.16.60.38
另一種做法是:awk + xargs + sed
[root@ss-server ~]# cat log.txt |awk '{print $2}'
172.16.60.34
172.16.60.35
172.16.60.38
[root@ss-server ~]# cat log.txt |awk '{print $2}'|xargs
172.16.60.34 172.16.60.35 172.16.60.38
[root@ss-server ~]# cat log.txt |awk '{print $2}'|xargs|sed 's/ /,/g'
172.16.60.34,172.16.60.35,172.16.60.38
21)su命令用法
su命令用於變更為其他使用者的身份,除 root 外,需要鍵入該使用者的密碼。(即root用戶使用su切換到其他用戶不需要輸入密碼,其他用戶之間使用su切換均需要輸入密碼)。 =================================== 1)su 和 su - 的區別 "su"命令僅僅是切換了用戶身份,但用戶的shell環境變量沒有切換!即su切換用戶身份后,環境變量還是切換前的用戶的環境變量。 "su -"命令不僅切換了用戶身份,用戶的shell環境變量也一起切換!即su切換用戶身份后,環境變量是切換后的用戶的環境變量。 示例: [root@ss-server ~]# su kevin [kevin@ss-server root]$ whoami #用戶身份切換了 kevin [kevin@ss-server root]$ pwd #用戶的環境變量沒有切換 /root [root@ss-server ~]# su - kevin Last login: Fri Dec 6 10:29:59 CST 2019 on pts/16 [kevin@ss-server ~]$ whoami #用戶身份切換了 kevin [kevin@ss-server ~]$ pwd #用戶的環境變量也切換了 /home/kevin 2)-c 參數,表示切換到某用戶后執行一些命令,注意,這個命令是在切換用戶后的狀態下執行的,並且執行命令后再變回原來的用戶。 示例: [root@ss-server ~]# su - kevin -c "pwd" #即是在切換到kevin用戶狀態下執行的"pwd"命令。這里使用"su -",即也切換了shell環境變量 /home/kevin [root@ss-server ~]# su kevin -c "pwd" #即是在切換到kevin用戶狀態下執行的"pwd"命令。這里沒有切換shell環境變量 /root su和sudo權限使用可參考: https://www.cnblogs.com/kevingrace/p/5823003.html https://www.cnblogs.com/kevingrace/p/6130008.html
22)test 命令用法
test 命令最短的定義可能是評估一個表達式;如果條件為真,則返回一個 0 值。如果表達式不為真,則返回一個大於 0 的值(一般為1),也可以將其稱為假值。 需要注意: 1. 在shell中,test命令 和 [] 是同一個命令的不同名稱。也就是說,test xxxx 等同於 [ xxxx ] 的形式!!,注意[]里面的內容兩邊要有空格!! 2. 檢查最后所執行命令的狀態的最簡便方法是使用 $? 值。 3. test功能是檢查文件和比較值。 test 和 [ ] 的語法如下: test expression [ expression ] 其中,expression為test命令構造的表達式。這里expression是test命令可以理解的任何有效表達式,該簡化格式將是我們可能會踫見的最常用格式返回值: test命令或者返回0(真) 或者返回1(假). 因為它們彼此互為別名,所以使用 test 或 [ ] 均需要一個表達式。表達式一般是文本、數字或文件和目錄屬性的比較,並且可以包含變量、常量和運算符。 運算符可以是字符串運算符、整數運算符、文件運算符或布爾運算符 — 我們將在以下各部分依次介紹每一種運算符。 test可理解的表達式類型分為四類: 1. 表達式判斷 2. 字符串比較 3. 數字比較 4. 文件比較 1)判斷表達式 -------------------------------------------------------- if test 表達式 #表達式為真 if test ! 表達式 #表達式為假 test 表達式1 –a 表達式 2 #兩個表達式都為真 test 表達式1 –o 表達式2 #兩個表達式有一個為真 2)判斷字符串 -------------------------------------------------------- test –n 字符串 #字符串的長度非零。即非空字符串。 test –z 字符串 #字符串的長度為零。即空字符串。 test 字符串1=字符串 2 #字符串相等 test 字符串1 !=字符串2 #字符串不等 3)判斷整數 -------------------------------------------------------- test 整數1 –eq 整數2 #整數相等 test 整數1 –ne 整數 2 #整數1不等於整數2 test 整數 1 –ge 整數2 #整數1大於等於整數2 test 整數1 –gt 整數 2 #整數1大於整數2 test 整數1 –le 整數 2 #整數1小於等於整數2 test 整數1 –lt 整數 2 #整數1小於整數2 4)判斷文件 -------------------------------------------------------- test File1 –nt File2 #文件1比文件2新(new) test File1 –ot File2 #文件1比文件2舊(old) test File1 –ef File2 #判斷兩個文件是否與同一個設備相連,是否擁有相同的inode號 test –d File #文件存在並且是目錄 test –e File #文件存在 test –f File #文件存在並且是正規文件 test –h File #文件存在並且是一個符號鏈接(同-L)。即存在且是軟鏈接文件 test –L File #文件存在並且是一個符號鏈接(同-h)。即存在且是軟鏈接文件 test –k File #文件存在並且設置了sticky位(即設置了t權限) test –r File #文件存在並且可讀 test –w File #文件存在並且可寫 test –x File #文件存在並且可執行 示例如下: 1)判斷表達式。下面hang.txt文件是不存在的。 [root@ss-server ~]# cat test.sh #!/bin/bash if test `cat /hang.txt`;then echo "haha.txt is exist" else echo "haha.txt is not exist" fi [root@ss-server ~]# sh test.sh cat: /hang.txt: No such file or directory haha.txt is not exist [root@ss-server ~]# cat test.sh #!/bin/bash if test !`cat /hang.txt >/dev/null 2>&1`;then echo "haha.txt is exist" else echo "haha.txt is not exist" fi [root@ss-server ~]# sh test.sh haha.txt is exist 使用[]的形式操作上面的腳本 [root@ss-server ~]# cat test.sh #!/bin/bash if [ `cat /hang.txt` ];then echo "haha.txt is exist" else echo "haha.txt is not exist" fi [root@ss-server ~]# sh test.sh cat: /hang.txt: No such file or directory haha.txt is not exist [root@ss-server ~]# cat test.sh #!/bin/bash if [ !`cat /hang.txt >/dev/null 2>&1` ];then echo "haha.txt is exist" else echo "haha.txt is not exist" fi [root@ss-server ~]# sh test.sh haha.txt is exist 2)判斷第一個參數是否為空字符串,不空則打印該字符串,為空則打印"空字符串" [root@ss-server ~]# cat test.sh #!/bin/bash if test -n "$1";then echo "$1" else echo "空字符串" fi 執行結果: [root@ss-server ~]# sh test.sh beijing beijing [root@ss-server ~]# sh test.sh 空字符串 由於test xxxx 等同於 [ xxxx ] 的形式,所以上面還可以改成: [root@ss-server ~]# cat test.sh #!/bin/bash if [ -n "$1" ];then echo "$1" else echo "空字符串" fi 執行結果: [root@ss-server ~]# sh test.sh shanghai shanghai [root@ss-server ~]# sh test.sh 空字符串 3)文件判斷 [root@ss-server ~]# test -h heihei [root@ss-server ~]# echo $? 0 [root@ss-server ~]# [ -h heihei ] [root@ss-server ~]# echo $? 0 [root@ss-server ~]# test -x haha [root@ss-server ~]# echo $? 1 [root@ss-server ~]# [ -x haha ] [root@ss-server ~]# echo $? 1 [root@ss-server ~]# ll haha -rw-r--r-- 1 root root 32 Dec 18 13:06 haha [root@ss-server ~]# ll hehe -rw-r--r-- 1 root root 0 Dec 19 17:19 hehe [root@ss-server ~]# test haha -nt hehe [root@ss-server ~]# echo $? 1 [root@ss-server ~]# [ haha -nt hehe ] [root@ss-server ~]# echo $? 1 [root@ss-server ~]# test haha -ot hehe [root@ss-server ~]# echo $? 0 [root@ss-server ~]# [ haha -ot hehe ] [root@ss-server ~]# echo $? 0
23)printf 命令用法
關於printf的含義,需要注意下面四點: 1. printf 命令用於格式化輸出,它是echo命令的增強版。它是C語言printf()庫函數的一個有限的變形,並且在語法上有些不同。 2. printf 由 POSIX 標准所定義,因此使用 printf 的腳本比使用 echo 移植性好。 3. printf 使用引用文本或空格分隔的參數,外面可以在 printf 中使用格式化字符串,還可以制定字符串的寬度、左右對齊方式等。 4. 默認 printf 不會像 echo 自動添加換行符,但是可以手動添加 \n。 printf 命令的語法: printf format-string [arguments...] 參數說明: format-string: 為格式控制字符串 arguments: 為參數列表。 需要注意: 1. printf 不像 echo 那樣會自動換行,必須顯式添加換行符(\n)。 2. printf 命令不用加括號 3. format-string 可以沒有引號,但最好加上,單引號雙引號均可。 4. 參數多於格式控制符(%)時,format-string 可以重用,可以將所有參數都轉換。 5. arguments 使用空格分隔,不用逗號。 示例如下: echo默認自動換行。加上-n參數,則不換行 printf默認不會自動換行 [root@ss-server ~]# echo "Hello, Shell" Hello, Shell [root@ss-server ~]# echo -n "Hello, Shell" Hello, Shell[root@ss-server ~]# [root@ss-server ~]# printf "Hello, Shell" Hello, Shell[root@ss-server ~]# [root@ss-server ~]# printf "Hello, Shell\n" Hello, Shell [root@ss-server ~]# [root@ss-server ~]# printf "%-10s %-8s %-4s\n" 姓名 性別 體重kg 姓名 性別 體重kg [root@ss-server ~]# printf "%-10s %-8s %-4.2f\n" 小明 男 65.1234 小明 男 65.12 [root@ss-server ~]# printf "%-10s %-8s %-4.2f\n" 小洋 男 72.6589 小洋 男 72.66 [root@ss-server ~]# printf "%-10s %-8s %-4.2f\n" 小梅 女 48.7167 小梅 女 48.72 需要注意: %d指的是針對數字的格式化 %s指的是字符串的格式化 %-10s 指一個寬度為10個字符(-表示左對齊,沒有則表示右對齊),任何字符都會被顯示在10個字符寬的字符內,如果不足則自動以空格填充,超過也會將內容全部顯示出來。 %-4.2f 指格式化為小數,其中.2指保留2位小數。 示例如下: format-string為雙引號(換行) [root@ss-server ~]# printf "%d %s\n" 1 "abc" 1 abc 單引號與雙引號效果一樣(換行) [root@ss-server ~]# printf '%d %s\n' 1 "abc" 1 abc 沒有引號也可以輸出(不換行) [root@ss-server ~]# printf %s abcdef abcdef[root@ss-server ~]# 格式只指定了一個參數,但多出的參數仍然會按照該格式輸出,format-string 被重用 [root@ss-server ~]# printf %s abc def abcdef[root@ss-server ~]# [root@ss-server ~]# printf "%s\n" abc def abc def [root@ss-server ~]# printf "%s %s %s\n" a b c d e f g h i j a b c d e f g h i j 如果沒有 arguments,那么 %s 用NULL代替,%d 用 0 代替 [root@ss-server ~]# printf "%s and %d \n" and 0 如果以 %d 的格式來顯示字符串,那么會有警告,提示無效的數字,此時默認置為 0 [root@ss-server ~]# printf "The first program always prints'%s,%d\n'" Hello Shell -bash: printf: Shell: invalid number The first program always prints'Hello,0 printf的轉義序列 ================================================================================= \a 警告字符,通常為ASCII的BEL字符 \b 后退 \c 抑制(不顯示)輸出結果中任何結尾的換行字符(只在%b格式指示符控制下的參數字符串中有效),而且,任何留在參數里的字符、任何接下來的參數以及任何留在格式字符串中的字符,都被忽略 \f 換頁(formfeed) \n 換行 \r 回車(Carriage return) \t 水平制表符 \v 垂直制表符 \\ 一個字面上的反斜杠字符 \ddd 表示1到3位數八進制值的字符。僅在格式字符串中有效 \0ddd 表示1到3位的八進制值字符 示例如下: [root@ss-server ~]# printf "welcome, 哈哈,北京:<%s>\n" "A\nB" welcome, 哈哈,北京:<A\nB> [root@ss-server ~]# printf "welcome, 哈哈,北京::<%b>\n" "A\nB" welcome, 哈哈,北京::<A B> [root@ss-server ~]# printf "www.kevin.com \a" www.kevin.com [root@ss-server ~]#
24)zcat 和 zgrep命令用法
服務器端常有很多壓縮過的日志文件,當需要查找日志中某些特定信息的時候,為了避免解壓文件,可以使用zgrep,zcat等命令查找、查看壓縮文件中的信息。
1)gzip打包壓縮文件 [root@localhost ~]# cat test name:wangbo age:29 address:beijing school:lanzhoucaida [root@localhost ~]# gzip test [root@localhost ~]# ls test.gz test.gz ------------------------------------------------------------------ 注意: gzip filename # 將filename文件打包壓縮成filename.gz gzip -d filename.gz # 解壓filename.gz文件 gzip -r dirname # 將dirname目錄下的文件打包壓縮為.gz格式的文件 gzip打包壓縮一個文件,打包后,原文件就變成壓縮文件,原文件不存在了! ----------------------------------------------------------------- 現在想在不解壓的情況下查看或者搜索test.tar.gz文件中的數據 使用zcat命令 [root@localhost ~]# zcat test.gz |grep "age" age:29 使用gzip命令 [root@localhost ~]# zgrep "age" test.gz age:29 [root@localhost ~]# zgrep "age" test.gz |more age:29 2)zip打包壓縮文件 以上針對的是gzip壓縮的gz格式的壓縮文件,如果換成zip格式的話,效果如何?繼續看下面: [root@localhost ~]# gzip -d test.gz [root@localhost ~]# zip test.zip test adding: test (stored 0%) [root@localhost ~]# ls test* test test.zip [root@localhost ~]# zcat test.zip name:wangbo age:29 address:beijing school:lanzhoucaida [root@localhost ~]# zgrep "address" test.zip address:beijing [root@localhost ~]# zgrep "address" test.zip |more address:beijing ------------------------------------------------------------------ 注意: zip filename.zip filename #將filename文件打包壓縮成filename.zip unzip filename.zip #解壓filename.zip文件 unzip -r xxx.zip ./* #當前目錄的內容打包壓縮為為xxx.zip文件 unzip -r dirname.zip dirname #當dirname目錄打包壓縮為dirname.zip文件 zip打包壓縮后,原文件還存在,原文件和打包文件同時存在 ------------------------------------------------------------------ 3)tar打包壓縮文件 如果換成tar打包壓縮文件,又該如何?繼續往下看 [root@localhost ~]# ls test* test test.zip [root@localhost ~]# rm -f test.zip [root@localhost ~]# tar -zvcf test.tar.gz test test [root@localhost ~]# ls test* test test.tar.gz [root@localhost ~]# zgrep "age" test.tar.gz Binary file (standard input) matches 上面報錯是因為grep認為test.tar.gz是個二進制文件,無法grep查找。 解決辦法:加上-a參數即可 [root@localhost ~]# zgrep -a "age" test.tar.gz age:29 [root@localhost ~]# zgrep -a "age" test.tar.gz |more age:29 [root@localhost ~]# zcat test.tar.gz test0000644000000000000000000000006713576332517010475 0ustar rootrootname:wangbo age:29 address:beijing school:lanzhoucaida [root@localhost ~]# zgrep -a "name" test.tar.gz |more test 可以看出,對於tar格式的壓縮文件,zcat或zgrep查看或搜索,會在第一行多處一個字符串, 這個字符串是tar打包壓縮后出現的,並把文件正文中的第一行內容和這個字符串放在一行! 這樣當zgrep搜索內容的字段在原文第一行,則就搜索不出來了! ------------------------------------------------------------------ 如果一個目錄被打成tar包了,怎么查看這個tar包里有哪些文件? 如下,使用"tar -tvf"查看即可 [root@localhost ~]# tar -tvf test1.tar.gz drwxr-xr-x root/root 0 2020-01-13 12:59 test1/ -rw-r--r-- root/root 7 2020-01-13 12:59 test1/b.txt -rw-r--r-- root/root 7 2020-01-13 12:59 test1/a.txt drwxr-xr-x root/root 0 2020-01-13 13:00 test1/haha/ -rw-r--r-- root/root 9 2020-01-13 13:00 test1/haha/test.txt
25); || && 區別
三種符號均用於多用命令之間的銜接。區別如下: 1); 符號,表示不論前面的命令執行結果是true還是false,后面的命令照樣執行。 2)|| 符號,表示只有前面的命令執行結果為false時,后面的命令才會繼續執行;如果前面命令執行結果為true,則后面的命令就不會繼續執行了。 3)&& 符號,表示只有前面的命令執行結果為true時,后面的命令才會繼續執行;如果前面命令執行結果為false,則后面的命令就不會繼續執行了。 示例如下: [root@kevin_test ~]# cat haha.txt cat: haha.txt: No such file or directory [root@kevin_test ~]# hostname kevin_test [root@kevin_test ~]# cat haha.txt ; hostname cat: haha.txt: No such file or directory kevin_test [root@kevin_test ~]# hostname ; cat haha.txt kevin_test cat: haha.txt: No such file or directory [root@kevin_test ~]# cat haha.txt || hostname cat: haha.txt: No such file or directory kevin_test [root@kevin_test ~]# hostname || cat haha.txt kevin_test [root@kevin_test ~]# cat haha.txt && hostname cat: haha.txt: No such file or directory [root@kevin_test ~]# hostname && cat haha.txt kevin_test cat: haha.txt: No such file or directory
26)如何比較兩個目錄
在Linux系統里如何快速比較兩個目錄中的文件列表的差別,比如test1、test2兩個目錄,對兩個目錄中多出的文件、少掉的文件分別做處理。基於運維日常中常用的方法,總結如下:
首先比較下兩個目錄的結構:
[root@ss-server ~]# yum install -y tree
[root@ss-server opt]# tree test1 test2
test1
├── a.txt
├── b.txt
└── haha
└── test.txt
test2
├── a.txt
├── b.txt
└── haha
├── bo.txt
└── test.txt
2 directories, 7 files
一、命令行輸出的結果 #################################################################################################
方法1:使用diff命令 -----------------------------------------------------------------
[root@ss-server ~]# cd /opt/
[root@ss-server opt]# diff -r test1 test2
diff -r test1/a.txt test2/a.txt
1a2
> hhhhh # 該行內容是test2/a.txt文件比test1/a.txt文件多出來的內容"
diff -r test1/b.txt test2/b.txt
1c1,2
< abcdfg # 該行是test1/b.txt文件內容
---
> 66666 # 這兩行是test2/b.txt文件內容。這兩個文件內容各不相同
> asdfsafd
Only in test2/haha: bo.txt # test2/haha目錄相比如test1/haha目錄多處一個bo.txt文件。
# 另外,test1/haha和test2/haha兩個目錄下的test.txt文件內容相同。diff命令只輸出不同的,相同的不輸出。
需要注意:diff命令會對兩個每個文件中的每一行都做比較,所以文件較多或者文件較大的時候會非常慢。請謹慎使用
方法2:使用diff結合tree -----------------------------------------------------------------
[root@ss-server opt]# diff <(tree -Ci --noreport /opt/test1) <(tree -Ci --noreport /opt/test2)
1c1
< /opt/test1
---
> /opt/test2
4a5
> bo.txt #/opt/test2目錄比/opt/test1目錄多處一個bo.txt文件
# 該方法比較的只是兩個目錄下多處的文件。
說明:
tree的-C選項是輸出顏色,如果只是看一下目錄的不同,可以使用該選項,但在結合其他命令使用的時候建議不要使用該選項,因為顏色也會轉換為對應的編碼而輸出;
-i是不縮進,建議不要省略-i,否則diff的結果很難看,也不好繼續后續的文件操作;
--noreport是不輸出報告結果,建議不要省略該選項。
該方法效率很高!!!
方法3:find結合diff -----------------------------------------------------------------
[root@ss-server opt]# find /opt/test1 -printf "%P\n" | sort > file1_diff.txt
[root@ss-server opt]# find /opt/test2 -printf "%P\n" | sort | sort | diff file1_diff.txt -
4a5
> haha/bo.txt
說明:
< 代表的是第一個目錄/opt/test1中有,而第二個目錄/opt/test2中沒有的文件
> 則相反,代表的是第二個目錄/opt/test2中有而第一個目錄/opt/test1中沒有。
不要省略-printf "%P\n",此處的%P表示find的結果中去掉前綴路徑。
例如/opt/test2目錄下多出了haha/bo.txt文件,所以結果顯示的是haha/bo.txt,將前綴路徑/opt/test2去掉了!
這樣效率很高,輸出也簡潔!
如果不想使用-printf,那么先進入各目錄再find也是可以的。如下:
[root@ss-server opt]# (cd /opt/test1;find . | sort >/tmp/file1.txt)
[root@ss-server opt]# (cd /opt/test2;find . | sort | diff /tmp/file1.txt -)
4a5
> ./haha/bo.txt
上面將命令放進括號中執行是為了在子shell中切換目錄,不用影響當前所在目錄。
方法4:使用rsync ------------------------------------------------------------------------
[root@ss-server opt]# rsync -rvn --delete /opt/test1/ /opt/test2 | sed -n '2,/^$/{/^$/!p}'
a.txt
b.txt
deleting haha/bo.txt
haha/test.txt
其中deleting所在的行就是第二個目錄/opt/test2中多出的文件。
其他的都是兩個目錄中相同文件名的文件。
如果想區分出不同的是目錄還是文件。可以加上"-i"選項。
[root@ss-server opt]# rsync -rvn -i --delete /opt/test1/ /opt/test2 | sed -n '2,/^$/{/^$/!p}'
>f.sT...... a.txt
>f.sT...... b.txt
*deleting haha/bo.txt
>f..T...... haha/test.txt
其中>f+++++++++中的f代表的是文件,d代表的目錄。
需要注意上面的rsync比較目錄的命令中有幾點要說明:
1)一定不能缺少-n選項,它表示:嘗試着進行rsync同步,但不會真的同步。
2)第一個目錄(/opt/test1/)后一定不能缺少斜線,否則表示將/opt/test1整個目錄同步到/opt/test2目錄下。
3)其它選項,如"-r -v --delete"也都不能缺少,它們的含義很簡單,分別表示遞歸、打印詳細信息、同步前刪除(只是模擬rsync操作,不會真的執行)
4)sed的作用是過濾掉和文件不相關的內容。
5)以上rsync命令是假定了比較兩個目錄中只有普通文件和目錄,沒有軟鏈接、塊設備等特殊文件。如果有,請考慮加上對應的選項或者使用-a替代-r,
否則結果中將出現skipping non-regular file的提示。但請注意,如果有軟鏈接,且加了對應選項(-l或-a或其他相關選項),則可能會出現fileA-->fileB的輸出。
6)這種方式效率很高,因為rsync的原因,篩選的可定制性也非常強。
二、圖形化的比較結果 #################################################################################################
方法1:使用vimdiff ---------------------------------------------------------------------
vimdiff命令用於快速比較和合並少量文件,詳細用法參考:https://man.linuxde.net/vimdiff
[root@ss-server opt]# vimdiff <(cd /opt/test1; find . | sort) <(cd /opt/test2; find . | sort)
或者
[root@ss-server ~]# vimdiff <(find /opt/test1 -printf "%P\n"| sort) <(find /opt/test2 -printf "%P\n"| sort)
如何從圖形中退出??
依次按:Shift + 冒號,輸入quit,回車; 再接着輸入依次Shift + 冒號,輸入quit,回車。 以上兩次操作后,就退出圖形了。
方法2:使用meld ---------------------------------------------------------------------
meld是python寫的一個圖形化文件/目錄比較工具,所以必須先安裝Linux圖形界面或設置好圖形界面接受協議。
它的功能非常豐富,和win下的beyond compare有異曲同工之妙。
meld是一個很酷的圖形化工具(一個 GNOME 桌面下的可視化的比較和合並工具),可供那些喜歡使用鼠標的人使用,可按如下來安裝。
[root@ss-server ~]# yum install -y meld
安裝了meld之后,就可以在linux桌面上使用meld比較兩個目錄了,功能非常強大。這里就不做介紹了~
27)Shell中判斷傳入的變量是否為空? ( 使用 if [ X$1 == X ] )("== 和 = ")("()和{}")
[root@ss-server ~]# cat test.sh
#!/bin/bash
Deploy_Env=$1
Deploy_Env=`echo ${Deploy_Env} | tr '[a-z]' '[A-Z]'`
echo ${Deploy_Env}
if [[ x${Deploy_Env} == x ]];then
echo "變量參數為空"
exit 0
fi
[root@ss-server ~]# sh test.sh aa
AA
[root@ss-server ~]# sh test.sh
變量參數為空
[root@ss-server ~]#
注意: 上面test.sh腳本中的"==" 也可以寫成 "="
這里需要說明下
===========================================================
1)== 和 = 的區別
== 可用於判斷變量是否相等
= 除了可用於判斷變量是否相等外,還可以表示賦值。
在 [ ] 或 [[ ]] 中,= 與 == 都表示判斷(字符串比較),此種情況下二者是等價的!
例如:
[root@ss-server ~]# a=beijing
[root@ss-server ~]# b=shanghai
[root@ss-server ~]# [ $a=$b ] && echo "equal"
equal
[root@ss-server ~]# [ $a==$b ] && echo "equal"
equal
[root@ss-server ~]# [[ $a=$b ]] && echo "equal"
equal
[root@ss-server ~]# [[ $a==$b ]] && echo "equal"
equal
在 (( )) 中 = 表示賦值, == 表示判斷(整數比較),此種情況下二者是不等價的!
例如:
[root@ss-server ~]# ((n=5))
[root@ss-server ~]# echo $n
5
[root@ss-server ~]# ((n==5)) && echo "equal"
equal
===========================================================
2)()和{} 區別
()和{}都是對一串的命令進行執行,但有所區別:
相同點:
()和{}都是把一串的命令放在括號里面,並且命令之間用;號隔開
不同點:
()只是對一串命令重新開一個子shell進行執行,{}對一串命令在當前shell執行。
()最后一個命令可以不用分號,{}最后一個命令要用分號。
()里的第一個命令和左邊括號不必有空格,{}的第一個命令和左括號之間必須要有一個空格!!!
()和{}中括號里面的某個命令的重定向只影響該命令,但括號外的重定向則影響到括號里的所有命令。
示例如下:
[root@ss-server ~]# var=test
[root@ss-server ~]# echo $var
test
[root@ss-server ~]# (var=notest;echo $var)
notest
[root@ss-server ~]# echo $var
test
[root@ss-server ~]# { var=notest;echo $var;}
notest
[root@ss-server ~]# echo $var
notest
有上面可知:
在{}中 第一個命令和{之間必須有空格,結束必須有;分號!!
{}中的修改了$var的值,說明在當前shell執行!!
[root@ss-server ~]# { var1=test1;var2=test2;echo $var1>a;echo $var2;}
test2
[root@ss-server ~]# cat a
test1
[root@ss-server ~]# { var1=test1;var2=test2;echo $var1;echo $var2;}>a
[root@ss-server ~]# cat a
test1
test2
腳本如下:
[root@ss-server ~]# cat test.sh
#!/bin/bash
(
echo "1"
echo "2"
) | awk '{print NR,$0}'
[root@ss-server ~]# sh test.sh
1 1
2 2
28)eval 命令用法
eval可讀取一連串的參數,然后再依參數本身的特性來執行。eval是shell內建命令,可用shell查看其用法。參數不限數目,彼此之間用分號隔開。
eval [參數]
eval命令將會首先掃描命令行進行所有的置換,然后再執行該命令。
該命令適用於那些一次掃描無法實現其功能的變量。該命令對變量進行兩次掃描。這些需要進行兩次掃描的變量有時被稱為復雜變量。
不過這些變量本身並不復雜。eval命令也可以用於回顯簡單變量,不一定是復雜變量。
示例1: eval命令也可以用於回顯簡單變量,不一定是復雜變量 ##########################################################################
[root@localhost ~]# NAME=kevin
[root@localhost ~]# echo ${NAME}
kevin
[root@localhost ~]# eval echo ${NAME}
kevin
[root@localhost ~]# echo '${'"NAME"'}'
${NAME}
[root@localhost ~]# eval echo '${'"NAME"'}'
kevin
[root@localhost ~]# a=123
[root@localhost ~]# echo ${a}
123
[root@localhost ~]# eval echo ${a}
123
[root@localhost ~]# echo '${a}' #單引號默認不識別變量
${a}
[root@localhost ~]# eval echo '${a}' #使用eval后,用戶回顯變量,則單引號里面的變量就會被顯示出來
123
[root@localhost ~]# echo "${a}" #雙引號默認識別變量
123
[root@localhost ~]# eval echo "${a}"
123
示例2:執行含有字符串的命令 ##########################################################################
首先我們首先創建一個名為test的小文件,在這個小文件中含有一些文本。
接着,將cat test賦給變量myfile,現在我們e c h o該變量,看看是否能夠執行上述命令。
[root@localhost ~]# cat test
Hello World!!!
I am a chinese Boy!
將"cat test"賦給變量myfile
[root@localhost ~]# myfile="cat test"
如果直接echo該變量,那么將無法列出test文件中的內容。
[root@localhost ~]# echo $myfile
cat test
接着來試一下eval命令,記住eval命令將會對該變量進行兩次掃瞄。
[root@localhost ~]# eval $myfile
Hello World!!!
I am a chinese Boy!
從上面的結果可以看出,使用e v a l命令不但可以置換該變量,還能夠執行相應的命令。第
一次掃描進行了變量置換,第二次掃描執行了該字符串中所包含的命令cat test。
示例3: eval可以用來顯示出傳遞給腳本的最后一個參數 ##########################################################################
下面提及的命令是eval其中一個很普通的應用,它重復了1次命令行參數傳遞過程,純粹地執行命令的命令。
[root@localhost ~]# echo 'eval echo \$$#' > test
[root@localhost ~]# cat test
eval echo \$$#
[root@localhost ~]# sh test aa bb cc
cc
[root@localhost ~]# cat test.sh
#!/bin/bash
echo "Total of the arguments passed $#"
echo "The process Id is $$"
echo "Last argument os "$(eval echo \$$#)""
[root@localhost ~]# sh test.sh beijing shanghai anhui
Total of the arguments passed 3
The process Id is 82540
Last argument os anhui
在上面的腳本中,eval命令首先把$# 解析為當前shell的參數個數,然后再進行第二次掃描時得出最后一個參數。
示例4:給每個值一個變量名 ##########################################################################
可以給一個值一個變量名。下面會對此做些解釋,假定有一個名為test2的文件:
[root@localhost ~]# cat test2
COMMANY TQ
LANGUE ENGLISH
LIKE YES
希望該文件中的第一列成為變量名,第二列成為該變量的值,這樣就可以:
[root@localhost ~]# cat test.sh
#!/bin/bash
while read NAME VALUE
do
eval "${NAME}=${VALUE}"
done <test2
echo "$COMMANY $LANGUE $LIKE"
[root@localhost ~]# sh test.sh
TQ ENGLISH YES
示例5:使用eval命令進行1次命令行參數傳遞 ##########################################################################
[root@localhost ~]# cat test.sh
#!/bin/bash
NODE_IP_LIST=$1
NODE_PORT_LIST=$2
for ((i=1;i<=3;i++)); do
eval NODE_IP='`echo ${NODE_IP_LIST}|awk -F, "{ print $"$i" }"`' #這里awk語句中的print后面{}兩邊之所以加雙引號而不是單引號,是因為要將$i變量識別出來!!!
eval NODE_PORT='`echo ${NODE_PORT_LIST}|awk -F, "{ print $"$i" }"`' #如果awk語句中的print后面{}兩邊加單引號,則$i變量就識別不出來了!!!
echo "address is ${NODE_IP}:${NODE_PORT}"
done
[root@localhost ~]# sh test.sh 172.16.60.21,172.16.60.22,172.16.60.23 8080,8081,8082
address is 172.16.60.21:8080
address is 172.16.60.22:8081
address is 172.16.60.23:8082
如果上面test.sh腳本中不使用eval,將eval去掉的話,則命令行參數就傳遞失敗了
[root@localhost ~]# cat test.sh
#!/bin/bash
NODE_IP_LIST=$1
NODE_PORT_LIST=$2
for ((i=1;i<=3;i++)); do
NODE_IP='`echo ${NODE_IP_LIST}|awk -F, "{ print $"$i" }"`'
NODE_PORT='`echo ${NODE_PORT_LIST}|awk -F, "{ print $"$i" }"`'
echo "address is ${NODE_IP}:${NODE_PORT}"
done
[root@localhost ~]# sh test.sh 172.16.60.21,172.16.60.22,172.16.60.23 8080,8081,8082
address is `echo ${NODE_IP_LIST}|awk -F, "{ print $"$i" }"`:`echo ${NODE_PORT_LIST}|awk -F, "{ print $"$i" }"`
address is `echo ${NODE_IP_LIST}|awk -F, "{ print $"$i" }"`:`echo ${NODE_PORT_LIST}|awk -F, "{ print $"$i" }"`
address is `echo ${NODE_IP_LIST}|awk -F, "{ print $"$i" }"`:`echo ${NODE_PORT_LIST}|awk -F, "{ print $"$i" }"`
將NODE_IP和NODE_PORT兩個參數變量后面的單引號去掉,則效果和使用了eval命令一樣
[root@localhost ~]# cat test.sh
#!/bin/bash
NODE_IP_LIST=$1
NODE_PORT_LIST=$2
for ((i=1;i<=3;i++)); do
NODE_IP=`echo ${NODE_IP_LIST}|awk -F, "{ print $"$i" }"` #這里awk語句中的print后面{}兩邊之所以加雙引號而不是單引號,是因為要將$i變量識別出來!!!
NODE_PORT=`echo ${NODE_PORT_LIST}|awk -F, "{ print $"$i" }"` #如果awk語句中的print后面{}兩邊加單引號,則$i變量就識別不出來了!!!
echo "address is ${NODE_IP}:${NODE_PORT}"
done
[root@localhost ~]# sh test.sh 172.16.60.21,172.16.60.22,172.16.60.23 8080,8081,8082
address is 172.16.60.21:8080
address is 172.16.60.22:8081
address is 172.16.60.23:8082
##########################################################################################################################
需要注意一個細節:
下面兩種方式是一樣的效果,即awk中-F后面的分隔符兩邊加不加引號,加雙引號還是單引號,都不影響分割的效果!!!
但是后面print的{}兩邊必須是單引號!!!
awk -F"分隔符" '{print $n}' filename
awk -F'分隔符' '{print $n}' filename
awk -F分隔符 '{print $n}' filename
[root@localhost ~]# echo 172.16.60.21,172.16.60.22,172.16.60.23|awk -F"," '{ print $1 }'
172.16.60.21
[root@localhost ~]# echo 172.16.60.21,172.16.60.22,172.16.60.23|awk -F, '{ print $1 }'
172.16.60.21
下面兩種方式均是不正確的。直接將$1作為前面echo輸出的整體了,即-F后面的分隔符沒有起作用!
[root@localhost ~]# echo 172.16.60.21,172.16.60.22,172.16.60.23|awk -F, "{ print $1 }"
172.16.60.21,172.16.60.22,172.16.60.23
[root@localhost ~]# echo 172.16.60.21,172.16.60.22,172.16.60.23|awk -F"," "{ print $1 }"
172.16.60.21,172.16.60.22,172.16.60.23
下面表達方式也是不正確的。$1兩邊加雙引號,直接將$1打印出來了
[root@localhost ~]# echo 172.16.60.21,172.16.60.22,172.16.60.23|awk -F, '{ print "$1" }'
$1
[root@localhost ~]# echo 172.16.60.21,172.16.60.22,172.16.60.23|awk -F"," '{ print "$1" }'
$1
29)shell判斷傳入的便利是否為空 if [ x$1 = x ]
if語句中使用"x$1 = x"來判斷$1傳入參數是否為空。
如果x$1等於x,則$1傳入參數為空。
如果x$1不等於x,則$1傳入參數不為空。
#################### 注意 ####################
x要么兩邊都是小寫,要么兩邊都是大寫!
等號可以是"=",也可以使用"=="
###############################################
如下腳本配置,如果x${num}的值為x,則$num傳入的變量為空!
[root@localhost ~]# cat test.sh
#!/bin/bash
num=$1
if [ x${num} = x ];then
echo "the num argu is empty!"
else
echo "the num args is $1"
fi
[root@localhost ~]# sh test.sh
the num argu is empty!
[root@localhost ~]# sh test.sh beijing
the num args is beijing
[root@localhost ~]# cat test.sh
#!/bin/bash
num=$1
if [ X${num} = X ];then
echo "the num argu is empty!"
else
echo "the num args is $1"
fi
[root@localhost ~]# sh test.sh
the num argu is empty!
[root@localhost ~]# sh test.sh shanghai
the num args is shanghai
[root@localhost ~]# cat test.sh
#!/bin/bash
num=$1
if [ X${num} == X ];then
echo "the num argu is empty!"
else
echo "the num args is $1"
fi
[root@localhost ~]# sh test.sh
the num argu is empty!
[root@localhost ~]# sh test.sh shenzheng
the num args is shenzheng
30)set 命令用法
set命令是 Bash 腳本的重要環節,卻常常被忽視,導致腳本的安全性和可維護性出問題。set命令用來修改 Shell 環境的運行參數,也就是可以定制環境。set一共有十幾個參數可以定制,官方手冊有完整清單,這里重點介紹其中最常用的幾個參數。
1)set -e 參數
========================================================================================
set -e:表示執行的時候如果出現了返回值為非零,整個腳本就會立即退出。
set +e:表示執行的時候如果出現了返回值為非零,將會繼續執行下面的腳本。
即"set +e"表示關閉-e選項,"set -e"表示重新打開-e選項。
"set -e"命令作用
對於編寫的每個腳本,都應該在文件開頭加上set -e,這句語句的作用是告訴bash,如果任何語句的執行結果不是true則應該退出。
這樣的好處是防止錯誤像滾雪球般變大導致一個致命的錯誤,而這些錯誤本應該在之前就被處理掉。
如果要增加可讀性,可以使用set -o errexit,它的作用與set -e相同。
"set -e"命令用法總結:
1. 當命令的返回值為非零狀態時,則立即退出腳本的執行。
2. 作用范圍僅僅只限於腳本執行的當前進行,不作用於其創建的子進程!比如在當前終端下設置"set -e", 但是關閉該窗口,另開一個終端窗口,之前設置的"set -e"功能就失效了!
3. 另外,當想根據命令執行的返回值,輸出對應的log時,最好不要采用set -e選項,而是通過配合exit 命令來達到輸出log並退出執行的目的。
示例如下:
執行"set -e"之后,在接下來執行的命令中,如果命令的返回值不為0,那么會使所在的進程或shell退出。
示例1:
查看haha.txt文件,由於該文件不存在,之后命令后的返回值不為0
由於沒有提前執行"set -e",則命令執行后不會退出該進程
[root@ss-server ~]# cat haha.txt
cat: haha.txt: No such file or directory
[root@ss-server ~]# echo $?
1
[root@ss-server ~]#
現在執行"set -e"
[root@ss-server ~]# set -e
[root@ss-server ~]# cat haha.txt
cat: haha.txt: No such file or directory #由於在之前添加了"set -e",則命令返回值為非零時就自動退出。
如果將"set -e"換成"set +e",則命令執行后返回值為非零,將不會自動退出shell,會繼續執行。
[root@ss-server ~]# set +e
[root@ss-server ~]# cat haha.txt
cat: haha.txt: No such file or directory
[root@ss-server ~]# echo $?
1
[root@ss-server ~]#
為了增加可讀性,可以使用"set -o errexit"命令來替換"set -e",兩者效果一樣!
[root@ss-server ~]# set -o errexit
[root@ss-server ~]# cat haha.txt
cat: haha.txt: No such file or directory #執行命令的返回值為非零,直接退出當前shell。
示例2:
[root@ss-server ~]# cat hehe.txt;hostname
cat: hehe.txt: No such file or directory
ss-server
[root@ss-server ~]# set -e
[root@ss-server ~]# cat hehe.txt;hostname
cat: hehe.txt: No such file or directory #執行命令的返回值為非零,直接退出當前shell。
[root@ss-server ~]# set -o errexit
[root@ss-server ~]# cat hehe.txt;hostname
cat: hehe.txt: No such file or directory #執行命令的返回值為非零,直接退出當前shell。
############### 需要注意 ###############
1. "set -e"參數 和 "set -o errexit" 兩者等同!上面已有示例說明。
2. 還有一種方法是使用command || true,使得該命令即使執行失敗,腳本也不會終止執行。這個需要特別注意下!
如下,雖然設置了"set -e"參數,並且"cat hehe.txt"執行命令的返回值為非零。但是由於使用了||符號,表示前面命令執行結果為false后,繼續執行后面的命令。
那么只要這個||后面的命令執行結果為true,則整個命令執行結果返回值就是0,所以即使設置了"set -e",也沒有退出當前shell!
示例如下:
[root@ss-server ~]# set -e
[root@ss-server ~]# cat hehe.txt || hostname
cat: hehe.txt: No such file or directory
ss-server
[root@ss-server ~]# echo $?
0
[root@ss-server ~]# set -e
[root@ss-server ~]# cat hehe.txt || cat haha.txt || hostname
cat: hehe.txt: No such file or directory
cat: haha.txt: No such file or directory
ss-server
如果||最后面的那條命令執行結果為false,則整個命令執行結果返回值就是非零,則就會退出當前shell
[root@ss-server ~]# set -e
[root@ss-server ~]# cat hehe.txt || cat haha.txt
cat: hehe.txt: No such file or directory
cat: haha.txt: No such file or directory
3. 還要注意一種例外情況,就是set -e不適用於管道命令。
所謂管道命令,就是多個子命令通過管道運算符(|)組合成為一個大的命令。Bash 會把最后一個子命令的返回值,作為整個命令的返回值。
也就是說,只要最后一個子命令不失敗,管道命令總是會執行成功,因此它后面命令依然會執行,set -e就失效了。
示例如下:
[root@ss-server ~]# cat test.sh
#!/bin/bash
set -e
cat haha_yu | echo "hello world"
hostname
執行結果:
[root@ss-server ~]# sh test.sh
hello world
cat: haha_yu: No such file or directory
ss-server
上面腳本中,雖然haha文件不存在,但是cat haha_yu | echo "hello world"它是一個整體命令,只有|后面的命令執行成功,則整個命令的返回值就是0
所以后面的"hostname"命令也會繼續執行。
2)set -o pipefail 參數
========================================================================================
針對上面set -e不適用於管道命令的例子,set -o pipefail 用來解決這種情況,只要一個子命令失敗,整個管道命令就失敗,腳本就會終止執行。
set -o pipefail表示在管道連接的命令序列中,只要有任何一個命令返回非0值,則"整個管道命令"返回非0值,即使最后一個命令返回0!!
示例如下:
[root@ss-server ~]# cat test.sh
#!/bin/bash
set -eo pipefail
cat haha_yu | echo "hello world"
hostname
執行結果如下:
[root@ss-server ~]# sh test.sh
hello world
cat: haha_yu: No such file or directory
示例2:
|管道命令,只要最后一個命令執行成功,則整個管道命令執行結果返回值就是0,即整個管道命令執行就算成功!
[root@ss-server ~]# cat haha_yu | echo "hello world"
hello world
cat: haha_yu: No such file or directory
[root@ss-server ~]# echo $?
0
[root@ss-server ~]# cat haha_yu | echo "hello world" >/dev/null 2>&1
cat: haha_yu: No such file or directory
[root@ss-server ~]# echo $?
0
設置了"set -o pipefail"之后,只要有任何一個命令返回非0值,則"整個管道命令"返回非0值,即使最后一個命令返回0!
[root@ss-server ~]# set -o pipefail
[root@ss-server ~]# cat haha_yu | echo "hello world" >/dev/null 2>&1
cat: haha_yu: No such file or directory
[root@ss-server ~]# echo $?
1
3)set -u 參數
========================================================================================
該參數表示在腳本執行中,如遇到不存在的變量就會報錯,並停止執行!
示例如下:
[root@ss-server ~]# cat test.sh
#!/bin/bash
echo ${a}
echo `hostname`
上面腳本中,${a}是一個不存在的變量。執行結果如下:
[root@ss-server ~]# sh test.sh
ss-server
可以看到,echo ${a}輸出了一個空行,說明Bash忽略了不存在的${a}變量,然后繼續執行echo `hostname`。
大多數情況下,這不是我們想要的行為,遇到變量不存在,腳本應該報錯,而不是一聲不響地往下執行。
那么"set -u"參數就用來改變這種行為。腳本在頭部加上它,遇到不存在的變量就會報錯,並停止執行。
[root@ss-server ~]# cat test.sh
#!/bin/bash
set -u
echo ${a}
echo `hostname`
執行結果如下:
[root@ss-server ~]# sh test.sh
test.sh: line 3: a: unbound variable
從上面可以看到,添加"set -u"參數后,腳本執行中發現不存在${a}變量就報錯了(報錯"未綁定的變量"),並且不再執行后面的語句。
需要注意:
"set -u" 還有另一種寫法"set -o nounset",兩者是等價的!!!
[root@ss-server ~]# cat test.sh
#!/bin/bash
set -o nounset
echo ${a}
echo `hostname`
[root@ss-server ~]# sh test.sh
test.sh: line 3: a: unbound variable
4)set -x 參數
========================================================================================
默認情況下,shell腳本執行后,屏幕只顯示運行結果,沒有其他內容。
如果多個命令連續執行,它們的運行結果就會連續輸出。有時會分不清,某一段內容是什么命令產生的。
"set -x"參數用來在運行結果之前,先輸出執行的那一行命令。即輸出腳本執行的詳細過程!
示例如下:
[root@ss-server ~]# cat test.sh
#!/bin/bash
DATE=$(date +%Y%m%d)
echo "$(hostname) and ${DATE}"
執行結果如下,只輸出運行結果,沒有輸出詳細的執行命令
[root@ss-server ~]# sh test.sh
ss-server and 20191218
現在加上"set -x"參數
[root@ss-server ~]# cat test.sh
#!/bin/bash
set -x
DATE=$(date +%Y%m%d)
echo "$(hostname) and ${DATE}"
執行結果如下:
[root@ss-server ~]# sh test.sh
++ date +%Y%m%d
+ DATE=20191218
++ hostname
+ echo 'ss-server and 20191218'
ss-server and 20191218
需要注意:
"set -x"參數還有另一種寫法"set -o xtrace",兩者是等價的!!!
[root@ss-server ~]# cat test.sh
#!/bin/bash
set -o xtrace
DATE=$(date +%Y%m%d)
echo "$(hostname) and ${DATE}"
執行結果如下:
[root@ss-server ~]# sh test.sh
++ date +%Y%m%d
+ DATE=20191218
++ hostname
+ echo 'ss-server and 20191218'
ss-server and 20191218
其實,上面"set -x" 和 "set -o xtrace" 作用就是打印腳本執行的詳細過程,這和"sh -x xxx.sh"的效果也是一樣的!
[root@ss-server ~]# cat test.sh
#!/bin/bash
DATE=$(date +%Y%m%d)
echo "$(hostname) and ${DATE}"
如上,腳本中沒有添加"set -x" 或 "set -o xtrace", 在執行腳本時使用"sh -x"也可以打印腳本執行過程。
[root@ss-server ~]# sh -x test.sh
++ date +%Y%m%d
+ DATE=20191218
++ hostname
+ echo 'ss-server and 20191218'
ss-server and 20191218
5)shell腳本的錯誤處理
========================================================================================
如果腳本里面有運行失敗的命令(返回值非0),Bash 默認會繼續執行后面的命令。
[root@ss-server ~]# cat test.sh
#!/bin/bash
cat haha_yu
echo `hostname`
上面腳本中,"cat haha_yu"是一個執行會報錯的命令,因為haha_yu文件不存在。
但是,Bash 會忽略這個錯誤,繼續往下執行。
[root@ss-server ~]# sh test.sh
cat: haha_yu: No such file or directory
ss-server
可以看到,上面腳本中Bash 只是顯示有錯誤,並沒有終止執行。
這種行為很不利於腳本安全和除錯。實際開發中,如果某個命令失敗,往往需要腳本停止執行,防止錯誤累積。這時,一般采用下面的寫法。
"command || exit 1"
上面的寫法表示只要command有非零返回值,腳本就會停止執行。
如果停止執行之前需要完成多個操作,就要采用下面三種寫法。
# 寫法一
command || { echo "command failed"; exit 1; }
# 寫法二
if ! command; then echo "command failed"; exit 1; fi
# 寫法三
command
if [ "$?" -ne 0 ]; then echo "command failed"; exit 1; fi
另外,除了停止執行,還有一種情況:
如果兩個命令有繼承關系,只有第一個命令成功了,才能繼續執行第二個命令,那么就要采用下面的寫法。
command1 && command2
至於,;、||、&& 這三者的使用區別,在上面已經詳細介紹過了,這里就不贅述了。
示例1
[root@ss-server ~]# cat test.sh
#!/bin/bash
cat haha_yu || exit 1
echo `hostname`
執行結果如下:
[root@ss-server ~]# sh test.sh
cat: haha_yu: No such file or directory
示例2
[root@ss-server ~]# cat test.sh
#!/bin/bash
cat haha_yu || { echo "執行失敗";exit 1;}
echo `hostname`
執行結果如下:
[root@ss-server ~]# sh test.sh
cat: haha_yu: No such file or directory
執行失敗
示例3
[root@ss-server ~]# cat test.sh
#!/bin/bash
if ! cat haha_yu;then
echo "執行失敗"
exit 1
fi
echo `hostname`
執行結果如下:
[root@ss-server ~]# sh test.sh
cat: haha_yu: No such file or directory
執行失敗
示例4
[root@ss-server ~]# cat test.sh
#!/bin/bash
cat haha_yu
if [ $? -ne 0 ];then
echo "執行失敗"
exit 1
fi
echo `hostname`
執行結果如下:
[root@ss-server ~]# sh test.sh
cat: haha_yu: No such file or directory
執行失敗
6)set命令總結
========================================================================================
set命令的上面這四個參數,一般都放在一起在shell腳本中使用。
# 寫法一
set -euxo pipefail
# 寫法二
set -eux
set -o pipefail
以上這兩種寫法建議放在所有 Bash 腳本的頭部。
還有另一種辦法:在執行Bash腳本的時候,從命令行傳入這些參數。
# bash -euxo pipefail script.sh
