zoukankan      html  css  js  c++  java
  • Shell脚本字符串匹配及日常命令工具

    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}
    ok"
    else
    echo "invaliad"
    fi
     
    需要注意:
    脚本中的echo后面只需要添加-e参数,是为了让打印中的
    换行符生效!如果不加-e参数,则
    就被做当普通字符打印出来了!
    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 
     213"| tr " " "#"
    root#
    #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'都属于集合,集合里可以使用'
    '、'	',可以可以使用其他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 '
    ' '+') 0 ]
    45
    [root@kevin ~]# echo "10 11 12 13 14"|xargs -n1|echo $[ $(tr '
    ' '+') 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中出现的换行'
    '、制表'	'字符 
    [root@kevin ~]# cat filename | tr -d "
    	"
    
    删除换行符等
    [root@kevin ~]# echo -e "asdf
    123"|tr -d "
    "
    asdf123
    [root@kevin ~]# echo -e "asdf
    
    	123"|tr -d "
    
    	"
    asdf123
    
    7)删除空行
    [root@kevin ~]# cat file | tr -s "
    " > new_file
    
    [root@kevin ~]# cat test
    beijing
    shanghai
    
    abcde
    
    
    123123
    [root@kevin ~]# cat test| tr -s "
    "    
    beijing
    shanghai
    abcde
    123123
    
    8)把路径变量中的冒号":",替换成换行符"
    "
    [root@kevin ~]# echo "a:b:c:d"|tr ":" "
    "           
    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 
    '
     123  
    [root@kevin ~]# echo "wang beijinganhui"|tr -d -c 'a-m
    '
    agbeijigahi
    [root@kevin ~]# echo "123WRsdf"|tr -d -c "a-z
    "
    sdf
    [root@kevin ~]# echo "123@#QWEanhui"|tr -d -c "anhui
    " 
    anhui
    
    10) 删除Windows文件"造成"的'^M'字符
    [root@kevin ~]# cat file | tr -s "
    " "
    " > new_file
    或
    [root@kevin ~]# cat file | tr -d "
    " > 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 "
    ${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 "
    fine ,continue";;
    N | n)
          echo -e "
    ok,good bye";;
    *)
         echo -e "
    error 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语句中添加换行符"
    ",否则执行脚本,在输入内容后会将echo的内容和read提示信息放在一行!
    echo需要加上参数"-e"才能将后面引号内的特殊字符"
    "生效!否则就当普通字符处理了!
    
    如果输入的字符超过了设定的最大字符长度,则就默认使用前面最大长度值的字符!
    [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 "
    需要输入的名字是:${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 "
    输入的名字长度超过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 "
    your 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 "
        33[31m A 显示主机ip 33[0m
        33[32m B 显示磁盘剩余空间 33[0m
        33[33m C 显示系统运行时间 33[0m
        33[34m Q 退出系统 33[0m
                "
    read -p "请输入你的选择:" char
    case ${char} in
    a|A)
        echo -e "33[31m `ifconfig eth0 | grep "netmask" | awk '{print $2}'` 33[0m"
        ;;
    b|B)
        echo -e "33[32m `df -h | awk 'NR==2{print "剩余空间大小为:"$4}'` 33[0m"
        ;;
    c|C)
        echo -e "33[33m `uptime | awk '{print "系统已经运行了"$3""$4""}'` 33[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/^[ 	]*//g' filename        #不加-i参数,表示仅仅在当前终端显示命令执行效果。
    # sed -i 's/^[ 	]*//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/^[ 	]*//g' test   
    aa 
    bb
    123  aa
    sadf
    12313 yy
    45
    [root@ss-server ~]# cat test|sed -i 's/^[ 	]*//g' test
    [root@ss-server ~]# cat test
    aa 
    bb
    123  aa
    sadf
    12313 yy
    45
    
    3. 删除文件中行尾空格的命令
    # sed 's/[ 	]*$//g' filename              #仅仅在当前终端显示执行效果。
    # sed -i 's/[ 	]*$//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去重,teesplit

    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 自动添加换行符,但是可以手动添加 
    。
    
    printf 命令的语法:
    printf format-string [arguments...]
    
    参数说明:
    format-string: 为格式控制字符串
    arguments: 为参数列表。
    
    需要注意:
    1. printf 不像 echo 那样会自动换行,必须显式添加换行符(
    )。
    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
    "
    Hello, Shell
    [root@ss-server ~]# 
    
    [root@ss-server ~]# printf "%-10s %-8s %-4s
    " 姓名 性别 体重kg  
    姓名     性别   体重kg
    [root@ss-server ~]# printf "%-10s %-8s %-4.2f
    " 小明 男 65.1234
    小明     男      65.12
    [root@ss-server ~]# printf "%-10s %-8s %-4.2f
    " 小洋 男 72.6589 
    小洋     男      72.66
    [root@ss-server ~]# printf "%-10s %-8s %-4.2f
    " 小梅 女 48.7167 
    小梅     女      48.72
    
    需要注意:
    %d指的是针对数字的格式化
    %s指的是字符串的格式化
    %-10s 指一个宽度为10个字符(-表示左对齐,没有则表示右对齐),任何字符都会被显示在10个字符宽的字符内,如果不足则自动以空格填充,超过也会将内容全部显示出来。
    %-4.2f 指格式化为小数,其中.2指保留2位小数。
    
    示例如下:
    format-string为双引号(换行)
    [root@ss-server ~]# printf "%d %s
    " 1 "abc"
    1 abc
    
    单引号与双引号效果一样(换行)
    [root@ss-server ~]# printf '%d %s
    ' 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
    " abc def
    abc
    def
    
    [root@ss-server ~]# printf "%s %s %s
    " 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 
    "
     and 0 
    
    如果以 %d 的格式来显示字符串,那么会有警告,提示无效的数字,此时默认置为 0
    [root@ss-server ~]# printf "The first program always prints'%s,%d
    '" Hello Shell
    -bash: printf: Shell: invalid number
    The first program always prints'Hello,0
    
    
    printf的转义序列
    =================================================================================
    a     警告字符,通常为ASCII的BEL字符
         后退
    c     抑制(不显示)输出结果中任何结尾的换行字符(只在%b格式指示符控制下的参数字符串中有效),而且,任何留在参数里的字符、任何接下来的参数以及任何留在格式字符串中的字符,都被忽略
    f     换页(formfeed)
    
         换行
    
         回车(Carriage return)
    	     水平制表符
    v     垂直制表符
    \     一个字面上的反斜杠字符
    ddd  表示1到3位数八进制值的字符。仅在格式字符串中有效
    ddd 表示1到3位的八进制值字符
    
    示例如下:
    [root@ss-server ~]# printf "welcome, 哈哈,北京:<%s>
    " "A
    B"
    welcome, 哈哈,北京:<A
    B>
    
    [root@ss-server ~]# printf "welcome, 哈哈,北京::<%b>
    " "A
    B"
    welcome, 哈哈,北京::<A
    B>
    
    [root@ss-server ~]# printf "www.kevin.com a"      
    www.kevin.com [root@ss-server ~]# 

    24)zcatzgrep命令用法
    服务器端常有很多压缩过的日志文件,当需要查找日志中某些特定信息的时候,为了避免解压文件,可以使用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
    " | sort > file1_diff.txt
    [root@ss-server opt]# find /opt/test2 -printf "%P
    " | sort | sort | diff file1_diff.txt -
    4a5
    > haha/bo.txt
    
    说明:
    < 代表的是第一个目录/opt/test1中有,而第二个目录/opt/test2中没有的文件
    > 则相反,代表的是第二个目录/opt/test2中有而第一个目录/opt/test1中没有。
    
    不要省略-printf "%P
    ",此处的%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
    "| sort) <(find /opt/test2 -printf "%P
    "| 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
  • 相关阅读:
    PHP实现微信小程序人脸识别刷脸登录功能
    thinkphp3.2.3中设置路由,优化url
    ThinkPHP URL 路由简介
    在 Linux 下搭建 Git 服务器
    图解 Android 广播机制 狼人:
    手机系统竞争背后的利益竞逐 狼人:
    你必须知道的Windows Phone 7开发 狼人:
    展望Android之前世今生 狼人:
    在美做开发多年,写给国内iPhone开发新手 狼人:
    NDK入门项目实战 狼人:
  • 原文地址:https://www.cnblogs.com/kevingrace/p/5996133.html
Copyright © 2011-2022 走看看