zoukankan      html  css  js  c++  java
  • shell编程

    一、正则表达式

    1. 基础正则表达式

    元字符                作用
    
    *                 前一个字符匹配0次或任意多次
    
    .                 匹配除了换行符外任意一个字符
    
    ^                 匹配行首。例:^hello会匹配以hello开头的行
    
    $                 匹配行尾。例:hello$会匹配以hello结尾的行
      
    []               匹配中括号内的任意一个字符,只匹配一个字符。例:[a-z]匹配任意一个小写字母,[0-9]匹配任意一位数字,
               [a-z][0-9]匹配小写字母和一位数字构成的两位字符 [^] 匹配除中括号内的字符以外的任意一个字符。例:[^0-9]匹配任意一位非数字字符,[^a-z]表示任意一位非小写字母。    转义符。 {n} 表示其前面的字符恰好出现n次。例:[0-9]{4}匹配4位数字,[1][3-8][0-9]{9}匹配手机号 {n,} 表示其前面的字符出现次数不小于n次。例:[0-9]{2,}表示两位及以上的数字 {n,m} 表示其前面的字符至少出现n次,最多出现m次。例:[a-z]{6,8}匹配6到8位的小写字母

     在/etc/profile.d/*.sh文件中建立别名

    [root@centos2 ~]# vim /etc/profile.d/custom.sh 
    
    alias sudo='sudo '
    
    alias egrep='egrep --color=auto'
    alias fgrep='fgrep --color=auto'
    alias grep='grep --color=auto'
    alias ls='ls --color=auto'
    alias l.='ls -d .* --color=auto'
    alias ll='ls -l --color=auto'
    alias l='ls -laF'
    
    alias cp='cp -i'
    alias mv='mv -i'
    alias rm='rm -i'
    
    alias now='date +%Y%m%d%H%M%S'

    2.1 建立练习文件

    [root@centos2 ~]# vim test.txt
    
    aaaaaaaaaaa
    bbbbbbbbbb
    ccccccccc
    dddddd
    123abc
    hello world
    faiz
    faaaiiiz kabuto
    555 build wwwww joker HELLO WORLD zio

    2.2 "*"前一个字符匹配0次,或任意多次

    [root@centos2 ~]# grep "a*" test.txt 
    aaaaaaaaaaa
    bbbbbbbbbb
    ccccccccc
    dddddd
    123abc
    hello world
    faiz
    faaaiiiz kabuto
    555 build wwwww joker HELLO WORLD zio

      如果正则表达式写成"aa*"代表这行字符串一定要有一个a,但是后面有没有a都可以。也就是说会匹配至少含有一个a的行

    [root@centos2 ~]# grep "aa*" test.txt 
    aaaaaaaaaaa
    123abc
    faiz
    kabuto

       如果正则表达式是"aaa*",则会匹配至少包含两个连续a的字符串

    [root@centos2 ~]# grep "aaa*" test.txt 
    aaaaaaaaaaa
    [root@centos2 ~]# grep "555*" test.txt 
    555
    [root@centos2 ~]# grep "5555*" test.txt 
    555
    [root@centos2 ~]# grep "55555*" test.txt 
    [root@centos2 ~]# 
    #由于此文件中并没有4个连续的5的存在,故匹配不到

     2.3 "."匹配除了换行符外任意一个字符

      正则表达式"."只能匹配一个字符,这个字符可以是任意字符

    [root@centos2 ~]# grep "f..z" test.txt 
    faiz
    #“f..z”会匹配在f和z这两个字母之间一定有两个字符的单词
    
    
    [root@centos2 ~]# grep "f.*z" test.txt 
    faiz
    faaaiiiz
    [root@centos2 ~]# grep ".*" test.txt 
    aaaaaaaaaaa
    bbbbbbbbbb
    ccccccccc
    dddddd
    123abc
    hello world
    faiz
    faaaiiiz
    kabuto
    555
    build
    wwwww
    joker
    HELLO WORLD
    zio

    2.4 "^"匹配行首,"$"匹配行尾

      "^"匹配行首,如"^f"会匹配以小写"f"开头的行

    [root@centos2 ~]# grep "^f" test.txt 
    faiz
    faaaiiiz

      "$"匹配行尾,如"d$"会匹配以小写"d"结尾的行

    [root@centos2 ~]# grep "d$" test.txt 
    dddddd
    hello world
    build

      而"^$"会匹配空白行

    [root@centos2 ~]# grep -n "^$" test.txt 

    2.5 "[]"匹配中括号中指定的任意一个字符,只匹配一个字符

      "[]"会匹配中括号中指定的任意一个字符,注意只能匹配一个字符。比如[ab]不是匹配一个a字符,就是匹配一个b字符

    [root@centos2 ~]# grep "f[az]iz" test.txt 
    faiz

      "[0-9]"会匹配任意一个数字

    [root@centos2 ~]# grep "[0-9]" test.txt 
    123abc
    555

      "[A-Z]"会匹配一个大写字母

    [root@centos2 ~]# grep "[A-Z]" test.txt 
    HELLO WORLD

      "^[a-z]"则会匹配以小写字母开头的行

    [root@centos2 ~]# grep "^[a-z]" test.txt 
    aaaaaaaaaaa
    bbbbbbbbbb
    ccccccccc
    dddddd
    hello world
    faiz
    faaaiiiz
    kabuto
    build
    wwwww
    joker
    zio

    2.6 "[^]"匹配中括号中的字符以外的任意一个字符

    [root@centos2 ~]# grep "^[^a-z]" test.txt 
    123abc
    555
    HELLO WORLD

      "^[^a-zA-Z]"会匹配不以字母开头的行

    [root@centos2 ~]# grep "^[^a-zA-Z]" test.txt 
    123abc
    555

    2.7 ""转义符

    [root@centos2 ~]# grep ".$" test.txt
    #匹配以"."结尾的行

    2.8 "{n}"表示其前面的字符恰好出现n次

    [root@centos2 ~]# grep "a{3}" test.txt 
    aaaaaaaaaaa
    faaaiiiz
    #第一行包含有连续的三个a,故会被匹配。

      如果想要只显示三个连续的a,

    [root@centos2 ~]# grep "fa{3}" test.txt 
    faaaiiiz

      如果正则是"[0-9]{3}"会匹配包含连续是三个数字的行

    [root@centos2 ~]# grep "[0-9]{3}" test.txt 
    123abc
    555

    2.9 "{n,}"表示其前面的字符出现不小于n次

      "{n,}"会匹配前面的字符出现最少n次。比如"fa{3,}z"这个正则就会匹配以f开头,z结尾,中间最少有三个a的字符串,"^[0-9]{3,}[a-z]"这个正则会匹配最少有连续三个数字开头的字符串

    oot@centos2 ~]# grep "^[0-9]{3,}[a-z]" test.txt 
    123abc

    2.10 "{n,m}"匹配其前面的字符至少出现n次,最多出现m次

    [root@centos2 ~]# grep "f[a-z]{1,3}" test.txt 
    faiz
    faaaiiiz
    [root@centos2 ~]# grep "f[a-z]{1,3}z" test.txt 
    faiz
    
    [root@centos2 ~]# grep "f[a-z]{2,3}z" test.txt 
    faiz
    [root@centos2 ~]# grep "f[a-z]{2,3}" test.txt 
    faiz
    faaaiiiz
    
    [root@centos2 ~]# grep "f[a-z]{3,6}z" test.txt 
    faaaiiiz

    3. 扩展正则表达式

      在正则表达式中还可以支持一些元字符,比如"+","?","|","()"。但是grep命令默认不支持。如果想要支持这些元字符,必须使用egrep命令或grep -E选项。故这些元字符被称为扩展元字符。

    扩展元字符                    作用
       +                前一个字符匹配1次或任意多次
                        如"go+gle"会匹配"gogle""google""gooogle",当前如果"o"有更多个,也能匹配
       ?               前一个字符匹配0次或1次
                        如"colou?r"可以匹配"colour""color"
       |                匹配两个或多个分支选择
                        如"was|his" 会匹配既包含"was"的行,也匹配包含"his"的行
       ()               匹配其整体为一个字符,即模式单元。可以理解为由多个单个字符组成的大字符
                如"(dog)+"会匹配“dog”、“dogdog”、“dogdogdog”等,因为被()包含的字符会被当成一个整体,但"hello (world|earth)"会匹配到"hello world""hello earth"

    二、字符截取和替换命令

    1. cut列截取命令

    [root@centos2 ~]# cut [选项] 文件名
    选项
        -f 列号            提取第几列
        -d 分隔符        按照指定分隔符分割列
        -c 字符范围      不依赖分隔符来区分列,而是通过字符范围(行首为0)来进行字段提取。"n-"表示从第n个字符到行尾;"n-m"从第n个字符到第m个字符;"-m"表示从第一个字符到第m个字符

       cut命令默认分隔符是制表符,就是"tab"键,但是对空格符的支持不好。

    [root@centos2 ~/sh]# vim student.txt
    ID      NAME     gender Mark
    1       zs      M       88
    2       ls      M       92
    3       ww      M       83
    [root@centos2 ~/sh]# cut -f 2 student.txt 
    NAME
    zs
    ls
    ww
    #提取第二列内容

      想要提取多列,只需列号之间使用","隔开即可

    [root@centos2 ~/sh]# cut -f 2,3 student.txt 
    NAME     gender
    zs    M
    ls    M
    ww    M

      cut也可以按照字符提取,需要注意"8-"代表的是提取所有行的第八个字符开始到行尾,而"10-20"代表提取所有行的第十个字符到第二十个字符,而"-8"代表提取所有行从行首到第八个字符

    [root@centos2 ~/sh]# cut -c 8- student.txt 
         gender    Mark
    88
    92
    83
    #提取第八个字符开始到行尾
    [root@centos2 ~/sh]# cut -d ":" -f 1,3 /etc/passwd
    root:0
    ...
    #以“:”作为分隔符,提取/etc/passwd文件的第一列和第三列
    [root@centos2 ~/sh]# df -h | cut -d " " -f 1,3
    文件系统 
    devtmpfs 
    tmpfs 
    tmpfs 
    tmpfs 
    /dev/mapper/centos-root 
    /dev/sda1 
    tmpfs 
    tmpfs 
    /dev/sr0 
    
    #cut对空格符的支持不好

    2. awk

    2.1 printf格式化输出

    [root@centos2 ~/sh]# printf '输出类型输出格式' 输出内容
    输出类型
        %ns        输出字符串。n是数字指代输出几个字符
        %ni        输出整数。n是数字指代输出几个数字
        %m.nf     输出浮点数。m和n是数字,指代输出的整数位数和小数位数。如%8.2f代表共输出8位数,其中2位小数,6位整数
    
    输出格式
        a        输出警告音
                输出退格键,就是Backspace键
        f        清除屏幕
        
            换行
        
            回车,就是enter键
        	        水平输出退格键,就是tab键
        v        垂直输出退格键,就是tab键

    修改student.txt文件

    [root@centos2 ~/sh]# vim student.txt 
      
    ID      NAME    PHP     LINUX   MYSQL   AVERAGE
    1       zs      80      90      70      80
    2       ls      75      85      86      82
    3       ww      80      90      85      85

    使用printf命令输出这个文件的内容

    [root@centos2 ~/sh]# printf '%s' $(cat student.txt)
    IDNAMEPHPLINUXMYSQLAVERAGE1zs809070802ls758586823ww80908585[root@centos2 ~/sh]#

    printf命令如果不指定输出格式,就会把所有输出内容连在一起输出。其实文本的输出本身就是这样的,cat等文本输出命令之所以可以按照格式输出,是因为cat命令已经设定了输出格式。为了printf输出合理的格式,

    [root@centos2 ~/sh]# printf '%s	 %s	 %s	 %s	 %s	 %s	 
    ' $(cat student.txt)
    #注意printf命令的单引号中,只能识别格式输出符号,手动输入的格式是无效的
    ID     NAME     PHP     LINUX     MYSQL     AVERAGE     
    1     zs     80     90     70     80     
    2     ls     75     85     86     82     
    3     ww     80     90     85     85    

    如果不把成绩当成字符串输出,而是按照整形和浮点型输出

    [root@centos2 ~/sh]# printf '%i	 %s	 %i	 %i	 %i	 %8.2f	 
    ' $(cat student.txt | grep -v NAME) 
    
    1     zs     80     90     70        80.00     
    2     ls     75     85     86        82.00     
    3     ww     80     90     85        85.00     

    2.2 awk基本使用

    [root@centos2 ~/sh]# awk '条件1{动作1} 条件2{动作2}...' 文件名
    条件(Pattern)
        一般使用关系表达式作为条件。例
        x > 10    判断变量x是否大于10
        x == y    判断变量x是否等于变量y
        A ~ B    判断字符串A中是否包含能匹配B表达式的子字符串
        A !~ B    判断字符串A中是否不包含能匹配B表达式的子字符串
    动作(Action)
        格式化输出
        流程控制语句
    [root@centos2 ~/sh]# awk '{printf $2 "	" $6 "
    "}' student.txt 
    NAME    AVERAGE
    zs    80
    ls    82
    ww    85
    #输出第二列和第六列

    截取df命令的结果

    [root@centos2 ~/sh]# df -h | awk '{print $1 "	" $3}'
    文件系统    已用
    devtmpfs    0
    tmpfs    0
    tmpfs    11M
    tmpfs    0
    /dev/mapper/centos-root    7.5G
    /dev/sda1    237M
    tmpfs    0
    tmpfs    44K
    /dev/sr0    11G
    
    
    [root@centos2 ~/sh]# du -sh student.txt | awk '{print $1 }'
    4.0K
    [root@centos2 ~/sh]# du -sh student.txt | awk '{print $1 }' | cut -d 'K' -f 1
    4.0

    2.3 awk的条件

    条件类型 条件 说明
    awk保留字 BEGIN 在awk程序一开始时,尚未读取任何数据之前执行。BEGIN后的动作只在程序开始时执行一次
    END 在awk程序处理完所有数据,即将结束时执行。END后的动作只在程序结束时执行一次
    关系运算符 > 大于
    < 小于
    >= 大于等于
    <= 小于等于
    == 等于。用于判断两个值是否相等,如果给变量赋值,只有"="号
    != 不等于
    A~B 判断字符串A中是否包含能匹配B表达式的子字符串
    A!~B 判断字符串A中是否不包含能匹配B表达式的子字符串
    正则表达式 /正则/ 如果在"//"中可以写入字符,也可以支持正则表达式

    2.3.1 BEGIN

      BEGIN是awk的保留字,是一种特殊的条件类型。BEGIN的执行时机是在"awk程序一开始时,尚未读取任何数据之前执行"。一旦BEGIN后的动作执行一次,当awk开始从文件中读入数据,BEGIN的条件就不再成立,故BEGIN定义的动作只能被执行一次。例

    [root@centos2 ~/sh]# awk 'BEGIN{printf "this is a transcript 
    "} {printf $2 "	" $6 "
    "}' student.txt 
    #awk命令只要检测不到完整的单引号不会执行
    #这里定义了两个动作
    #第一个动作使用BEGIN条件,所有会在读入文件数据前打印"this is a transcript"(之后执行一次)
    #第二个动作会打印文件的第二列和第六列
    this is a transcript NAME AVERAGE zs
    80 ls 82 ww 85

    2.3.2 END

      END也是awk的保留字,只不过和BEGIN刚好相反。END是在awk程序处理完所有数据,即将结束时执行。END后的动作只在程序结束时执行一次。例

    [root@centos2 ~/sh]# awk 'END{printf "the end 
    "} {printf $2 "	" $6 "
    "}' student.txt 
    #在输出结尾输入“the end”,这并不是文档本身的内容,而且只会执行一次
    NAME    AVERAGE
    zs    80
    ls    82
    ww    85
    the end 

    2.3.3 关系运算符

      例

    [root@centos2 ~/sh]# cat student.txt | grep -v NAME 
    > | awk '$6 >= 85 {printf $2 "
    "}'
    #使用cat输出文件内容,用grep取反包含“NAME”的行
    #判断第六列(平均成绩)大于等于85分的行,如果条件成立,则打印第六列
    
    ww

      加入了条件之后,只有条件成立动作才会执行,如果条件不满足,则动作不会执行。虽然awk是列提取命令,但是也要按行来读入。这个命令的执行过程如下:

      1)如果有BEGIN条件,则会执行BEGIN定义的动作

      2)如果没有BEGIN条件,则读入第一行,把第一行的数据一次赋予$0、$1、$2等变量。其中$0代表此行的整体数据,$1代表第一字段,$2代表第二字段

      3)依据条件类型判断动作是否执行。如果条件符合,则执行动作,否则读入下一行数据。如果没有条件,则每行都会执行动作

      4)读入下一行数据,重复执行一行步骤

      例,查看zs用户的平均成绩

    [root@centos2 ~/sh]# awk '$2 ~ /zs/ {printf $6 "
    "}' student.txt 
    #如果第二字段中输入包含有"zs"字符,则打印第六列数据
    80

      这里要注意在awk中,使用"//"包含的字符串,awk命令才会查找。也就是说字符串必须用"//"包含,awk才能正确识别。

    2.3.4 正则表达式

      如果想让awk识别字符串,必须使用"//"包含。例

    [root@centos2 ~/sh]# awk '/ls/ {print}' student.txt 
    #打印ls的成绩
    2    ls    75    85    86    82

      当使用df命令查看分区使用情况时,如果我只想查看真正的系统分区的使用状况,而不想看光盘和临时分区的使用状况,可以

    [root@centos2 ~/sh]# df -h |awk '/sda[0-9]/ {print $1 "	" $5 "
    "}'
    #查询包含sda数字的行,并打印第一第五列
    
    /dev/sda1    24%

    2.4 awk内置变量

    awk内置变量 作用
    $0 代表目前awk所读入的整行数据。已知awk是一行一行读入数据的,$0就代表当前读入行的整行数据
    $n 代表目前读入行的第n个字段
    NF 当前行拥有的字段(列)总数
    NR 当前awk所处理的行,是总数据的第几行
    FS 用户定义分隔符。awk默认的分隔符是任何空格,如果想要使用其他分隔符(如":"),需要FS变量定义
    ARGC 命令行参数个数
    ARGV 命令行参数数组
    FNR 当前文件中的当前记录数(对输入文件起始为1)
    OFMT 数值的输出格式(默认为%.6g)
    OFS 输出字段的分隔符(默认为空格)
    ORS 输出记录分割符(默认为换行符)
    RS 输入记录分隔符(默认为换行符)
    [root@centos2 ~]# cat /etc/passwd |grep "/bin/bash" | 
    > awk '{FS=":"} {printf $1 "	" $3 "
    "}'
    #查询可以登录的用户的用户名和UID
    
    root:x:0:0:root:/root:/bin/bash    
    shw    1000
    sss    1001

      这里":"分隔符生效了,但是在第一行却没起作用,需加上"BEGIN"条件

    [root@centos2 ~]# cat /etc/passwd |grep "/bin/bash" | 
    > awk 'BEGIN{FS=":"} {printf $1 "	" $3 "
    "}'
    root    0
    shw    1000
    sss    1001
    [root@centos2 ~]# cat /etc/passwd | grep "/bin/bash" | 
    > awk 'BEGIN{FS=":"} {printf $1 "	" $3 "	 行号: " NR "	 列数: " NF "
    "}'
    #开始执行{分隔符是“:”} {输出第一字段和第三字段 输出行号(NR值) 列数(NF值)}
    
    
    root    0     行号: 1     列数: 7
    shw    1000     行号: 2     列数: 7
    sss    1001     行号: 3     列数: 7

      只想看sshd这个伪用户的相关信息,可以这样

    [root@centos2 ~]# cat /etc/passwd | 
    > awk 'BEGIN{FS=":"} $1=="sshd" {printf $1 "	" $3 "	 行号: " NR "	 列数:" NF "
    "}'
    #可以看到sshd伪用户的UID是74,是/etc/passwd文件的第40行,此行有7列(7个字段)
    sshd    74     行号: 40     列数:7

    2.5 awk流程控制

      首先看一下student.txt这个文件的内容

    [root@centos2 ~/sh]# cat student.txt 
    ID    NAME    PHP    LINUX    MYSQL    AVERAGE
    1    zs    80    90    70    80
    2    ls    75    85    86    82
    3    ww    80    90    85    85

      先看看如何在awk中定义变量与调用变量的值。假如我想统计LINUX成绩的总分,

    [root@centos2 ~/sh]# awk 'NR==2{LINUX1=$4} NR==3{LINUX2=$4} NR==4{LINUX3=$4;totle=LINUX1+LINUX2+LINUX3;print "totle linux is " totle}' student.txt 
    #统计LINUX成绩的总分
    
    totle linux is 265

      解释一下这个命令。"NR==2{LINUX1=$4}"(条件是NR==2,动作是LINUX=$4)这句话指如果输入数据是第二行(第一行为标题行),就把第二行的第四列的值赋予变量"LINUX1"。"NR==2{LINUX1=$4}"这句话指如果输入数据是第三行,就把第三行的第四列的值赋予变量"LINUX1"。"NR==4{LINUX=$4;totle=LINUX1+LINUX2+LINUX3;print "totle linux is " totle}"("NR==4"是条件,后面{}中的都是动作)这句话指如果输入数据是第四行,就把第四行的第四列的值赋予变量"LINUX3";然后定义变量totle的值为"LINUX1+LINUX2+LINUX3";然后输出"totle linux is"关键字,然后加变量totle的值。

      在awk中,因为命令语句非常长,在输入格式时需要注意以下内容:

        多个条件{动作}可以用空格分割,也可以用回车分割

        在一个动作中,如果需要执行多个命令,需要用";"分割,或者回车分割

        在awk中,变量的赋值与调用都不需要加入"$"符

        条件中判断两个值是否相同,使用"==",以便和变量赋值进行区分

       再看一下如何实现流程控制,

    [root@centos2 ~/sh]# awk '{if (NR>=2) {if ($4>=90) printf $2 " is better
    "}}' student.txt 
    #程序中有两个if判断,第一个判断行号大于2,第二个判断linux成绩大于等于90分
    
    zs is better
    ww is better

      其实在awk中if判断语句,完全可以直接利用awk自带的条件来取代

    [root@centos2 ~/sh]# awk 'NR>=2 {test=$4} test>=90 {printf $2 " is better
    "}' student.txt 
    #先判断行号如果大于2,就把第四个字段赋予变量test
    #再判断如果test的值大于等于90,就打印is better
    zs is better
    ww is better

    2.6 awk函数

      awk编程也允许在编程时使用函数。awk函数的自定义方法:

    function 函数名 (参数列表) {
    函数体
    }

      定义一个简单的函数,使用函数打印出student.txt文件的学员姓名和平均成绩

    [root@centos2 ~/sh]# awk 'function test(a,b) {printf a "	" b "
    "} { test($2,$6) }' student.txt 
    #定义函数test,包含两个参数,函数体的内容是输出这两个参数的值
    #调用函数test,并向两个参数传递值
    
    NAME    AVERAGE
    zs    80
    ls    82
    ww    85

    2.7 awk中调用脚本

      对于小的单行程序来说,将脚本作为命令行自变量传递给awk是非常简单的,而对于多行程序就比较难处理。当程序时多行的时候,使用外部脚本是很合适的。首先在外部文件中写好脚本,然后使用awk的-f选项,使其读入脚本并且执行。

      例,先编写一个awk脚本

    [root@centos2 ~/sh]# vim pass.awk
    BEGIN   {FS=":"}
    { print $1 "	" $3}

      然后使用"-f"选项调用这个脚本

    [root@centos2 ~/sh]# awk -f pass.awk /etc/passwd
    root    0
    bin    1
    daemon    2
    ...

    3. sed命令

      sed主要是用来将数据进行选取、替换、删除、新增的命令

    [root@centos2 ~/sh]# sed [选项] '[动作]' 文件名
    选项
        -n                    一般sed命令会把所有的数据都输出到屏幕,如果加入此选项,则只会把经过sed命令处理的行输出到屏幕
        -e                    允许对输入数据应用多条sed命令编辑
        -f 脚本文件名     从sed脚本中读入sed操作。和awk的-f选项类似
        -r                    在sed中支持扩展正则表达式
        -i                    用sed的修改结果直接修改读取数据的文件,而不是由屏幕输出
    动作
        a                     追加,在当前行后添加一行或多行。添加多行时,除最后一行外,每行末尾需要用“”代表数据未完结
        c                     行替换,用c后面的字符串替换原数据行,替换多行时,除最后一行外,每行末尾需用""代表数据未完结
        i                     插入,在当前行前插入一行或多行。插入多行时,除最后一行外,每行末尾需用""代表数据未完结
        d                     删除,删除指定的行
        p                     打印,输出指定的行
        s                     字串替换,用一个字符串替换另外一个字符串。格式为"行范围s/旧字串/新字串/g"(和vim中的替换类似)

      对sed命令需注意,sed所做的修改并不会直接改变文件的内容(如果是用管道符接受的命令的输入,这种情况连文件都没有),而是把修改结果只显示到屏幕上,除非使用"-i"选项才会直接修改文件。

    3.1 行数据操作

      查看student.txt文件中的第二行,可使用"p"动作

    [root@centos2 ~/sh]# sed '2p' student.txt 
    ID    NAME    PHP    LINUX    MYSQL    AVERAGE
    1    zs    80    90    70    80
    1    zs    80    90    70    80
    2    ls    75    85    86    82
    3    ww    80    90    85    85

      "p"命令确实输出了第二行数据,但是sed命令还会把所有数据都输出一次,这是就会看到上述这个奇怪的结果。如果想要指定输出某行数据,需使用"-n"选项

    [root@centos2 ~/sh]# sed -n '2p' student.txt 
    1    zs    80    90    70    80

      如何删除文件的数据

    [root@centos2 ~/sh]# sed '2,4d' student.txt 
    #删除第二行到第四行的数据
    ID    NAME    PHP    LINUX    MYSQL    AVERAGE
    
    
    
    
    [root@centos2 ~/sh]# cat student.txt 
    #但是文件本身并没有修改
    ID    NAME    PHP    LINUX    MYSQL    AVERAGE
    1    zs    80    90    70    80
    2    ls    75    85    86    82
    3    ww    80    90    85    85

      如何追加和插入行数据

    [root@centos2 ~/sh]# sed '2a hello' student.txt 
    #在第二行后加入hello
    ID    NAME    PHP    LINUX    MYSQL    AVERAGE
    1    zs    80    90    70    80
    hello
    2    ls    75    85    86    82
    3    ww    80    90    85    85

      "a"会在指定行后面追加入数据,如果想要在指定行前面插入数据,则需要使用动作"i"

    [root@centos2 ~/sh]# sed '2i hello world' student.txt 
    #在第二行前面插入数据
    ID    NAME    PHP    LINUX    MYSQL    AVERAGE
    hello world
    1    zs    80    90    70    80
    2    ls    75    85    86    82
    3    ww    80    90    85    85

      如果想要追加或插入多行数据,除最后一行外,每行的末尾都要加入""代表数据未完结。再来看看"-n"选项的作用

    [root@centos2 ~/sh]# sed -n '2i hello 
    > world' student.txt
    hello 
    world

      "-n"只查看sed命令操作的数据,而不是所有的数据。

      如何实现行数据的替换。

    [root@centos2 ~/sh]# cat student.txt | sed '2c no such person'
    ID    NAME    PHP    LINUX    MYSQL    AVERAGE
    no such person
    2    ls    75    85    86    82
    3    ww    80    90    85    85

      sed命令默认情况是不会修改文件内容的,如果确定要让sed命令直接处理文件的内容,可以使用"-i"选项。该选项需谨慎使用。

    [root@centos2 ~/sh]# sed -i '2c No such person' student.txt 
    
    [root@centos2 ~/sh]# cat student.txt 
    ID    NAME    PHP    LINUX    MYSQL    AVERAGE
    No such person
    2    ls    75    85    86    82
    3    ww    80    90    85    85

    3.2 字符串替换

      "c"动作是进行行替换的,仅仅想要替换行中的部分数据,需使用"s"动作,格式如下:

    [root@centos2 ~/sh]# sed -i 's/旧字串/新字串/g' 文件名

      替换的格式和vim非常类似

    [root@centos2 ~/sh]# cat students.txt 
    ID    NAME    PHP    LINUX    MYSQL    AVERAGE
    1    zs    80    90    70    80
    2    ls    92    85    86    82
    3    ww    80    90    85    85
    
    [root@centos2 ~/sh]# sed -i '4s/90/92/g' students.txt 
    #在第四行中,把90替换成92
    
    [root@centos2 ~/sh]# cat students.txt 
    ID    NAME    PHP    LINUX    MYSQL    AVERAGE
    1    zs    80    90    70    80
    2    ls    92    85    86    82
    3    ww    80    92    85    85

      想把某行数据注释掉

    [root@centos2 ~/sh]# sed -i '4s/^/#/g' students.txt 
    #使用正则表达式,"^"代表行首
    [root@centos2 ~/sh]# cat students.txt 
    ID    NAME    PHP    LINUX    MYSQL    AVERAGE
    1    zs    80    90    70    80
    2    ls    92    85    86    82
    #3    ww    80    92    85    85

      在sed中只能指定行范围

    [root@centos2 ~/sh]# sed -e 's/zs//g ; s/ww//g' students.txt 
    #同时把“zs”和“ww”替换为空
    ID    NAME    PHP    LINUX    MYSQL    AVERAGE
    1        80    90    70    80
    2    ls    92    85    86    82
    #3        80    92    85    85

      "-e"选项可以同时执行多个sed动作,当然如果只是执行一个动作也可以使用"-e"选项,但是没什么意义。注意,多个动作之间要用";"号或回车分割。

    [root@centos2 ~/sh]# sed -e 's/zs//g 
    > s/ww//g' students.txt
    ID    NAME    PHP    LINUX    MYSQL    AVERAGE
    1        80    90    70    80
    2    ls    92    85    86    82
    #3        80    92    85    85

    三、字符处理命令

    1. 排序命令sort

    [root@centos2 ~/sh]# sort [选项] 文件名
    选项
        -f                忽略小写
        -b                忽略每行前面的空白部分
        -n                以数值型进行排序,默认使用字符串型排序
        -r                反向排序
        -u                删除重复行。就是uniq命令
        -t                指定分隔符,默认分隔符是制表符
        -k n[,m]         按照指定的字段范围排序、从第n字段开始,m字段结束(默认到行尾)

      sort命令默认是用每行开头第一个字符来进行排序的,例

    [root@centos2 ~/sh]# sort /etc/passwd
    #排序用户信息文件
    abrt:x:173:173::/etc/abrt:/sbin/nologin
    adm:x:3:4:adm:/var/adm:/sbin/nologin
    avahi:x:70:70:Avahi mDNS/DNS-SD Stack:/var/run/avahi-daemon:/sbin/nologin
    bin:x:1:1:bin:/bin:/sbin/nologin
    chrony:x:992:989::/var/lib/chrony:/sbin/nologin
    colord:x:996:993:User for colord:/var/lib/colord:/sbin/nologin
    daemon:x:2:2:daemon:/sbin:/sbin/nologin

      如果想反向排序,使用"-r"选项

    [root@centos2 ~/sh]# sort -r /etc/passwd
    #反向排序
    usbmuxd:x:113:113:usbmuxd user:/:/sbin/nologin
    unbound:x:991:988:Unbound DNS resolver:/etc/unbound:/sbin/nologin
    tss:x:59:59:Account used by the trousers package to sandbox the tcsd daemon:/dev/null:/sbin/nologin
    tcpdump:x:72:72::/:/sbin/nologin
    systemd-network:x:192:192:systemd Network Management:/:/sbin/nologin
    sync:x:5:0:sync:/sbin:/bin/sync
    sss:x:1001:1002::/home/sss:/bin/bash
    ...

      想要指定排序的字段,需使用"-t"选项指定分隔符,并使用"-k"选项指定字段号,假如想要按照UID字段排序/etc/passwd文件

    [root@centos2 ~/sh]# sort -t ":" -k 3,3 /etc/passwd
    #指定分隔符是“:”,用第三段开头,第三段结尾排序,就是只有第三字段排序
    root:x:0:0:root:/root:/bin/bash
    bin:x:1:1:bin:/bin:/sbin/nologin
    shw:x:1000:1000:shw:/home/shw:/bin/bash
    sss:x:1001:1002::/home/sss:/bin/bash
    qemu:x:107:107:qemu user:/:/sbin/nologin
    operator:x:11:0:operator:/root:/sbin/nologin
    usbmuxd:x:113:113:usbmuxd user:/:/sbin/nologin
    games:x:12:100:games:/usr/games:/sbin/nologin
    ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin
    pulse:x:171:171:PulseAudio System Daemon:/var/run/pulse:/sbin/nologin
    rtkit:x:172:172:RealtimeKit:/proc:/sbin/nologin
    abrt:x:173:173::/etc/abrt:/sbin/nologin
    systemd-network:x:192:192:systemd Network Management:/:/sbin/nologin
    daemon:x:2:2:daemon:/sbin:/sbin/nologin
    ...

      仔细看发现daemon用户的UID是2,却排在下面,这是因为sort默认是按照字符排序,前面用户的UID的第一个字符都是1,所有这么排序。要想按照数字排序,请使用"-n"选项

    [root@centos2 ~/sh]# sort -n -t ":" -k 3,3 /etc/passwd
    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
    mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
    operator:x:11:0:operator:/root:/sbin/nologin
    games:x:12:100:games:/usr/games:/sbin/nologin
    ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin
    ...

      当然"-k"选项可以直接使用"-k 3",代表从第三字段到行尾都排序(第一个字符先排序,如果一致,第二个字符再排序,直到行尾)

     2. uniq

      uniq命令是用来取消重复行的命令,其实和"sort -u"选项都是一样的。格式如下:

    [root@centos2 ~/sh]# uniq [选项] 文件名
    选项
        -i            忽略大小写

    3. wc统计命令

    [root@centos2 ~/sh]# wc [选项] 文件名
    选项
        -l                只统计行数
        -w                只统计单词数
        -m                只统计字符数

    四、条件判断

    1. 按照文件类型进行判断

    测试选项 作用
    -b 文件 判断该文件是否存在,并且是否为块设备文件(是块设备文件为真)
    -c 文件 判断该文件是否存在,并且是否为字符设备文件(是字符设备文件为真)
    -d 文件 判断该文件是否存在,并且是否为目录文件(是目录为真)
    -e 文件 判断该文件是否存在(存在为真)
    -f 文件 判断该文件是否存在,并且是否为普通文件(是普通文件为真)
    -L 文件 判断该文件是否存在,并且是否为符号链接文件(是符号链接文件为真)
    -p 文件 判断该文件是否存在,并且是否为管道文件(是管道文件为真)
    -s 文件 判断该文件是否存在,并且是否为非空(非空为真)
    -S 文件 判断该文件是否存在,并且是否为套接字文件(是套接字文件为真)
    [root@centos2 ~/sh]# [ -e /root/sh ]
    [root@centos2 ~/sh]# echo $?
    0
    #判断结果为0,/root/sh目录存在
    
    
    [root@centos2 ~/sh]# [ -e /root/test ]
    [root@centos2 ~/sh]# echo $?
    1
    #在/root/下并没有test文件或目录,故"$?"的返回值非零

      多命令顺序执行"&&"和"||"。可以再判断一下/root/sh是否为目录

    [root@centos2 ~/sh]# [ -d /root/sh ] && echo "yes" || echo "no"
    yes
    #第一个判断命令如果正确执行,则打印"yes",否则打印"no"

    2. 按照文件权限进行判断

      test是非常完善的判断命令,还可以判断文件的权限

    测试选项 作用
    -r 文件 判断该文件是否存在,并且是否该文件拥有读权限(有读权限为真)
    -w 文件 判断该文件是否存在,并且该文件是否拥有写权限(有写权限为真)
    -x 文件 判断该文件是否存在,并且该文件是否拥有执行权限(有执行权限为真)
    -u 文件 判断该文件是否存在,并且该文件是否拥有SUID权限(有SUID权限为真)
    -g 文件 判断该文件是否存在,并且该文件是否拥有SGID权限(有SGID权限为真)
    -k 文件 判断该文件是否存在,并且该文件是否拥有SBIT权限(有SBIT权限为真)

    [root@centos2 ~/sh]# ll
    总用量 12
    -rw-r--r-- 1 root root 35 12月  2 21:44 pass.awk
    -rw-r--r-- 1 root root 84 12月  2 22:17 students.txt
    -rw-r--r-- 1 root root 81 12月  2 22:11 student.txt
    [root@centos2 ~/sh]# [ -w student.txt ] && echo "yes" || echo "no"
    yes
    #判断该文件是否拥有写权限

    3. 两个文件之间进行比较

    测试选项 作用
    文件1 -nt 文件2 判断文件1的修改时间是否比文件2的新(如果新则为真)
    文件1 -ot 文件2 判断文件1的修改时间是否比文件2的旧(如果旧则为真)
    文件1 -ef 文件2 判断文件1是否和文件2的Inode号一致,可以理解为两个文件是否为同一个文件。这个判断用于硬链接判断是很好的方法

      如何判断两个文件是否为硬链接

    [root@centos2 ~/sh]# ln /root/sh/students.txt /tmp/stus.txt
    #创建硬链接
    [root@centos2 ~/sh]# [ /root/sh/students.txt -ef /tmp/stus.txt ] && echo "yes" || echo "no"
    yes

    4. 两个证书之间比较

    测试选项 作用
    整数1 -eq 整数2 判断整数1是否和整数2相等(相等为真)
    整数1 -ne 整数2 判断整数1是否和整数2不相等(不相等为真)
    整数1 -gt 整数2 判断整数1是否大于整数2(大于为真)
    整数1 -lt 整数2 判断整数1是否小于整数2(小于为真)
    整数1 -ge 整数2 判断整数1是否大于等于整数2(大于等于为真)
    整数1 -le 整数2 判断整数1是否小于等于整数2(小于等于为真)

    [root@centos2 ~/sh]# [ 23 -gt 22 ] && echo "yes" || echo "no"
    yes
    #判断23是否大于22
    [root@centos2 ~/sh]# [ 23 -le 22 ] && echo "yes" || echo "no"
    no
    #判断23是否小于等于22

    5. 字符串的判断

    测试选项 作用
    -z 字符串 判断字符串是否为空(为空返回真)
    -n 字符串 判断字符串是否为非空(非空返回证)
    字串1 == 字串2 判断字符串1是否和字符串2相等(相等返回真)
    字串1 != 字串2 判断字符串1是否和字符串2不相等(不相等返回真)

    [root@centos2 ~/sh]# name=zs
    #为变量赋值
    [root@centos2 ~/sh]# [ -z $name ] && echo "yes" || echo "no"
    no
    #判断name变量是否为空,因为不为空,故返回no

    如何判断两个字符串相等

    [root@centos2 ~/sh]# aa=11
    [root@centos2 ~/sh]# bb=22
    #给变量aa、bb赋值
    [root@centos2 ~/sh]# [ "$aa" == "$bb" ] && echo "yes" || echo "no"
    no
    #判断两个变量的值是否相等

    6. 多重条件判断

    测试选项 作用
    判断1 -a 判断2 逻辑与,判断1和判断2都成立,最终的结果才为真
    判断1 -o 判断2 逻辑或,判断1和判断2有一个成立,最终的结果为真
    ! 判断 逻辑非,使原始的判断式取反

    [root@centos2 ~/sh]# a=11
    #赋值
    [root@centos2 ~/sh]# [ -n "$a" -a "$a" -gt 22 ] && echo "yes" || echo "no"
    no
    #判断变量a是否有值,同时判断变量a是否大于22
    #因为变量a的值不大于22,所以虽然第一个判断值为真,返回的结果仍是假
    [root@centos2 ~/sh]# a=23
    [root@centos2 ~/sh]# [ -n "$a" -a "$a" -gt 22 ] && echo "yes" || echo "no"
    yes

    逻辑非

    [root@centos2 ~/sh]# [ ! -n "$a" ] && echo "yes" || echo "no"
    no
    #变量a的值非空,返回值为真
    #加!后,判断值会取反,故当a有值时,返回值是假
    
    [root@centos2 ~/sh]# [ ! -z "$a" ] && echo "yes" || echo "no"
    yes

    五、流程控制

    1. if条件判断

    1.1 单分支if条件语句

      单分支条件语句最简单,就是只有一个判断条件,如果符合条件则执行某个程序,否则什么事情都不做。语法如下:

    if [ 条件判断式 ];then
        程序
    fi

      单分支条件语句需要注意几个点:

        if语句使用fi结尾,和一般语言使用大括号结尾不同

        [ 条件判断式 ]就是使用命令判断,中括号和条件判断式之间必须有空格

        then后面跟符合条件之后执行的程序,可以放在[]之后,用";"分割。也可以换行写入,就不需要";"了,如:

    if [ 条件判断式 ]
        then
            程序
    fi
    [root@centos2 ~/sh]# vi if1.sh
    #!/bin/bash
    #author by nanshan 20191202
    
    rate=$(df -h | grep /dev/mapper/centos-root |awk '{print $5}' | cut -d "%" -f 1)
    #把根分区使用率最为变量值赋予变量rate
    if [ $rate -ge 80 ];then
    #判断rate的值,如果大于等于80,则执行以下程序
            echo "Warning! /dev/sda3 is full!"
            #打印警告信息。也可以向管理员发送邮件
    fi
        

    1.2 双分支if条件语句

    if [ 条件判断式 ];then
          条件成立时,执行程序1
      else
          条件不成立时,执行程序2
    fi

    例,数据备份

    [root@centos2 ~/sh]# vim if2.sh 
    #!/bin/bash
    #author by nanshan 20191202
    
    ntpdate asia.pool.ntp.org &>/dev/null
    #同步系统时间
    date=$(date +%y%m%d)
    #把当前系统时间按照"年月日"格式赋予变量date
    size=$(du -sh /var/lib/mysql)
    #统计mysql数据库的大小,并把大赋予size变量
    
    if [ -d /tmp/dbbak ];then
    #判断备份目录是否存在,是否为目录
            echo "DATE : $date!" > /tmp/dbbak/dbinfo.txt
            #把当前日期写入临时文件
            echo "Data size : $size" >> /tmp/dbbak/dbinfo.txt
            #把数据库的大小写入临时文件
            cd /tmp/dbbak
            #进入备份目录
            tar -zcf mysql-lib-$date.tar.gz /var/lib/mysql dbinfo.txt &>/dev/null
            #打包压缩数据库与临时文件,把所有输出丢入垃圾箱(不看任何输出)
            rm -rf /tmp/dbbak/dbinfo.txt
            #删除临时文件
    else
            mkdir /tmp/dbbak
            #如果判断不为真,则建立备份目录
            echo "Date : $date!" > /tmp/dbbak/dbinfo.txt
            echo "Data : $size" >> /tmp/dbbak/dbinfo.txt
            #把日期和数据库大小保存入临时文件
            cd /tmp/dbbak
            tar -zcf mysql-lib-$date.tar.gz /var/lib/mysql dbinfo.txt $>/dev/null
            #压缩备份数据库与临时文件
            rm -rf /tmp/dbbak/dbinfo.txt
            #删除临时文件
    fi

       在工作中,服务器经常会宕机。通过脚本监听本机的服务,如果服务停止或宕机了,可以自动重启这些服务。以apache为例

    [root@centos2 ~/sh]# vim autostart.sh 
    #!/bin/bash
    #author by nanshan 20191203
    
    PORT=$(nmap -sT 192.168.80.128 | grep tcp |grep http |awk '{print $2}')
    #使用nmap命令扫描服务器,并截取Apache服务的状态,赋予变量PORT
    if [ "$PORT" == "open" ];then
            echo "$(date) httpd is ok!" >> /tmp/autostart-acc.log
    else
            /etc/init.d/httpd start &>/dev/null
            echo "$(date) restart httpd !!" >> /tmp/autostart-acc.log
    fi

    nmap端口扫描命令,格式如下

    [root@centos2 ~/sh]# nmap -sT 域名或IP
    选项
        -s        扫描
        -T        扫描所有开启的TCP端口

    1.3 多分支if条件语句

    if [ 条件判断式1 ];then
        条件判断式1成立时,执行程序1
    elif [ 条件判断式2 ];then
        条件判断式2成立时,执行程序2
    ...
    else
        所有条件都不成立时,最后执行此程序
    fi

      例,使用if多分支条件句判断用户输入的是一个文件还是目录

    [root@centos2 ~/sh]# vim if-elif.sh
    #!/bin/bash
    #by author nanshan 20191203
    
    read -p "please input a filename: " file
    #接受键盘的输入,并赋予变量file
    
    if [ -z "$file" ];then
    #判断变量file是否为空
            echo "ERROR,please input a filename"
            #如果为空,执行程序1,输出报错信息
            exit 1
            #退出程序,并返回值为1(返回值赋予变量$?elif [ ! -e "$file" ];then
    #判断文件是否存在
            echo "$your input is not a file!"
            #如果不存在,执行程序2
            exit 2
            #退出程序,并定义返回值为2
    elif [ -f "$file" ];then
    #判断file变量的值是否为普通文件
            echo "$file is a regulare file."
            #如果是,则执行程序3
    elif [ -d "$file" ];then
    #判断变量file的值是否为目录
            echo "$file is a directory."
            #如果是,则执行程序4
    else
            echo "$file is an other file!"
            #如果以上判断都不是,执行最后程序
    fi

    2. 多分支case条件语句

      case语句和if···elif···else语句一样是多分支条件语句,不过和if多分支条件语句不同的是,case只能判断一种条件关系,而if语句可以判断多种条件语句。case语句语法如下:

    case $变量名 in
        "值1")
            如果变量的值等于值1,则执行程序1
            ;;
        "值2")
            如果变量的值等于值2,则执行程序2
            ;;
        ···略···
        *)
            如果变量的值不等于以上所有值,则执行此程序
            ;;
    esac

      这个语句需注意下述内容

        case语句,会取出变量中的值,然后与语句体中的值逐一比较。如果数值符合,则执行对应的程序,如果数值不等,则依次比较下一个值。如果所有的值都不符合,则执行"*)" ("*"代表所有其他值)中的程序

        case语句以"case"开头,以"esac"结尾。

    每个分支程序之后要通过";;"双分号结尾,代表该程序段结束(不可忘记)。

    例:判断是yes或no;

    [root@centos2 ~/sh]# vim case.sh 
    #!/bin/bash
    #by author nanshan 20191203
    
    read -p "please choose yes or no: " -t 30 cho
    
    case $cho in
            "yes")
                    echo "your choose is yes."
                    ;;
            "no")
                    echo "your choose is no."
                    ;;
            *)
                    echo "your choose is error!"
                    ;;
    esac

    3. for 循环

      for循环是固定循环,也就是循环时已经知道需要进行几次的循环,有时也把for循环称为计数循环。for循环有两种语法:

    语法一:
    
    for 变量 in 值1 值2 值3 ···
        do
            程序
        done

      这种语法中for循环的次数,取决于in后面值的个数(空格分隔),有几个就循环几次,并且每次循环都把值赋予变量。

    语法二:
    
    for (( 初始值;循环控制条件;变量变化))
        do
            程序
        done

      语法二中需要注意:

        初始值:在循环开始时,需要给某个变量赋予初始值,如i=1;

        循环控制条件:用于指定变量循环的次数,如i<=100,则只要i的值小于等于100,循环就会继续;

        变量变化:每次循环之后,变量要如何变化,如 i=i+1。代表每次循环之后,变量i的值都加1。

    3.1 语法一举例

    例1,打印时间

    [root@centos2 ~/sh]# vim for.sh 
    #!/bin/bash
    #by author nanshan 20191203
    
    for time in morning noon afternoon eveing
            do
                    echo "this time is $time!"
            done

    例2,批量解压缩

    [root@centos2 ~/sh]# vim auto-tar.sh
    #!/bin/bash
    #by author nanshan 20191203
    
    cd /lamp
    #进入到压缩包目录
    ls *.tar.gz > ls.log
    #把所有以.tar.gz结尾的文件覆盖到ls.log临时文件中
    
    for i in $(cat ls.log)
    #读取ls.log文件中的内容,文件有多少个值,就会循环多少次,每次循环把文件名赋予变量i
            do
                    tar -zxf $i &>/dev/null
                    #解压,并把所有的输出丢弃
            done
    
    rm -rf /lamp/ls.log
    #删除临时文件ls.log

    3.2 语法二举例

      语法二和其他语言中的for循环类似,事先决定循环次数的固定循环。

    例1. 从1加到100

    [root@centos2 ~/sh]# vim for2.sh
      
    #!/bin/bash
    #by author nanshan 20191203
    
    s=0
    for (( i=1;i<=100;i++ ))
            do
                    s=$(( $s+$i ))
            done
    echo "the sum of 1+2+···+100 is : $s"

    例2. 批量添加用户

    [root@centos2 ~/sh]# vim useradd.sh
      
    #!/bin/bash
    #by author nanshan 20191203
    
    read -p "please input user name: " -t 30 name
    #让用户输入用户名,把输入保存到变量name
    read -p "please input the number of users: " -t 30 num
    #让用户输入添加用户的数量,把输入保存到变量num
    read -p "please input the password of users: " -t 30 pass
    #让用户输入初始密码,把输入保存到变量pass
    
    if [ ! -z "$name" -a ! -z "$num" -a ! -z "$pass" ];then
    #判断三个变量是否为空
            y=$(echo $num | sed 's/[0-9]//g')
            #定义变量的值为后续命令的结果
            #把变量num的值替换为空。如果能替换为空,则证明num的值为数字,反之,为非全数字
            if [ -z "$y" ];then
            #如果变量y的值为空
                    for (( i=1;i<=$num;i=i+1 ))
                    #循环num变量指定的次数
                    do
                                    /usr/sbin/useradd $name$i &>/dev/null
                                    #添加用户,用户名为变量name的值加上变量i的数字
                                    echo $pass | /usr/sbin/passwd --stdin $name$i &>/dev/null
                                    #给用户设定初始密码位变量pass的值
                            done
            fi
    fi
        

    例3. 批量删除用户

    [root@centos2 ~/sh]# vim userdel.sh
      
    #!/bin/bash
    #by author nanshan 20191203
    
    user=$(cat /etc/passwd | grep "/bin/bash" | grep -v "root" | cut -d ":" -f 1)
    #读取用户信息文件,提取可以登录的用户信息,取消root用户,截取第一列用户名
    for i in $user
    #循环,有多少个普通用户,循环几次
    do userdel -r $i done

    4. while循环

    while [ 条件判断式 ]
        do
            程序
        done

      对while循环来讲,只有条件判断式成立,循环就会一直继续,直到条件判断式不成立,循环才会停止。

    [root@centos2 ~/sh]# vim while.sh
      
    #!/bin/bash
    #by author nanshan 20191203
    
    i=1
    s=0
    while [ $i -le 100 ]
    #如果变量i的值小于等于100,就执行循环
            do
                    s=$(( $s+$i ))
                    i=$(( $i+1 ))
            done
    
    echo "the sum is: $s"

    5. until循环

      until循环与while循环相反,until循环只要条件判断式不成立则进行循环,并执行循环程序。一旦循环条件成立,就终止循环。

    until [ 条件判断式 ]
        do
            程序
        done

    [root@centos2 ~/sh]# vim until.sh
      
    #!/bin/bash
    #by author nanshan 20191203
    
    s=0
    i=1
    until [ $i -gt 100 ]
    #循环直到变量i的值大于100,才停止循环
            do
                    s=$(( $s+$i ))
                    i=$(( $i+1 ))
            done
            
    echo "the sum is: $s"   

    6. 函数

    function 函数名 () {
        程序
    }

    [root@centos2 ~/sh]# vim function.sh 
    #!/bin/bash
    
    
    function sum () {
    #定义函数
            s=0
            for (( i=0;i<=$1;i=i+1 ))
            #循环直到i大于$1为止,$1是函数sum的第一个参数
            #在函数中也可以使用位置参数变量,只是这里的$1指的是函数的第一个参数
                    do
                            s=$(( $s+$i ))
                    done
            echo "the sum of 1+2+3+···+$1 is: $s"
            #输出1加到$1的和
    }
    
    read -p "please input a number: " -t 30 num
    #接受用户输入的数字,并把值赋予变量num
    y=$(echo $num | sed 's/[0-9]//g')
    #把变量num的值替换为空,并赋予变量y
    if [ -z "$y" ];then
    #判断y变量是否为空,以确定变量num是否为纯数字
            sum $num
            #调用sum函数,并把变量num的值作为第一个参数传递给sum函数
    else
            echo "ERROR! Please input a number!"
            #如果变量num的值不是纯数字,就输出报错信息
    fi

    7. 特殊流程控制语句

    7.1 exit语句

      系统使用exit命令的,用户退出当前用户的登录状态。在shell脚本中,exit语句是用来退出当前脚本的。就是说,在shell脚本中,只要碰到exit语句,后续的程序就不再执行,而直接退出脚本。语法如下:

    exit [返回值]

      如果exit命令之后定义了返回值,那么脚本执行之后的返回值就是我们自己定义的返回值。可以通过"echo $?"这个命令查看返回值。如果exit之后没有定义返回值,脚本执行之后的返回值是执行exit语句之前,最后执行的一条命令的返回值。

    [root@centos2 ~/sh]# vim exit.sh 
    #!/bin/bash
    
    read -p "please input a number: " -t 30 num
    
    y=$(echo $num | sed 's/[0-9]//g')
    
    [ -n "$y" ] && echo "error! please input a number!" && exit 10
    #判断变量y的值,如果为空,输出报错信息,退出脚本,返回值为10
    
    echo "the number is: $num"
    #如果没有退出脚本,打印变量num中的数字

    7.2 break语句

      当程序执行到break语句时,会结束整个当前循环。而continue语句时结束本次循环。

      break结束整个循环,continue结束此次循环,下次继续

    [root@centos2 ~/sh]# vim break.sh
      
    #!/bin/bash
    
    for (( i=1;i<=10;i=i+1 ))
            do
                    if [ "$i" -eq 4 ];then
                    #如果变量i的值等于4
                            break
                    #退出整个循环
                    fi
                    echo $i
                    #输出变量i的值
            done

    7.3 continue语句

      continue语句也是结束流程控制语句。在循环中,continue语句只会结束单次当前循环。

    [root@centos2 ~/sh]# vim continue.sh
    #!/bin/bash
    
    for (( i=1;i<=10;i=i+1 ))
            do
                    if [ "$i" -eq 4 ];then
                            continue
                            #换成continue
                    fi
                    echo $i
            done

      continue只会退出当前此次循环,不影响后续的循环。

  • 相关阅读:
    Project Chameleon Work In Progress 14
    All about Project Chameleon
    网页中图片连续滚动代码 (转)
    一点感言
    一些常用javascript代码(转)
    asp.net(c#)的一个非常非常奇怪的问题
    用javascript拦截a标签的超链接执行
    asp.net中用TreeView控件实现无限分级的好办法
    windows7 安装ez usb基本驱动
    管道编程
  • 原文地址:https://www.cnblogs.com/sswind/p/11929002.html
Copyright © 2011-2022 走看看