• shell脚本学习


    	shell是一个用 C 语言编写的程序,是用户使用 Linux 的桥梁。Shell 既是一种命令语言,又是一种程序设计语言。
    	实际上Shell是一个命令解释器,它解释由用户输入的命令并且把它们送到内核。不仅如此,Shell有自己的编程语言用于对命令的编辑,它允许用户编写由shell命令组成的程序。Shell编程语言具有普通编程语言的很多特点,比如它也有循环结构和分支控制结构等,用这种编程语言编写的Shell程序与其他应用程序具有同样的效果。
    
    • 1
    • 2

    1、shell的分类

    查看分类:cat /etc/shells

    [root@localhost lk_test]# cat /etc/shells
    /bin/sh
    /bin/bash
    /usr/bin/sh
    /usr/bin/bash
    
    • 1
    • 2
    • 3
    • 4
    • 5

    linux中有很多类型的shell,不同的shell具备不同的功能,shell还决定了脚本中函数的语法,linux中默认的shell是/bin/bash。流行的shell有ash、bash、ksh、csh、zsh等,不同的shell都有自己的特点以及用途。

    编写规范:

    #!/bin/bash	告知系统当前这个脚本要使用的shell解释器
    Shell相关指令
    
    • 1
    • 2

    文件命名规范:

    文件名.sh # .sh是linux下bash shell 的默认后缀。

    1.1、Bash 常用快捷键

    快捷键作用
    ctrl+A把光标移动到命令行开头。如果我们输入的命令过长,想要把光标移动到命令行开头时使用。
    ctrl+E把光标移动到命令行结尾。
    ctrl+C强制终止当前的命令。
    ctrl+L清屏,相当于clear命令。
    ctrl+U删除或剪切光标之前的命令。我输入了一行很长的命令,不用使用退格键一个一个字符的删除,使用这个快捷键会更加方便
    ctrl+K删除或剪切光标之后的内容。
    ctrl+Y粘贴ctrl+U或ctul+K剪切的内容。
    ctrl+R在历史命令中搜索,按下ctrl+R之后,就会出现搜索界面,只要输入搜索内容,就会从历史命令中搜索。
    ctrl+D退出当前终端。
    ctrl+Z暂停,并放入后台。这个快捷键牵扯工作管理的内容。
    ctrl+S暂停屏幕输出。
    ctrl+Q恢复屏幕输出。

    1.2、输入输出重定向

    linux 的标准输入与输出

    设备设备名文件描述符作类型用
    键盘/dev/stdin0标准输入
    显示器/dev/stdout1标准输出
    显示器/dev/stderr2标准错误输出

    输入重定向

    输入重定向:是指不使用系统提供的标准输入端口,而进行重新的指定。换言之,就是不使用标准输入端口输入文件,而是使用指定的文件作为标准输入设备。(重定向简单理解就是使用 “<”符来修改标准输入设备).

    类型符号(语法)功能
    标准输入命令 < 文件1命令把文件1的内容作为标准输入设备
    标识符限定输入命令 << 标识符命令把标准输入中读入内容,直到遇到“标识符”分解符为止
    输入输出重定向(同时使用)命令 < 文件1 > 文件2命令把文件1的内容作为标准输入,把文件2作为标准输出。

    输出重定向

    ​ 输出重定向:(通俗的讲,就是把要输出的文件信息写入到一个文件中去,而不是将要输出的文件信息输出到控制台(显示屏),在linux中,默认的标准输出设备是控制台(或称为显示器),用户输出的信息默认情况下都会显示到控制台。

    &表示全部文件,文件不管对错;1表示标准输出文件;2表示标准错误输出。

    类型符号(语法)作用
    标住输出重定向命令 > 文件以覆盖方式,把命令的正确输出内容输出到指定的文件或设备当中
    标住输出重定向命令 >> 文件以追加方式,把命令的正确输出内容输出到指定的文件或设备当中
    标准错误输出重定向错误命令2 > 文件以覆盖方式,把命令的错误输出输出到指定的文件或设备当中
    标准错误输出重定向错误命令2 >> 文件以追加方式,把命令的错误输出输出到指定的文件或设备当中
    正确输出和错误输出同时保存命令 > 文件 2>&1以覆盖的方式,把正确输出和错误输出都保存到同一个文件当中。
    正确输出和错误输出同时保存命令 >> 文件 2>&1以追加的方式,把正确输出和错误输出都保存到同一个文件当中。
    正确输出和错误输出同时保存命令 &> 文件以覆盖的方式,把正确输出和错误输出都保存到同一个文件当中。
    正确输出和错误输出同时保存命令 &>> 文件以追加的方式,把正确输出和错误输出都保存到同一个文件当中。
    正确输出和错误输出同时保存命令 >> 文件a 2>>文件2把正确的输出追加到文件a中,把错误的输出追加到文件2中。

    1.3、/dev/null 文件

    如果希望执行某个命令,但又不希望在屏幕上显示输出结果,那么可以将输出重定向到**/dev/null**中

    [root@localhost ~]$  command > dev/null
    
    • 1

    1.4、多命令顺序执行

    多命令执行符作用格式
    命令1 ;命令2多个命令顺序执行,命令之间没有任何逻辑联系。
    &&命令1 && 命令2当命令1正确执行(? = 0 ) , 则 命 令 2 才 会 执 行;
    当 命 令 1 执 行 不 正 确 ( ?≠0),则命令2才会执行 。
    ll命令1 || 命令2当命令1执行不正确(? ≠ 0 ) , 则 命 令 2 才 会 执 行;
    当命令1正 确 执 行 ( ?=0),则命令2不会执行。

    1.5、两种方式执行shell脚本

    第一种:给文件增加执行权限

    [root@localhost ~]$ chmod u+x test.sh
    [root@localhost ~]$ ./test.sh  #绝对路径或相对路径执行
    
    • 1
    • 2

    第二种(了解):通过Bash调用执行脚本

    [root@localhost ~]$ bash test.sh
    
    • 1

    2、shell变量

    变量: 在一个脚本周期内,其值可以发生改变的量。

    2.1、变量的命名规则:

    在定义变量时,有一些规则需要遵守:

    1. 命名只能使用英文**字母,数字和下划线**,首个字符不能以数字开头。
    2. 等号左右两侧不能有空格,可以使用下划线“_”,变量的值如果有空格,需要使用单引号或双引号包括。如:“test=“hello world!””。其中双引号括起来的内容“$”,“(”和反引号都拥有特殊含义,而单引号括起来的内容都是普通字符。
    3. 不能使用标点符号,不能使用bash里的关键字(可用help命令查看保留关键字)。
    4. 环境变量建议大写,便于区分。
    5. 如果需要增加变量的值,那么可以进行变量值的叠加。不过变量需要用双引号包含" 变量名 " 或用 变量名"或用 变量名"或用{变量名}包含变量名。

    关于单双引号的问题:

    • 双引号能够识别变量,双引号能够实现转义(类似于“\*”)。
    • 单引号是不能识别变量,只会原样输出,单引号是不能转义的。

    示例:

    [root@localhost ~]$ test=123
    [root@localhost ~]$ test="$test"456
    [root@localhost ~]$ echo $test
    123456
    #叠加变量test,变量值变成了123456
    [root@localhost ~]$ test=${test}789
    [root@localhost ~]$ echo $test
    123456789
    #再叠加变量test,变量值编程了123456789
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    2.2、shell中特殊符号

    符号作用
    ’ ’单引号。在单引号中所有的特殊符号,如“$”和”(反引号)都没有特殊含义。单引号括起来的都是普通字符,会原样输出。
    " "双引号。在双引号中特殊符号都没有特殊含义,但是“$”,“`”(esc键下面)和“\”是例外,拥有“调用变量的值”、“引用命令”和“转义符”的特殊含义。
    · ·反引号。反引号括起来的内容是系统命令在Bash中会先执行它。和( ) 作 用 一 样 , 不 过 推 荐 使 用 ()作用一样,不过推荐使用()作用一样,不过推荐使用(),因为反引号非常容易看错。
    $()和反引号作用一样,用来引用系统命令。(推荐使用)
    ()用于一串命令执行时,()中的命令会在子Shell中运行。
    {}用于一串命令执行时,{ }中的命令会在当前Shell中执行。也可以用于变量变形与替换。
    [ ]用于变量的测试。
    #在Shell脚本中,#开头的行代表注释。
    $用于调用变量的值,如需要调用变量name的值时,需要用$name的方式得到变量的值。
    \转义符,跟在\之后的特殊符号将失去特殊含义,变为普通字符。如 将输出“ 将输出“ 将输出”符号,而不当做是变量引用。
    $(())是用来进行数学运算的。其等价于** [ ] ∗ ∗ ,支持 + − ∗ / []**,支持+-*/%,并且在使用中变量可以不使用 [],支持+/,直接使用变量名即可。如: ( ( (( ((a+ b ) ) 、 b)) 、 b))((a+b))、$[a+b]

    示例1:单引号和双引号

    [root@localhost ~]$ name=sc
    #定义变量name 的值是sc(就是最正直的人,超哥我了!)
    [root@localhost ~]$ echo '$name'
    $name
    #如果输出时使用单引号,则$name原封不动的输出
    [root@localhost ~]$ echo "$name"
    sc
    #如果输出时使用双引号,则会输出变量name的值 sc
    
    [root@localhost ~]$ echo `date`
    2018年10月21日星期一18:16:33 CST
    #反引号括起来的命令会正常执行
    [root@localhost ~]$ echo '`date`'
    `date`
    #但是如果反引号命令被单引号括起来,那么这个命令不会执行,`date`会被当成普通字符输出
    [root@localhost ~]$ echo "`date'"
    2018年10月21日星期一18:14:21 CST
    #如果是双引号括起来,那么这个命令又会正常执行
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    示例2:反引号

    [root@localhost ~]$ echo ls
    ls
    #如果命令不用反引号包含,命令不会执行,而是直接输出
    [root@localhost ~]$ echo `ls`
    anaconda-ks.cfginstall.loginstall.log.syslog sh test testfile
    #只有用反引号包括命令,这个命令才会执行
    [root@localhost ~]$ echo $(date)
    2018年10月21日星期一18:25:09 CST
    #使用$(命令)的方式也是可以的
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    2.3、变量的分类:

    • 用户自定义变量:是最常见的变量,由用户自由定义变量名和变量的值。
    • 环境变量:主要保存的是和系统操作环境相关的数据,比如当前登录用户,用户的家目录,命令的提示符等。环境变量的变量名可以自由定义,但是一般对系统起作用的环境变量的变量名是系统预先设定好的。
    • 位置参数变量:主要是用来向脚本当中传递参数或数据,变量名不能自定义,变量作用是固定的。
    • 预定义变量:是Bash中已经定义好的变量,变量名不能自定义,变量作用也是固定的。
    变量分类名称作用内容
    用户自定义变量自定义自定义自定义
    用户自定义环境变量自定义自定义自定义
    系统自带环境变量(/etc/profile)确定确定自定义
    位置参数变量确定自定义自定义
    预定义变量确定自定义自定义

    2.3.1、用户自定义变量:

    2.3.1.1、变量定义
    [root@localhost ~]$ 2name="li si"
    -bash: 2name=shen chao: command not found
    #变量名不能用数字开头
    [root@localhost ~]$ name = "li si"
    -bash: name: command not found
    #等号左右两侧不能有空格
    [root@localhost ~]$ name=li si
    -bash: chao: command not found
    #变量的值如果有空格,必须用引号包含
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    2.3.1.2、变量调用
    [root@localhost ~]$ name="li si"
    #定义变量name
    [root@localhost ~]$ echo $name #调用变量使用  $变量名
    li si
    #输出变量name的值
    
    • 1
    • 2
    • 3
    • 4
    • 5
    2.3.1.3、变量查看

    语法:set [选项]

    选项:

    • -u :如果设定此选项,调用未声明变量时会报错(默认无任何提示)。

    • -x :如果设定此选项,在命令执行之前,会把命令先输出一次。

    • +<参数>:取消某个set曾启动的参数。如:set +u

    #直接使用set 命令,会查询系统中所有的变量,包含用户自定义变量和环境变量
    [root@localhost ~]$ set
    BASH=/bin/bash
    …省略部分输出…
    name='shen chao'
    
    #当设置了-u选项后,如果调用没有设定的变量会有报错。默认是没有任何输出的。
    [root@localhost ~]$ set -u
    [root@localhost ~]$ echo $file
    -bash: file: unbound variable
    
    #如果设定了-x选项,会在每个命令执行之前,先把命令输出一次
    [root@localhost ~]$ set -x
    [root@localhost ~]$ ls
    +ls --color=auto
    anaconda-ks.cfginstall.loginstall.log.syslog sh tdir testtestfile
    
    #取消启动的x参数
    [root@localhost ~]$ set +x
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    2.3.1.4、变量删除
    # 删除某个变量
    [root@localhost ~]$ unset 变量名
    
    • 1
    • 2

    2.3.2、环境变量

    2.3.2.1、环境变量设置
    # 使用export声明的变量即是环境变量
    [root@localhost ~]$  export age="18"
    
    • 1
    • 2
    2.3.2.2、环境变量查询和删除

    env命令和set命令的区别:

    • set命令可以查看所有变量;
    • env命令只能查看环境变量
    [root@localhost ~]$ unset gender   #删除环境变量gender
    [root@localhost ~]$ env | grep gender
    
    • 1
    • 2
    2.3.2.2、系统默认环境变量
    [root@localhost ~]$ env
    HOSTNAME=localhost.localdomain      #主机名
    SHELL=/bin/bash                     #当前的shell
    TERM=linux                          #终端环境
    HISTSIZE=1000                       #历史命令条数
    SSH_CLIENT=192.168.26.130 4824 22   #当前操作环境是用ssh连接的,这里记录客户端ip
    SSH_TTY=/dev/pts/1                  #ssh连接的终端时pts/1
    USER=root                           #当前登录的用户
    ..........更多参数可以使用set和env命令查看.............
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    2.3.3、位置参数变量:

    位置参数变量作用
    $nn为数字$0表示当前 Shell 脚本程序的名称
    $1-9 代表第一到第九个参 数 , 10以上的参数需要用大括号包含 ,如: {10}
    $*这个变量代表命令行中所有的参数,$把所有的参数看成一个整体
    $@这个变量也代表命令行中所有的参数,不过$@把每个参数区分对待
    $#这个变量代表命令行中所有参数的个数

    示例1: $1 是给写的shell脚本传的第一个参数,$2 是给写的shell脚本传的第二个参数…

    [root@localhost ~]$ vim test.sh
    #!/bin/sh
    echo "shell脚本本身的名字: $0"
    echo "传给shell的第一个参数: $1"
    echo "传给shell的第二个参数: $2"
    
    # 保存退出后,在test.sh所在的目录下输入 bash test.sh 1 2
    [root@localhost ~]$ bash test.sh 1 2
    # 结果输出:
    shell脚本本身的名字: test.sh
    传给shell的第一个参数: 0
    传给shell的第二个参数: 1
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    示例2: ∗ 会把接收的所有参数当成一个整体对待,而 *会把接收的所有参数当成一个整体对待,而 会把接收的所有参数当成一个整体对待,而@则会区分对待接收到的所有参数。

    [root@localhost ~]$ vi parameter2.sh
    #!/bin/bash
    for i in "$*"
    #定义for循环,in后面有几个值,for会循环多少次,注意“$*”要用双引号括起来
    #每次循环会把in后面的值赋予变量i
    #Shell把$*中的所有参数看成是一个整体,所以这个for循环只会循环一次
    	do
    		echo "The parameters is: $i"
    		#打印变量$i的值
    	done
    x=1
    #定义变量x的值为1
    for y in "$@"
    #同样in后面的有几个值,for循环几次,每次都把值赋予变量y
    #可是Shell中把“$@”中的每个参数都看成是独立的,所以“$@”中有几个参数,就会循环几次
    	do
    		#输出变量y的值
    		echo "The parameter$x is: $y"
    		#然变量x每次循环都加1,为了输出时看的更清楚
    		# $(())是用来进行数学运算的。其等价于$[]
    		x=$(($x +1))		
    	done
    	
    [root@localhost ~]$ ./parameter2.sh a b c d e
    # 输出结果:
    The parameters is: a b c d e
    The parameter1 is: a
    The parameter2 is: b
    The parameter3 is: c
    The parameter4 is: d
    The parameter5 is: e
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    2.3.4、预定义变量

    预定义变量作用
    $? 最后一次执行的命令的返回状态。如果这个变量的值为0,证明上一个命令正确执行;
    如果这个变量的值为非0(具体是哪个数,由命令自己来决定),则证明上一个命令执行不正确了。
    $$当前进程的进程号(PID)
    $!后台运行的最后一个进程的进程号(PID)

    示例1:$?

    [root@localhost ~]$ ls
    count.sh hello.sh parameter2.sh parameter.sh
    #ls命令正确执行
    [root@localhost ~]$ echo $?
    #预定义变量“$?”的值是0,证明上一个命令执行正确
    [root@localhost ~]$ ls install.log
    ls:无法访问install.log:没有那个文件或目录
    #当前目录中没有install.log文件,所以ls命令报错了
    [root@localhost ~]$ echo $?
    2
    #变量“$?”返回一个非О的值,证明上一个命令没有正确执行
    #至于错误的返回值到底是多少,是在编写ls命令时定义好的,如果碰到文件不存在就返回数值2
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    2.4、只读变量

    **语法:**readonly (选项) (参数)

    选项:

    • -f:定义只读函数;
    • -a:定义只读数组变量;
    • -p:显示系统中全部只读变量列表。

    参数:

    • 变量定义:定义变量,格式为“变量名=‘变量值’

    注意:

    • declare -r variable不能去除只读属性,unset不能删除只读变量。
    • 只读变量不能更改内容,不能重设,只有当前shell注销后才失效。
    [root@localhost ~]$ vi readonly.sh
    #!/bin/bash
    a=10
    # 定义只读变量 或者 readonly a=10
    readonly a
    # 给只读变量重新赋值,会报错readonly variable
    a=20   
    echo $a
    [root@localhost ~]$ ./readonly.sh
    # 输出结果
    ./readonly.sh: line 6: a: readonly variable
    10
    
    # 显示所有只读变量
    [root@localhost ~]# readonly     
    declare -r BASHOPTS="checkwinsize:cmdhist:expand_aliases:extquote:force_fignore:histappend:hostcomplete:interactive_comments:login_shell:progcomp:promptvars:sourcepath"
    declare -ir BASHPID
    declare -ar BASH_VERSINFO='([0]="4" [1]="2" [2]="46" [3]="1" [4]="release" [5]="x86_64-redhat-linux-gnu")'
    declare -ir EUID="1102"
    declare -ir PPID="118456"
    declare -r SHELLOPTS="braceexpand:emacs:hashall:histexpand:history:interactive-comments:monitor"
    declare -ir UID="1102"
    declare -r a="10"
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    2.5、接受键盘输入

    语法:read [选项] [变量名]
    选项:
    -a 后跟一个变量 :该变量会被认为是个数组,然后给其赋值,默认是以空格为分割符。
    -p “提示信息”:在等待read输入时,输出提示信息。
    -t 秒数:read命令会一直等待用户输入,使用此选项可以指定等待时间。
    -n 数字:read命令只接受指定的字符数,就会执行。
    -s :隐藏输入的数据,适用于机密信息的输入。
    -d : 后面跟一个标志符,其实只有其后的第一个字符有用,作为结束的标志。
    -e : 在输入的时候可以使用命令补全功能。

    变量名:
    变量名可以自定义,如果不指定变量名,会把输入保存入默认变量REPLY ;
    如果只提供了一个变量名,则整个输入行赋予该变量。
    如果提供了一个以上的变量名,则输入行分为若干字,一个接一个地赋予各个变量,而命令行上的最后一个变量取得剩余的所有字。

    示例:

    [root@localhost ~]$ vi read.sh
    #!/bin/bash
    # 提示“请输入姓名”并等待30 秒(超时的话,会获取已输入的内容),把用户的输入保存入变量name 中
    read -t 30 -p "Please input your name: " name
    # 看看变量“$name”中是否保存了你的输入
    echo "Name is $name"
    
    # 提示“请输入年龄”并等待30秒,把用户的输入保存入变量age中,
    # 年龄是隐私,所以我们用“-s”选项隐藏输入
    read -s -t 30 -p "Please enter your age: " age
    # 输出年龄前,先换一行
    echo -e "\n"
    echo "Age is $age"
    
    # 提示“请选择性别”并等待30秒,把用户的输入保存入变量gender
    # 使用“-n 1”选项只接收一个输入字符就会执行(都不用输入回车)
    read -n 1 -t 30 -p "Please select your gender[M/F]:" gender
    # 输出性别前,先换一行
    echo -e "\n"
    echo "Sex is $gender"
    
    [appuser@iZ6uc01r2mgepqzn51on1sZ lk_test]$ ./read.sh 
    #录入及输出结果
    Please input your name: lisi
    Name is lisi
    Please enter your age: 
    
    Age is 20
    Please select your gender[M/F]:M
    
    Sex is M
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    3、shell运算符

    ​ 在shell中,运算符和其他编程脚本语言一样,常见的有算数运算符、关系运算符、逻辑运算符、字符串运算符、文件测试运算符 等。

    3.1、算数运算符

    ​ 原生bash不支持简单的数学运算,但是可以通过其他命令来实现,例如 awk 和 expr,expr 最常用。

    expr 是一款表达式计算工具,使用它能完成表达式的求值操作

    **示例1:**两个数相加(注意使用的是反引号 ` 而不是单引号 ')

    [root@localhost ~]$ vi computer.sh
    #!/bin/bash
    val=`expr 2 + 2`
    echo "两数之和为 : $val"
    #注意
    #表达式和运算符之间要有空格,例如 2+2 是不对的,必须写成 2 + 2,这与我们熟悉的大多数编程语言不一样。
    #完整的表达式要被 ` ` 包含,注意这个字符不是常用的单引号,在 Esc 键下边。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    常用的算术运算符:(假定变量 a 为 10,变量 b 为 20)

    运算符说明示例
    +加法expr $a + $b 结果为 30
    -减法expr $a - $b 结果为 -10
    *乘法expr $a * $b 结果为 200
    /除法expr $b / $a 结果为 2
    %取余expr $b % $a 结果为 0
    =赋值a=$b 将把变量 b 的值赋给 a
    ==相等。用于比较两个数字,相同则返回 true[ $a == $b ] 返回 false
    !=不相等。用于比较两个数字,不相同则返回 true[ $a != $b ] 返回 true

    注意:条件表达式要放在方括号之间,并且要有空格,必须写成 [ $a == $b ]

    示例:

    [root@localhost ~]$ vi computers.sh
    #!/bin/bash
    a=10
    b=20
    echo '算术运算符测试'
    echo 'a+b= ' `expr $a + $b`
    echo 'a-b= ' `expr $a - $b`
    echo 'a*b= ' `expr $a \* $b`
    echo 'a/b= ' `expr $a / $b`
    echo 'a%b= ' `expr $a % $b`
    
    #判断是否相等
    if [ $a == $b ]
    then
    	echo 'a等于b'
    else
    	echo 'a不等于b'
    fi
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    3.1.1、let、expr、[ ] 的使用

    关于这三个的区别和联系,文字说太烦,看达到同样效果,它们各自如何实现:

    a=$[1+1]
    a=$((1+1))
    let a=1+1
    a=$(expr 1 + 1 )
    
    • 1
    • 2
    • 3
    • 4

    3.2、 关系运算符

    ​ 关系运算符只支持数字,不支持字符串,除非字符串的值是数字

    常用的关系运算符:(假定变量 a 为 10,变量 b 为 20)

    关系运算符单词说明示例
    -eqequal检测两个数是否相等,相等返回 true[ $a -eq $b ] 返回 false
    -nenot equal检测两个数是否相等,不相等返回 true[ $a -ne $b ] 返回 true
    -gtgreat than检测左边的数是否大于右边的,
    如果是,则返回 true
    [ $a -gt $b ] 返回 false
    -ltless than检测左边的数是否小于右边的,
    如果是,则返回 true
    [ $a -lt $b ] 返回 true
    -gegreat than or equal检测左边的数是否大于等于右边的,
    如果是,则返回 true
    [ $a -ge $b ] 返回 false
    -leless than or equal检测左边的数是否小于等于右边的,
    如果是,则返回 true
    [ $a -le $b ] 返回 true

    示例:

    [root@localhost ~]$ [ 10 -gt 10 ] 
    [root@localhost ~]$ echo $? #最后一次执行的命令的返回状态。返回0代表执行正确。
     1
    [root@localhost ~]$ [ 10 -eq 10 ] 
    [root@localhost ~]$ echo $? 
     0
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    3.3、逻辑运算符

    常用的布尔运算符:(假定变量 a 为 10,变量 b 为 20)

    逻辑运算符说明示例
    !非运算,表达式为 true 则返回 false,否则返回 true[ ! false ] 返回 true
    -o或(或者)运算,有一个表达式为 true 则返回 true[ $a -lt 20 -o $b -gt 100 ] 返回 true
    -a与(并且)运算,两个表达式都为 true 才返回 true[ $a -lt 20 -a $b -gt 100 ] 返回 false

    3.4、字符串运算符

    常用的字符串运算符:(假定变量 a 为 “abc”,变量 b 为 “efg”)

    运算符说明示例
    =检测两个字符串是否相等,相等返回 true[ $a = $b ] 返回 false
    !=检测两个字符串是否相等,不相等返回 true[ $a != $b ] 返回 true
    -z检测字符串长度是否为0,为0返回 true[ -z $a ] 返回 false
    -n检测字符串长度是否为0,不为0返回 true[ -n $a ] 返回 true
    $检测字符串是否为空,不为空返回 true[ $a ] 返回 true

    示例:

     # 字符串长度是否为0,是则返回0[root@localhost ~]$ a="aaa"
    [root@localhost ~]$ b="bbb"
    [root@localhost ~]$ [ "${a}" = "{$b}" ]
    [root@localhost ~]$ echo "is eaqual:$?"
    is eaqual:1
    [root@localhost ~]$ [ "${a}" != "{$b}" ]
    [root@localhost ~]$ echo "is no  eaqual:$?"
    is no  eaqual:0
    [root@localhost ~]$ [ -z "${a}" ] # 字符串长度是否为0,是则返回0
    [root@localhost ~]$ echo "length is zero : $?"
    length is zero : 1
    [root@localhost ~]$ [ -n "${a}" ] # 字符串长度是否为0,不是则返回0
    [root@localhost ~]$ echo "length is no  zero : $?"
    length is no  zero : 0
    [root@localhost ~]$ [ $a ] # 字符串是否为空,不是则返回0
    [root@localhost ~]$ [root@localhost ~]$ [ $a ]
     echo "is empty:$?"
    is empty:0
    [root@localhost ~]$  [ $"$b" ] # 字符串是否为空,不是则返回0
    [root@localhost ~]$ echo "is empty:$?"
    is empty:0
    [root@localhost ~]$ echo "string length : ${#a}" # 输出变量a的长度
    string length : 3
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    3.5、文件测试运算符(重点)

    文件测试运算符用于检测 Unix/Linux 文件的各种属性。

    操作符说明示例
    -b filename检测文件是否是块设备文件,如果是,则返回 true[ -b $file ] 返回 false
    -c filename检测文件是否是字符设备文件,如果是,则返回 true[ -c $file ] 返回 false
    -d filename检测文件是否是目录,如果是,则返回 true[ -d $file ] 返回 false
    -f filename检测文件**是否是普通文件**(既不是目录,也不是设备文件),如果是,则返回 true[ -f $file ] 返回 true
    -g filename检测文件是否设置了 SGID 位,如果是,则返回 true[ -g $file ] 返回 false
    -k filename检测文件是否设置了粘着位(Sticky Bit),如果是,则返回 true[ -k $file ] 返回 false
    -p filename检测文件是否是有名管道,如果是,则返回 true[ -p $file ] 返回 false
    -u filename检测文件是否设置了 SUID 位,如果是,则返回 true[ -u $file ] 返回 false
    -r filename检测文件是否可读,如果是,则返回 true[ -r $file ] 返回 true
    -w filename检测文件是否可写,如果是,则返回 true[ -w $file ] 返回 true
    -x filename检测文件是否可执行,如果是,则返回 true[ -x $file ] 返回 true
    -s filename检测文件是否为空(文件大小是否大于0),不为空返回 true[ -s $file ] 返回 true
    -e filename检测文件(包括目录)是否存在,如果是,则返回 true[ -e $file ] 返回 true

    4、流程控制

    4.1、if条件判断

    4.1.1、单分支if条件

    语法:

    if [ 条件判断表达式 ]
    	then
    		程序
    fi
    
    • 1
    • 2
    • 3
    • 4

    示例1:统计根分区使用率

    [root@localhost ~]$ vi test_if_1.sh
    #!/bin/bash
    
    #统计根分区使用率
    rate=$(df -h | grep "/dev/vda1" | awk '{print $5}| cut -d "%" -f1)
    #把根分区使用率作为变量值赋予变量rate
    if [ $rate -ge 80 ]
    #判断rate的值如果大于等于80,则执行then程序
    	then
    		#打印警告信息。在实际工作中,也可以向管理员发送邮件。
    		echo "Warning!/dev/vda1 is fu11!!"	
    fi
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    示例2:创建目录

    [root@localhost ~]$ vi add_dir.sh
    #!/bin/bash
    #创建目录,判断是否存在,存在就结束,反之创建
    echo "当前脚本名称为$0"
    DIR="/media/cdrom"
    if [ ! -e $DIR ]
    then
    	mkdir -p $DIR
    fi
    echo "$DIR 创建成功"
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    4.1.2、双分支if条件语句

    语法:

    if [ 条件判断式 ]
    	then
    		条件成立时,执行的程序
    	else
    		条件不成立时,执行的另一个程序
    fi
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    4.1.3、多分支if条件语句

    语法:

    if [ 条件判断式1 ]
    	then
    		当条件判断式1成立时,执行程序1
    elif [ 条件判断式2 ]
    	then
    		当条件判断式2成立时,执行程序2
    …省略更多条件…
    else
    	当所有条件都不成立时,最后执行此程序
    fi
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    4.2、多分支case条件语句

    ​ case语句和if…elif…else语句一样都是多分支条件语句,不过和if多分支条件语句不同的是,case语句只能判断一种条件关系,而if语句可以判断多种条件关系。

    语法:

    case $变量名 in
    	"值1")
            如果变量的值等于值1,则执行程序1
            ;;
    	"值2")
            如果变量的值等于值2,则执行程序2
            ;;
    	…省略其他分支…
    	*)
            如果变量的值都不是以上的值,则执行此程序
            ;;
    esac
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    示例:

    [root@localhost ~]$ vi if-case.sh
    #!/bin/bash
    read -p "请输入一个字符,并按Enter确认:" KEY
    case "$KEY" in
    	[a-z]|[A-Z])
            echo "您输入的是字母"
            ;;
    	[0-9])
    		echo "您输入的是数字"
    		;;
    	*)
            echo "您输入的是其他字符"
            ;;
    esac
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    4.3、for循环

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

    语法1:

    • 这种语法中for循环的次数,取决于in后面值的个数(空格分隔),有几个值就循环几次,并且每次循环都把值赋予变量。
    for 变量 in 值1 值2 值3 …(可以是一个文件等)
    	do
    		程序
    	done
    
    • 1
    • 2
    • 3
    • 4

    语法2:

    • **初始值:**在循环开始时,需要给某个变量赋予初始值,如i=1;
    • 循环控制条件:用于指定变量循环的次数,如i<=100,则只要i的值小于等于100,循环就会继续;
    • 变量变化:每次循环之后,变量该如何变化,如i=i+1。代表每次循环之后,变量i的值都加1。
    for (( 初始值;循环控制条件;变量变化 ))
    	do
    		程序
    	done
    
    • 1
    • 2
    • 3
    • 4

    示例1:批量解压缩

    [root@localhost ~]$ vi auto-tar.sh
    #!/bin/bash
    #批量解压脚本
    #进入压缩包目录
    cd /lamp
    #把所有.tar.gz结尾的文件的文件覆盖到ls.log 临时文件中
    ls *.tar.gz > ls.log
    #或者这样写for i in `cat ls.log`
    for i in $(cat ls.log) `
    #读取ls.log文件的内容,文件中有多少个值,就会循环多少次,每次循环把文件名赋予变量i
    	do
    		tar -zxf $i &>/dev/null
    		#解压缩,并把所有输出都丢弃
    	done
    #删除临时文件ls.log
    rm -rf /lamp/ls.log
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    示例2:批量添加指定数量的用户

    [root@localhost ~]$ vi useradd.sh
    #!/bin/bash
    #批量添加指定数量的用户
    #让用户输入用户名前缀,把输入保存入变量name
    read -p "Please input user name: " -t 30 name
    
    #让用户输入添加用户的数量,把输入保存入变量num
    read -p "Please input the number of users: " -t 30 num
    
    #让用户输入初始密码,把输入保存入变量pass
    read -p "Please input the password of users: " -t 30 pass
    
    #判断三个变量不为空
    if [ ! -z "$name" -a ! -z "$num"-a ! -z "$pass"]
    	then
    		# 定义变量的值为后续命令的结果。
    		# 后续命令作用是:把变量num的值中的数字替换为空。如果能替换为空,证明num的值为数字;如果不能替换为空,证明num的值为非数字。
    		y=$(echo $num | sed 's/[0-9]//g')
    		#如果变量y的值为空,证明num变量是数字
    		if [ -z "$y"]
    			then
    				#循环num变量指定的次数
    				for (( i=1 ; i<=$num; i=i+1 ))
    				do
    					/usr/sbin/useradd $name$i &>/dev/null
    					#添加用户,用户名为变量name 的值加变量i的数字
    					echo $pass | /usr/bin/passwd --stdin $name$i &>/dev/null
    					#给用户设定初始密码为变量pass 的值
    				done
    			else 
    				echo "num 必须为纯数字!!!"
    		fi
    	else 
    		echo "name、num、password 均不能为空!!!"
    fi
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    示例3:批量删除用户

    [root@localhost ~]$ vi sh/userdel.sh
    #!/bin/bash
    #批量删除用户
    #读取用户信息文件,提取可以登录用户,取消root用户,截取第一列用户名
    user=$(cat /etc/passwd | grep "/bin/bash"|grep -v "root"|cut -d ":" -f 1)
    #循环,有多少个普通用户,循环多少次
    for i in $user
    	do
    		#每次循环,删除指定普通用户
    		userdel -r $i	
    	done
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    4.4、 while循环

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

    语法:

    while [ 条件判断式 ]
    	do
    		程序
    	done
    
    • 1
    • 2
    • 3
    • 4

    示例:输入的数值进行比较判断

    [root@localhost ~]$ vi addnum.sh
    #!/bin/bash
    
    # 说明:expr是一款表达式计算工具,使用它能完成表达式的求值操作
    PRICE=$(expr $RANDOM % 1000)
    TIMES=0
    
    echo "商品的价格为0-999之间,猜猜看是多少?"
    while true
    do
      	read -p "请输入您猜的价格:" INT
      	# let是用于计算的工具,用于执行一个或多个表达式。在计算中不用加$来表示变量。
      	let TIMES++
    	if [ $INT -eq $PRICE ] 
    		then
    	  		echo "恭喜您猜对了,实际价格是 $PRICE"
    	  		echo "您总共猜了 $TIMES 次"
    			exit 0
    	elif [ $INT -gt $PRICE ] 
    		then
    	 		echo "太高了"
    	else
    	  echo "太低了"
    	fi
    done
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    4.5、until循环

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

    语法:

    until [ 条件判断式 ]
    	do
    		程序
    	done
    
    • 1
    • 2
    • 3
    • 4

    示例:从1加到100

    [root@localhost ~]$ vi sh/until.sh
    #!/bin/bash
    #从1加到100
    #给变量i和变量s赋值
    i=1
    s=0
    #循环直到变量i的值大于100,就停止循环
    until [ $i -gt 100 ]
    	do
    		s=$(( $s+$i ))
    		i=$(( $i+1 ))
    	done
    echo "The sum is: $s"
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    4.6、函数

    语法:

    function 函数名 () {
    	程序
    }
    
    • 1
    • 2
    • 3

    示例:接收用户输入的数字,然后从1加到这个数字

    [root@localhost ~]$ vi function.sh
    #!/bin/bash
    #接收用户输入的数字,然后从1加到这个数字
    
    #定义函数sum
    function sum () {
    	s=0
    	#循环直到i大于$1为止。 $1 是函数sum 的第一个参数。
    	#在函数中也可以使用位置参数变量,不过这里的$1指的是函数的第一个参数
    	for (( i=0; i<=$num;i=i+1 ))		
    		do
    			s=$(( $i+$s ))
    		done
    	#输出1加到$1的和
    	echo "The sum of 1+2+3...+$1 is :$s"
    }
    
    #接收用户输入的数字,并把值赋予变量num
    read -p "Please input a number: " -t 30 num
    
    #把变量num值中的数字替换为空,并赋予变量y
    y=$(echo $num | sed 's/[0-9]//g')
    
    #判断变量y是否为空,以确定变量num中是否为数字
    if [ -z "$y"]
    	then
    		#调用sum函数,并把变量num的值作为第一个参数传递给sum函数
    		sum $num		
    else
    	#如果变量num 的值不是数字,则输出报错信息
    	echo "Error!! Please input a number!"	
    fi
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    4.7、特殊流程控制语句

    4.7.1、 exit语句

    ​ 系统是有exit命令的,用于退出当前用户的登录状态。

    ​ 在Shell脚本中,exit语句是用来退出当前脚本的。也就是说,在Shell脚本中,只要碰到了exit语句,后续的程序就不再执行,而直接退出脚本

    exit的语法: exit [返回值]

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

    示例:

    [root@localhost ~]$ vi exit.sh
    #!/bin/bash
    #演示exit的作用
    
    #接收用户的输入,并把输入赋予变量num
    read -p "Please input a number: " -t 30 num
    #如果变量num的值是数字,则把num的值替换为空,否则不替换。并把替换之后的值赋予变量y
    y=$(echo $num | sed 's/[0-9]//g')
    
    #判断变量y的值如果不为空,输出报错信息,退出脚本,退出返回值为18
    [ -n "$y" ] && echo "Error! Please input a number!" && exit 18
    
    #如果没有退出,则打印变量num中的数字
    echo "The number is: $num"
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    4.7.2、break语句

    ​ 当程序执行到break语句时,会结束整个当前循环。而continue 语句也是结束循环的语句,不过continue 语句是结束单次当前循环,而下次循环会继续。

    [root@localhost ~]$ vi break.sh
    #!/bin/bash
    #演示break 跳出循环
    #循环十次
    for (( i=1;i<=10; i=i+1 ))
        do
            #如果变量i的值等于4,方括号前中前后要有空格。
            if [ $i -eq 4 ]; then
                #退出整个循环
                break
            fi
            #输出变量i的值
            echo $i
        done
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    4.7.3、continue语句

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

    [root@localhost ~]$ vi continue.sh
    #!/bin/bash
    #演示continue
    #循环十次
    for (( i=1;i<=10;i=i+1 ))
    	do
    		#如果变量i的值等于4
    		if [ "$i" -eq 4 ]
    			then
    			#退出换成continue
    			continue			
    		fi
    		#输出变量i的值
    		echo $i	
    	done
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    5、字符截取、替换和处理命令

    5.1、正则表达式

    元字符描述示例
    \转义符,将特殊字符进行转义,忽略其特殊意义a.b匹配a.b,但不能匹配ajb,.被转义为特殊意义
    ^匹配行首,awk中,^则是匹配字符串的开始^tux匹配以tux开头的行
    $匹配行尾,awk中,$则是匹配字符串的结尾tux$匹配以tux结尾的行
    .匹配除换行符\n之外的**任意单个字符**ab.匹配abc或abd,不可匹配abcd或abde,只能匹配单字符
    [ ]匹配包含在**[指定字符]之中的任意一个字符**coo[kl]可以匹配cook或cool
    [^]匹配**[^指定字符]之外的任意一个字符**123[^45]不可以匹配1234或1235,1236、1237都可以
    [-]匹配[]中**指定范围内的任意一个字符**,要写成递增[0-9]可以匹配1、2或3等其中任意一个数字
    ?匹配之前的项1次或者0次colou?r可以匹配color或者colour,不能匹配colouur
    元字符描述示例
    \转义符,将特殊字符进行转义,忽略其特殊意义a.b匹配a.b,但不能匹配ajb,.被转义为特殊意义
    ^以什么开头^tux匹配以tux开头的行
    $以什么结尾tux$匹配以tux结尾的行
    .匹配除换行符\n之外的**任意单个字符**ab.匹配abc或abd,不可匹配abcd或abde,只能匹配单字符
    [ ]匹配包含在**[指定字符]之中的任意一个字符**coo[kl]可以匹配cook或cool
    [^]匹配**[^指定字符]之外的任意一个字符**123[^45]不可以匹配1234或1235,1236、1237都可以
    [-]匹配[]中**指定范围内的任意一个字符**,要写成递增[0-9]可以匹配1、2或3等其中任意一个数字
    ?匹配之前的单个字符1次或者0次colou?r可以匹配color或者colour,不能匹配colouur 。即:u字符匹配0次或1次
    +匹配**之前的单个字符1次或者多次**sa-6+匹配sa-6、sa-666,不能匹配sa-
    *匹配**之前的单个字符0次或者多次**co*l匹配cl、col、cool、coool等
    ()匹配表达式,创建一个用于匹配的子串,即 括号中的字符串 是一个整体ma(tri)?匹配max或maxtrix
    {n}匹配之前的项n次,n是可以为0的正整数[0-9]{3}匹配任意一个三位数,可以扩展为 [0-9][0-9][0-9]
    {n,}之前的项至少需要匹配n次[0-9]{2,}匹配任意一个两位数或更多位数不支持{n,}{n,}{n,}
    {n,m}指定之前的项至少匹配n次,最多匹配m次,n<=m[0-9]{2,5}匹配从两位数到五位数之间的任意一个数字
    |交替匹配|两边的任意一项, 即 或者ab(c|d)匹配abc或abd

    5.2、字符截取、替换命令

    5.2.1、cut 列提取命令

    ​ cut命令的默认分隔符是制表符,也就是“tab”键,不过对空格符支持的不怎么好。

    语法:cut [选项] 文件名
    选项:

    • -f 列号 :提取第几列。
    • -d 分隔符 :按照指定分隔符分割列。
    • -n :取消分割多字节字符。
    • -c 字符范围 :不依赖分隔符来区分列,而是通过字符范围(行首为0)来进行字段提取。
      • “n-”表示从第n个字符到行尾;
      • “n-m”从第n个字符到第m个字符;
      • “-m”表示从第1个字符到第m个字符。
    • –complement 补足被选择的字节、字符或字段
    • –out-delimiter 指定输出内容是的字段分割符

    示例1:切分或提取文件中的内容

    [root@localhost ~]$ vi student.txt
    id	name	gender	mark
    1	liming	m	86
    2	sc	m	67
    3	tg	n	90
    [root@localhost lk_test]$ cut -f 2 student.txt
    #提取第二列内容,输出结果如下:(默认以tab键作为分隔符)
    name
    liming
    sc
    tg
    [root@localhost lk_test]$ cut -f 2,3 student.txt
    # 想要提取多列,只要列号直接用“,”分开,输出结果如下:
    name    gender
    liming  m
    sc      m
    tg      n
    [root@localhost lk_test]$ cut -d ":" -f 1,3 /etc/passwd
    # 以冒号切分文件内容,并提起文件中的 第1、3列,输出结果如下:
    root:0
    bin:1
    daemon:2
    adm:3
    ......此处省略其它用户
    [root@localhost lk_test]$ cut -c 8- student.txt
    # 提取所有行的第8个字符开始到行尾的数据,输出结果如下:
            gender  mark
    g       m       86
    67
    90
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    示例2:切分、提取shell命令结果的数据

    [root@localhost lk_test]$ df -h | cut -d " " -f 1-12
    # 截取df命令的第1-12列的数据,输出结果如下:
    Filesystem      Size  Used Avail Use% Mounted
    /dev/vda1        99G   45G
    devtmpfs        1.9G   
    tmpfs           1.9G
    tmpfs           1.9G
    tmpfs           1.9G
    tmpfs           378M
    tmpfs           378M
    tmpfs           378M
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    5.2.2、awk 编程

    ​ awk是一种编程语言,用于linux下对文本和数据进行处理。数据可以来自标准输入一个或多个文件,或其他命令的输出

    5.2.2.1、printf 格式化输出

    语法:printf ‘输出类型输出格式’ 输出内容

    输出类型:

    • %c :ASCII字符,显示相对应参数的第一个字符。
    • %-ns :输出字符串。
      • 减号“-”表示左对齐(默认右对齐);
      • n是数字指代输出几个字符;几个参数就写几个%-ns
    • %-ni :输出整数,n是数字指代输出几个数字。
    • %m.nf :输出浮点数。
      • m和n是数字,m为宽度,n为小数位数。
      • 如%8.2f代表共输出8位数,其中2位是小数,6位是整数。
    • %a(%A) :浮点数、十六进制数字和p-(P-)记数法(C99)
    • %d :有符号十进制整数。
    • %f :浮点数(包括float和doulbe)
    • %e(%E) :浮点数指数输出[e-(E-)记数法]
    • %g(%G) :浮点数不显无意义的零"0"
    • %i :有符号十进制整数(与%d相同)
    • %u :无符号十进制整数
    • %o :八进制整数 e.g. 0123
    • %x(%X) :十六进制整数0f(0F) e.g. 0x1234
    • %p :指针
    • %s :字符串
    • %% :“%”

    输出格式:

    • \a :输出警告声音。
    • \b :输出退格键,也就是Backspace键。
    • \f :清除屏幕。
    • \n :换行。
    • \r :回车,也就是Enter键。
    • \t :水平输出退格键,也就是Tab 键。
    • \v :垂直输出退格键,也就是Tab 键。

    示例:

    [root@localhost ~]$ vi student2.txt
    ID      Name    php  	 Linux  	MySQL 	  Average
    1       AAA      66         66       66           66
    2       BBB      77         77       77           77
    3       CCC      88         88       88           88
    
    #printf格式输出文件
    [root@localhost ~]$ printf '%s\t %s\t %s\t %s\t %s\t %s\t \n' $(cat student2.txt)
    # %s分别对应后面的参数,6列就写6个
    ID	 Name	 php	 Linux	 MySQL	 Average	 
    1	 AAA	 66	 66	 66	 66	 
    2	 BBB	 77	 77	 77	 77	 
    3	 CCC	 88	 88	 88	 88	
    
    #如果不想把成绩当成字符串输出,而是按照整型和浮点型输出
    [root@localhost ~]$ printf '%i\t %s\t %i\t %i\t %i\t %8.2f\t \n' $(cat student2.txt | grep -v Name)
    #说明: grep -v 参数 :代表显示没有被匹配的行。
    1	 AAA	 66	 66	 66	    66.00	 
    2	 BBB	 77	 77	 77	    77.00	 
    3	 CCC	 88	 88	 88	    88.00
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    5.2.2.2、awk 基本使用

    语法:awk '条件1{动作1} 条件2{动作2}...' 文件名

    **条件(Pattern):**一般使用关系表达式作为条件。这些关系表达式非常多,例如:
    x > 10 判断变量x是否大于10
    x == y 判断变量x是否等于变量y
    A ~ B 判断字符串A中是否包含能匹配B表达式的子字符串
    A !~ B 判断字符串A中是否不包含能匹配B表达式的子字符串

    动作(Action) :

    • 格式化输出
    • 流程控制语句

    常用参数:

    • -F 指定输入时用到的字段分隔符.
    • -v 自定义变量.
    • -f 从脚本中读取awk命令.
    • -m 对val值设置内在限制.

    示例:

    [root@localhost ~]$ awk '{printf $2 "\t" $6 "\n"}' student2.txt
    # 输出第二列和第六列
    Name	Average
    AAA	66
    BBB	77
    CCC	88
    [root@localhost ~]$ df -h | awk '{print $1 "\t" $3}'
    #截取df命令的第一列和第三列
    Filesystem	Used
    /dev/vda1	45G
    devtmpfs	0
    tmpfs	65M
    tmpfs	520K
    tmpfs	0
    tmpfs	0
    tmpfs	0
    tmpfs	0
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    5.2.2.3、awk 的条件
    条件的类型条件说明
    awk保留字BEGIN在awk程序一开始时,尚未读取任何数据之前执行。BEGIN后的动作只在程序开始时执行一次
    awk保留字END在awk程序处理完所有数据,即将结束时执行。END后的动作只在程序结束时执行一次
    关系运算符>大于
    关系运算符<小于
    关系运算符>=大于等于
    关系运算符<=小于等于
    关系运算符==等于。用于判断两个值是否相等,如果是给变量赋值,请使用""号
    关系运算符!=不等于
    关系运算符A~B判断字符串A中是否包含能匹配B表达式的子字符串
    关系运算符A!~B判断字符串A中是否不包含能匹配B表达式的子字符串
    正则表达式/正则/如果在"//"中可以写入字符,也可以支持正则表达式
    BEGIN

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

    [root@localhost ~]$ awk 'BEGIN{printf "This is a transcript \n" } {printf $2 "\t" $6 "\n"}' student2.txt
    #这里定义了两个动作
    #第一个动作使用BEGIN条件,所以会在读入文件数据前打印“这是一张成绩单”(只会执行一次)
    #第二个动作会打印文件的第二字段和第六字段
    This is a transcript 
    Name	Average
    AAA	66
    BBB	77
    CCC	88
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    END

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

    [root@localhost ~]$ awk 'END{printf "The End \n"} {printf $2 "\t" $6 "\n"}' student2.txt
    #在输出结尾输入“The End”,这并不是文档本身的内容,而且只会执行一次
    Name	Average
    AAA	66
    BBB	77
    CCC	88
    The End
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    关系运算符

    示例1:假设我想看看平均成绩大于等于87分的学员是谁。

    [root@localhost ~]$ cat student2.txt | grep -v Name | awk '$6 >= 87 {printf $2 "\n"}'
    #使用cat输出文件内容,用grep取反包含“Name”的行
    #判断第六字段(平均成绩)大于等于87分的行,如果判断式成立,则打第二列(学员名$2)
    CCC
    
    • 1
    • 2
    • 3
    • 4

    ​ 加入了条件之后,只有条件成立动作才会执行,如果条件不满足,则动作则不运行。通过这个示例,可以发

    现,虽然awk是列提取命令,但是也要按行来读入的。

    awk命令的执行过程如下:

    • 1、如果有BEGIN条件,则先执行BEGIN定义的动作。
    • 2、如果没有BEGIN条件,则读入第一行,把第一行的数据依次赋予$0、$1、$2等变量。其中$0代表此行的整体数据,$1代表第一字段,$2代表第二字段。
    • 3、依据条件类型判断动作是否执行。如果条件符合,则执行动作,否则读入下一行数据。如果没有条件,则每行都执行动作。
    • 4、读入下一行数据,重复执行以上步骤。

    示例2:查看sc用户的平均成绩:

    [root@localhost ~]$ awk '$2 ~ /sc/ {printf $4 "\n"}' student.txt
    #如果第二字段中输入包含有“sc”字符,则打印第四字段数据
    67
    
    • 1
    • 2
    • 3

    注意:

    在awk中,使用“//”包含的字符串,awk命令才会查找。即 字符串必须用“//”包含,awk命令才能正确识别

    正则表达式

    ​ 如果要想让awk 识别字符串,必须使用“//”包含。

    示例1:打印liming的成绩

    [root@localhost ~]$ awk '/liming/ {print}' student.txt
    1       liming  m       86
    
    • 1
    • 2

    **例子2:**查看真正的系统分区的使用状况,而不想查看光盘和临时分区的使用状况。

    [root@localhost ~]$ df -h
    Filesystem      Size  Used Avail Use% Mounted on
    /dev/vda1        99G   45G   50G  48% /
    devtmpfs        1.9G     0  1.9G   0% /dev
    tmpfs           1.9G   65M  1.8G   4% /dev/shm
    tmpfs           1.9G  520K  1.9G   1% /run
    tmpfs           1.9G     0  1.9G   0% /sys/fs/cgroup
    tmpfs           378M     0  378M   0% /run/user/0
    tmpfs           378M     0  378M   0% /run/user/1100
    tmpfs           378M     0  378M   0% /run/user/1102
    [root@localhost ~]$ df -h | awk '/vda[0-9]/ {printf $1 "\t" $5 "\n"}'
    /dev/vda1	48%
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    5.2.2.4、awk 内置变量
    awk内置变量作用
    $0代表目前awk所读入的整行数据。我们已知awk是一行一行读入数据的,$0就代表当前读入行的整行数据
    $n代表目前读入行的第n个字段。比如,$1表示第1个字段(列),$2表示第2个字段(列),如此类推
    NF当前行拥有的字段(列)总数
    NR当前awk所处理的行,是总数据的第几行
    FS用户定义分隔符。awk的默认分隔符是任何空格,如果想要使用其他分隔符(如“:”),就需要FS变量定义
    ARGC命令行参数个数
    ARGV命令行参数数组
    FNR当前文件中的当前记录数(对输入文件起始为1)
    OFMT数值的输出格式(默认为%.6g)
    OFS输出字段的分隔符(默认为空格)
    ORS输出记录分隔符(默认为换行符)
    RS输入记录分隔符(默认为换行符)

    awk常用统计实例

    # 1、打印文件的第一列(域) :
    awk '{print $1}' filename
     
    # 2、打印文件的前两列(域) :
    awk '{print $1,$2}' filename
     
    # 3、打印完第一列,然后打印第二列 : 
    awk '{print $1 $2}' filename
    
    # 4、打印文本文件的总行数 : 
    awk 'END{print NR}' filename
    
    # 5、打印文本第一行 :
    awk 'NR==1{print}' filename
    
    # 6、打印文本第二行第一列 :
    sed -n "2, 1p" filename | awk 'print $1'
    
    
    # 1. 获取第一列
    ps -aux | grep watchdog | awk '{print $1}'
    
    # 2. 获取第一列,第二列,第三列
    ps -aux | grep watchdog | awk '{print $1, $2, $3}'
    
    # 3. 获取第一行的第一列,第二列,第三列
    ps -aux | grep watchdog | awk 'NR==1{print $1, $2, $3}'
    
    # 4. 获取行数NR
    df -h | awk 'END{print NR}'
    
    # 5. 获取列数NF(这里是获取最后一行的列数,注意每行的列数可能是不同的)
    ps -aux | grep watchdog | awk 'END{print NF}'
    
    # 6. 获取最后一列
    ps -aux | grep watchdog | awk '{print $NF}'
    
    # 7. 对文件进行操作
    awk '{print $1}' fileName
    
    # 8. 指定分隔符(这里以:分割)
    ps -aux | grep watchdog |awk  -F':' '{print $1}'
    
    # 9. 超出范围不报错
    ps -aux | grep watchdog | awk '{print $100}'
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    # 查询可以登录的用户的用户名和UID;(自定义:为分隔符)
    [root@localhost ~]$ cat /etc/passwd | grep "/bin/bash" | awk '{FS=":"} {printf $1 "\t" $3 "\n"}'
    # 输出结果,发现第一行没有生效。原因:忘记了“BEGIN”条件
    root:x:0:0:root:/root:/bin/bash 
    test6   1000
    
    # 修改后的命令如下
    [root@localhost ~]$ cat /etc/passwd | grep "/bin/bash" | awk 'BEGIN {FS=":"} {printf $1 "\t" $3 "\t 行号:" NR "\t 字段数:" NF "\n"}'
    #开始执行{分隔符是“:”}{输出第一字段和第三字段输出行号(NR值)字段数(NF值)}
    root    0        行号:1  字段数:7
    test6   1000     行号:2  字段数:7
    
    # 如果只想看看sshd这个伪用户的相关信息,则可以这样使用:
    [root@localhost ~]$  cat /etc/passwd | awk 'BEGIN {FS=":"} $1=="sshd" {printf $1 "\t" $3 "\t 行号:" NR "\t 字段数:" NF "\n"}'
    sshd    74       行号:19         字段数:7
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    5.2.2.5、awk 流程控制

    后面还要利用下student2.txt文件做练习,再看看这个文件的内容:

    [root@localhost ~]$  cat student2.txt
    ID      Name    php  	 Linux  	MySQL 	  Average
    1       AAA      66         66       66           66
    2       BBB      77         77       77           77
    3       CCC      88         88       88           88
    
    • 1
    • 2
    • 3
    • 4
    • 5

    **在awk中定义变量与调用变量的值。**假设想统计PHP成绩的总分:

    [root@localhost ~]$ awk 'NR==2 {php1=$3} 
    NR==3 {php2=$3} 
    NR==4 {php3=$3; totle=phpl+php2+php3; print "totle php is " totle}' student2.txt
    # 统计PHIP成绩的总分:
    totle php is 165
    
    • 1
    • 2
    • 3
    • 4
    • 5

    以上命令说明:

    • NR==2 {iphp1=$3} : (条件是NR==2,动作是php1=$3) 这句话是指如果输入数据是第二行(第一行是标题行),就把第二行的第三字段的值赋予变量“php1”。
    • NR==3 {php2=$3} :是指如果输入数据是第三行,就把第三行的第三字段的值赋予变量“php2”。
    • NR==4 {php3=$3;totle=phpl+php2+php3;print “totle php is " totle} :(NR==4是条件,后面(中的都是动作),这句话是指如果输入数据是第四行,就把第四行的第三字段的值赋予变量"php3”;然后定义变量totle的值是“php1+php2+php3”;然后输出“totle php is”关键字,后面加变量totle的值。

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

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

    • 在一个动作中,如果需要执行多个命令,需要用 “;” 分割,或用回车分割。

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

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

    再看下该如何实现流程控制,假设如果Linux成绩大于80,就是一个super man(学PHP的表示压力很大!) :

    [root@localhost ~]$ awk '{if (NR>=2) {if ($4>80) printf $2 " is a super man!\n"}}' student2.txt
    # 程序中有两个if判断,第一个判断行号大于2,第二个判断Linux成绩大于80分
    CCC is a super man!
    
    # 在 awk中 if判断语句,完全可以直接利用awk自带的条件来取代,优化后代码如下:
    [root@localhost ~]$ awk 'NR>=2 {test=$4}
    test>80 {printf $2 " is a super man! \n"}' student2.txt
    # 先判断行号如果大于2,就把第四字段赋予变量test;再判断如果test的值大于80分,就打印super man
    CCC is a super man!
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    5.2.2.6、awk 函数

    ​ awk编程也允许在编程时使用函数,awk函数的定义方法如下:

    function 函数名(参数列表){
    	函数体
    }
    
    • 1
    • 2
    • 3

    **示例1:**定义一个简单的函数,使用函数来打印student2.txt的学员姓名和平均成绩.

    [root@localhost ~]$ awk 'function test(a,b) { 
    		#定义函数test,包含两个参数,函数体的内容是输出这两个参数的值
    		printf a "\t" b "\n" 
    	}
    	# 调用test函数,并向两个参数传递值。
    	{ test($2,$6) } 
    	' student2.txt
    # 输出结果:
    Name	Average
    AAA	66
    BBB	77
    CCC	88
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    5.2.2.7、awk 中调用脚本

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

    # 编写一个awk脚本
    [root@localhost ~]$ vi pass.awk
    BEGIN {FS=":"}
    { print $1 "\t"  $3}
    
    # 使用“-f”选项来调用这个脚本:
    [root@localhost ~]$  awk -f pass.awk /etc/passwd
    root    0
    bin     1
    daemon  2
    ......此处省略部分输出
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    5.2.3、sed 文本选取、替换、删除、新增的命令

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

    语法:sed [选项] ‘[动作]’ 文件名

    选项:

    • -i :直接对原文件进行操作。
    • -r :支持扩展正则表达式。
    • -n :只显示匹配处理的行。不加-n的时候,sed处理的数据和不处理的都会输出。
    • -e :执行多个编辑命令时(一般用;代替)
    • -f 脚本文件名 :执行脚本文件中的命令,脚本文件中一行一条命令。

    动作:

    • a\ :在当前行下面插入文本; a前面可以跟行号、正则,等匹配规则。
    • i\ :在当前行上面插入文本;
    • c\ :把选定的行改为新的文本; 如:70c 为第70行, /cs/c 代表包含cs的行。
    • d :删除,删除选择的行;
    • D :删除模板块的第一行;
    • s :字串替换,用一个字符串替换另外一个字符串。(匹配正则表达式)
      • 格式为 ‘行范围s**/旧字串/**新字串/g’(和vim中的替换格式类似)。
    • h :拷贝模板块的内容到内存中的缓冲区;
    • H :追加模板块的内容到内存中的缓冲区;
    • g :获得内存缓冲区的内容,并替代当前模板块中的文本;
    • G :获得内存缓冲区的内容,并追加到当前模板块文本的后面;
    • l :列表不能打印字符的清单;
    • n :读取下一个输入行,用下一个命令处理新的行而不是用第一个命令;
    • N :追加下一个输入行到模板块后面并在二者间嵌入一个新行,改变当前行号码;
    • p :打印匹配行。 P(大写) 打印模板块的第一行; 如:
      • 1,5p:输出1到5行;
      • $p:最后1行;
      • 3,5!p:取反输出除3~5行之外的行;
      • 2~3p,每隔3行取2行
    • q :退出Sed;
    • b lable :分支到脚本中带有标记的地方,如果分支不存在则分支到脚本的末尾;
    • r file :从file中读行;
    • t label :if分支,从最后一行开始,条件一旦满足或者T,t命令,将导致分支到带有标号的命令处,或者到脚本的末尾;
    • T label :错误分支,从最后一行开始,一旦发生错误或者T,t命令,将导致分支到带有标号的命令处,或者到脚本的末尾;
    • w file :写并追加模板块到file末尾;
    • W file :写并追加模板块的第一行到file末尾;
    • ! :表示后面的命令对所有没有被选定的行发生作用;
    • = :打印当前行号;
    • # :把注释扩展到下一个换行符以前;

    sed替换标记:

    • g 表示行内全面替换;

    • p 表示打印行;

    • w 表示把行写入一个文件;

    • x 表示互换模板块中的文本和缓冲区中的文本;

    • y 表示把一个字符翻译为另外的字符(但是不用于正则表达式);

    • \1 子串匹配标记;

    • & 已匹配字符串标记;

    sed元字符集:

    • ^ 匹配行开始,如:/^sed/匹配所有以sed开头的行;

    • $ 匹配行结束,如:/sed$/匹配所有以sed结尾的行;

    • . 匹配一个非换行符的任意字符,如:/s.d/匹配s后接一个任意字符,最后是d;

    • 匹配0个或多个字符,如:/*sed/匹配所有模板是一个或多个空格后紧跟sed的行;

    • [] 匹配一个指定范围内的字符,如/[ss]ed/匹配sed和Sed;

    • [^] 匹配一个不在指定范围内的字符,如:/[^A-RT-Z]ed/匹配不包含A-R和T-Z的一个字母开头,紧跟ed的行;

    • 匹配子串,保存匹配的字符,如s/(love)able/\1rs,loveable被替换成lovers;

    • & 保存搜索字符用来替换其他字符,如s/love/&/,love这成love;

    • \< 匹配单词的开始,如:/\<

    • \> 匹配单词的结束,如**/love\>/** 该正则匹配包含以love结尾的单词的行;

    • x{m} 重复字符x,m次,如:/0{5}/匹配包含5个0的行;

    • x{m,} 重复字符x,至少m次,如:/0{5,}/匹配至少有5个0的行;

    • x{m,n} 重复字符x,至少m次,不多于n次,如:/0{5,10}/匹配5~10个0的行;

    注意:

    • sed所做的修改并不会直接改变文件的内容(如果是用管道符接收的命令的输出,这种情况连文件都没有),而是把修改结果只显示到屏幕上,除非使用 -i 选项才会直接修改文件。
    5.2.3.1、提取行数据

    ​ 假设想查看下student2.txt的第二行,那么就可以利用“p”动作了:

    [root@localhost ~]$ $ sed -n '2p' student2.txt
    # 指定输出某行,使用-n选项
    1       AAA      66         66       66           66
    
    • 1
    • 2
    • 3
    5.2.3.2、删除行数据
    [root@localhost ~]$ sed '2,4d' student2.txt
    #删除第二行到第四行数据
    ID      Name    php  	 Linux  	MySQL 	  Average
    
    • 1
    • 2
    • 3
    5.2.3.3、追加、插入行数据
    [root@localhost ~]$ sed '2a hello' student2.txt
    #在第二行后加入一行,内容为hello
    ID      Name    php  	 Linux  	MySQL 	  Average
    1       AAA      66         66       66           66
    hello
    2       BBB      77         77       77           77
    3       CCC      88         88       88           88
    
    [root@localhost ~]$ sed '2i hello world' student2.txt
    #在第二行前插入一行数据,内容为hello world
    ID      Name    php  	 Linux  	MySQL 	  Average
    hello world
    1       AAA      66         66       66           66
    2       BBB      77         77       77           77
    3       CCC      88         88       88           88
    
    [root@localhost ~]$ sed '1a\hello1\nhello2\nhello3' student2.txt
    #在第1行后加入多行数据,
    ID      Name    php      Linux          MySQL     Average
    hello1
    hello2
    hello3
    1       AAA      66         66       66           66
    2       BBB      77         77       77           77
    3       CCC      88         88       88           88
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    5.2.3.4、 替换行数据

    假设AAA的成绩太好了,我实在是不想看到他的成绩刺激我,那就可以使用"c"动作。

    [root@localhost ~]$ sed '2c No such person' student2.txt 
    # 将第二行数据替换为:No such person
    ID      Name    php      Linux          MySQL     Average
    No such person
    2       BBB      77         77       77           77
    3       CCC      88         88       88           88
    [root@localhost ~]$ sed -i '2c No such person' student2.txt
    # 使用-i直接修改文件
    [root@localhost ~]$ cat student2.txt
    ID      Name    php      Linux          MySQL     Average
    No such person
    2       BBB      77         77       77           77
    3       CCC      88         88       88           88
    # 恢复原AAA成绩
    [root@localhost ~]$ sed -i '2c 1       AAA      66         66       66           66' student2.txt
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    5.2.3.5、字符串替换

    ​ “c”动作是进行整行替换的,如果只想替换行中的部分数据,就要使用“s”动作了。g 使得 sed 对文件中所有符合的字符串都被替换, 修改后内容会到标准输出,不会修改原文件。

    ​ 写法:sed '行范围s/旧字串/新字串/g' 文件名

    # 假设想作弊给BBB用户成绩改高点:
    [root@localhost ~]$ sed '3s/77/99/g' student2.txt
    #在第三行中,把77换成99
    ID      Name    php      Linux          MySQL     Average
    1       AAA      66         66       66           66
    2       BBB      99         99       99           99
    3       CCC      88         88       88           88
    
    # 注释掉用户AAA的成绩
    [root@localhost ~]$ sed '2s/^/#/g' student2.txt
    #这里使用正则表达式,“^”代表行首
    ID      Name    php      Linux          MySQL     Average
    #1       AAA      66         66       66           66
    2       BBB      77         77       77           77
    3       CCC      88         88       88           88
    
    [root@localhost ~]$ sed -e 's/AAA//g ; s/BBB//g' student2.txt
    #同时把“AAA”和“BBB”替换为空
    ID      Name    php      Linux          MySQL     Average
    1             66         66       66           66
    2             77         77       77           77
    3       CCC      88         88       88           88
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    5.3、字符处理命令

    5.3.1、sort 排序命令

    语法:sort [选项] 文件名

    选项:

    • -f :忽略大小写。
    • -b :忽略每行前面的空白部分。
    • -n :以数值型进行排序,默认使用字符串型排序。
    • -r :反向排序。
    • -u :删除重复行。就是uniq命令。
    • -t :指定分隔符,默认是分隔符是制表符。
    • -k n[,m] :按照指定的字段范围排序。从第n字段开始,到m字段结束(默认到行尾)。此处[]代表m为非必输。
    # sort命令默认是用每行开头第一个字符来进行排序的。
    
    [root@localhost ~]$ sort /etc/passwd
    #排序用户信息文件
    abrt:x:173:173::/etc/abrt:/sbin/nologin
    adm:x:3:4:adm:/var/adm:/sbin/nologin
    bin:x:1:1:bin:/bin:/sbin/nologin
    daemon:x:2:2:daemon:/sbin:/sbin/nologin
    ......此处省略部分输出
    
    [root@localhost ~]$ sort -r /etc/passwd
    # 反向排序
    tss:x:59:59:Account used by the trousers package to sandbox the tcsd daemon:/dev/null:/sbin/nologin
    test6:x:1000:1000::/home/test6:/bin/bash
    systemd-network:x:192:192:systemd Network Management:/:/sbin/nologin
    ......此处省略部分输出
    
    # 指定排序的字段,使用“-t”指定分隔符,并使用“-k”选项指定字段号。加入我想要按照UID字段排序/etc/passwd文件:
    [root@localhost~]$ sort -t ":" -k 3,3 /etc/passwd
    # 指定分隔符是“:”,用第三字段开头,第三字段结尾排序,就是只用第三字段排序.
    # 第一个字符先排序,如果一致,第二个字符再排序.
    root:x:0:0:root:/root:/bin/bash
    bin:x:1:1:bin:/bin:/sbin/nologin
    test6:x:1000:1000::/home/test6:/bin/bash
    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
    ......此处省略部分输出
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    5.3.2、uniq 取消重复行

    语法:uniq [选项] 文件名
    选项:

    • -i :忽略大小写。
    [root@localhost ~]$ cat student.txt
    # 查看文件内容
    id      name    gender  mark
    no such person
    no such person
    No such person
    2       sc      m       67
    3       tg      n       90
    [root@localhost ~]$ uniq student.txt
    # 取消重复行,默认区分大小写。
    id      name    gender  mark
    no such person
    No such person
    2       sc      m       67
    3       tg      n       90
    [root@localhost ~]$ uniq -i student.txt
    # 取消重复行,-i 忽略大小写。
    id      name    gender  mark
    no such person
    2       sc      m       67
    3       tg      n       90
    [root@localhost ~]$ cat student.txt
    # 查看文件内容, 原文件内容未发生变化。
    id      name    gender  mark
    no such person
    no such person
    No such person
    2       sc      m       67
    3       tg      n       90
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    5.3.3、wc 统计命令

    语法:wc [选项] 文件名
    选项:

    • -l :只统计行数。
    • -w :只统计单词数。
    • -m :只统计字符数。
    [root@localhost ~]$ wc student.txt
    # student.txt文件有: 6行,21个单词,85个字符。 
     6 21 85 student.txt
    adm:x:3:4:adm:/var/adm:/sbin/nologin
    bin:x:1:1:bin:/bin:/sbin/nologin
    daemon:x:2:2:daemon:/sbin:/sbin/nologin
    ......此处省略部分输出
    
    [root@localhost ~]$ sort -r /etc/passwd
    # 反向排序
    tss:x:59:59:Account used by the trousers package to sandbox the tcsd daemon:/dev/null:/sbin/nologin
    test6:x:1000:1000::/home/test6:/bin/bash
    systemd-network:x:192:192:systemd Network Management:/:/sbin/nologin
    ......此处省略部分输出
    
    # 指定排序的字段,使用“-t”指定分隔符,并使用“-k”选项指定字段号。加入我想要按照UID字段排序/etc/passwd文件:
    [root@localhost~]$ sort -t ":" -k 3,3 /etc/passwd
    # 指定分隔符是“:”,用第三字段开头,第三字段结尾排序,就是只用第三字段排序.
    # 第一个字符先排序,如果一致,第二个字符再排序.
    root:x:0:0:root:/root:/bin/bash
    bin:x:1:1:bin:/bin:/sbin/nologin
    test6:x:1000:1000::/home/test6:/bin/bash
    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
    ......此处省略部分输出
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    5.3.2、uniq 取消重复行

    语法:uniq [选项] 文件名
    选项:

    • -i :忽略大小写。
    [root@localhost ~]$ cat student.txt
    # 查看文件内容
    id      name    gender  mark
    no such person
    no such person
    No such person
    2       sc      m       67
    3       tg      n       90
    [root@localhost ~]$ uniq student.txt
    # 取消重复行,默认区分大小写。
    id      name    gender  mark
    no such person
    No such person
    2       sc      m       67
    3       tg      n       90
    [root@localhost ~]$ uniq -i student.txt
    # 取消重复行,-i 忽略大小写。
    id      name    gender  mark
    no such person
    2       sc      m       67
    3       tg      n       90
    [root@localhost ~]$ cat student.txt
    # 查看文件内容, 原文件内容未发生变化。
    id      name    gender  mark
    no such person
    no such person
    No such person
    2       sc      m       67
    3       tg      n       90
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    5.3.3、wc 统计命令

    语法:wc [选项] 文件名
    选项:

    • -l :只统计行数。
    • -w :只统计单词数。
    • -m :只统计字符数。
    [root@localhost ~]$ wc student.txt
    # student.txt文件有: 6行,21个单词,85个字符。 
     6 21 85 student.txt
    
    • 1
    • 2
    • 3
  • 相关阅读:
    ts的接口和对象类型
    动态分区分配算法
    每天五分钟深度学习:解决for循环效率慢的关键在于向量化
    一、基本remix环境及HelloWord contract《2022 solidity8.+ 版本教程到实战》
    22年7月工作笔记整理(前端)
    学习笔记14--自动驾驶域接口
    解决springboot启动jar包时加载了jar包内的配置文件
    前端 Javascript 编程中的闭包知识点有哪些?
    如何使用 Yolov4 训练人脸口罩检测模型
    intellij idea的快速配置详细使用
  • 原文地址:https://blog.csdn.net/weixin_40482816/article/details/133780991