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