Shell脚本字符串匹配及日常命令工具 您所在的位置:网站首页 bash执行字符串 Shell脚本字符串匹配及日常命令工具

Shell脚本字符串匹配及日常命令工具

2023-08-28 02:17| 来源: 网络整理| 查看: 265

 

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 -------------------------------------------------------------------------------------- > [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 /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表示不换行 规律: 内层循环的变量 /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 相似 [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][-e][-o] [-t][-v][-1][-2][--help] [--version][文件1][文件2] 补充说明:找出两个文件中,指定栏位内容相同的行,并加以合并,再输出到标准输出设备。 参数: -a 除了显示原来的输出内容之外,还显示指令文件中没有相同栏位的行。 -e 若[文件1]与[文件2]中找不到指定的栏位,则在输出中填入选项中的字符串。 -i 比较栏位内容时,忽略大小写的差异。 -o 按照指定的格式来显示结果。 -t 使用栏位的分隔字符。 -v 跟-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, 哈哈,北京:\n" "A\nB" welcome, 哈哈,北京: [root@ss-server ~]# printf "welcome, 哈哈,北京::\n" "A\nB" welcome, 哈哈,北京:: [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 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 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 &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


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有