Shell编程

一、Shell概述

  • Shell是一个命令行解释器,它为用户提供了一个向Linux内核发送请求以便运行程序的界面系统级程序,用户可以用Shell来启动、挂起、停止甚至是编写一些程序。

  • Shell还是一个功能相当强大的编程语言,易编写,易调试,灵活性较强。Shell是解释执行的脚本语言,在Shell中可以直接调用Linux系统命令。

二、Shell脚本的执行方式

1、echo输出命令
echo [选项] [输出内容]
选项: 
    -e:支持反斜线控制的字符转换
控制字符 作用
\ \ 输出\本身
\a 输出警告音
\b 退格键,也就是向左删除键
\c 取消输出行末的换行符。和“-n”选项一致
\e ESCAPE键
\f 换页符
\n 换行符
\r 回车键
\t 制表符,也就是Tab键
\v 垂直制表符
\0nnn 按照八进制ASCII码表输出字符。其中0为数字零,nnn是三位八进 制数
\xhh 按照十六进制ASCII码表输出字符。其中hh是两位十六进制数
[root@peiqi ~]# echo -e "ab\bc"
ac
[root@peiqi ~]# echo -e "a\tb\tc\nd\te\tf"
a	b	c
d	e	f
[root@peiqi ~]# echo -e "\e[1;31m你好吗\e[0m" 
# 输出带颜色的结果
#30m=黑色,31m=红色,32m=绿色,33m=黄色 
#34m=蓝色,35m=洋红,36m=青色,37m=白色
2、第一个脚本
[root@peiqi ~]# vim helloworld.sh
#!/bin/Bash
echo "hello world"
3、脚本执行

两种方式:

  • 赋予执行权限,直接运行

    chmod 755 helloworld.sh
    ./helloworld.sh
    
  • 通过Bash调用执行脚本

    bash helloworld.sh
    

三、Bash的基本功能

1、历史命令

history [选项] [历史命令保存文件] 
选项:
	-c:清空历史命令 
	-w:把缓存中的历史命令写入历史命令保存文件 ~/.bash_history 

历史命令默认会保存1000条,可以在环境变量配置文件 /etc/profile 中进行修改。

历史命令的调用:
  • 使用上、下箭头调用以前的历史命令
  • 使用 !n 重复执行第n条历史命令
  • 使用 !! 重复执行上一条命令
  • 使用 !字串 重复执行最后一条以该字串开头的命令

2、命令别名

# 设定命令别名
alias 别名=原命令
# 查询命令别名
alias
# 删除别名
unalias 别名
# 让别名永久生效
vim /root/.bashrc

3、常用快捷键

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

4、输出重定向

标准输入输出:
设备 设备文件名 文件描述符 类型
键盘 /dev/stdin 0 标准输入
显示器 /dev/stdout 1 标准输出
显示器 /dev/stderr 2 标准错误输出
输出重定向
类 型 符 号 作用
标准输出重定向 命令 > 文件 以覆盖的方式,把命令的正确输出输出到指定的文件或设备当中。
标准输出重定向 命令 >> 文件 以追加的方式,把命令的正确输出输出到指定的文件或设备当中。
标准错误输出重定向 错误命令 2> 文件 以覆盖的方式,把命令的错误输出输出到指定的文件或设备当中。
标准错误输出重定向 错误命令 2>> 文件 以追加的方式,把命令的错误输出输出到指定的文件或设备当中。
命令 > 文件 2>&1 以覆盖的方式,把正确输出和错误输出都保存到同一个文件当中。
命令 >> 文件 2>&1 以追加的方式,把正确输出和错误输出都保存到同一个文件当中。
正确输出和错误输出同时保存 命令 &> 文件 以覆盖的方式,把正确输出和错误输出都保存到同一个文件当中。
命令 &>> 文件 以追加的方式,把正确输出和错误输出都保存到同一个文件当中。
命令 >> 文件1 2>>文件2 把正确的输出追加到文件1中,把错误的输出追加到文件2中。

5、多命令顺序执行

多命令执行符 格式 作用
; 命令1 ; 命令2 多个命令顺序执行,命令之间没有任何逻辑联系
&& 命令1 && 命令2 逻辑与。当命令1正确执行,则命令2才会执行;当命令1执行不正确,则命令2不会执行
|| 命令1 || 命令2 逻辑或。当命令1执行不正确,则命令2才会执行;当命令1正确执行,则命令2不会执行
[root@peiqi ~]# ls ; date
helloworld.sh
Sat Aug 15 11:51:42 CST 2020
[root@peiqi ~]# ls && echo yes
helloworld.sh
yes
[root@peiqi ~]# ls 1 || echo no
ls: cannot access 1: No such file or directory
no

6、管道符

命令1 | 命令2
# 命令1的正确输出作为命令2的操作对象 
[root@peiqi ~]# ps -ef | grep bash | grep -v "grep"
root     12026 12023  0 11:03 pts/1    00:00:00 -bash
1000     23691 22856  0 Jul26 ?        00:00:00 bash
grep [选项] 搜索内容
选项: 
	-i:忽略大小写 
	-n:输出行号 
	-v:反向查找 
	--color=auto:搜索出的关键字用颜色显示

7、特殊符号

符号 作用
'' 单引号。在单引号中所有的特殊符号,如 $ 和` 都没有特殊含义
"" 双引号。在双引号中特殊符号都没有特殊含义,但是 $ 和` 和 \ 是例外,拥有 “调用变量的值”、“引用命令” 和 “转义符” 的特殊含义
`` 反引号。反引号括起来的内容是系统命令,在Bash中会先执行它。$()作用一样,不过推荐使用 $(),因为反引号非常容易看错
$() 和反引号作用一样,用来引用系统命令
# 在Shell脚本中,# 开头的行代表注释
$ 用于调用变量的值,如需要调用变量name的值时,需要用$name的方式得到变量的值
\ 转义符,跟在 \ 之后的特殊符号将失去特殊含义,变为普通字符。如 \$ 将输出 $ 符号,而不当做是变量引用
# 反引号与$()
[root@peiqi ~]# echo `ls`
a b c
[root@peiqi ~]# echo $(date)
Sat Aug 15 12:12:25 CST 2020
# 单引号与双引号
[root@peiqi ~]# name=pd
[root@peiqi ~]# echo '$name'
$name
[root@peiqi ~]# echo "$name"
pd
[root@peiqi ~]# echo '$(date)'
$(date)
[root@peiqi ~]# echo "$(date)"
Sat Aug 15 12:13:43 CST 2020

四、Bash的变量

1、用户自定义变量

变量是计算机内存的单元,其中存放的值可以改变。当Shell脚本需要保存一些信息时,如一个文件名或是一个数字,就把它存放在一个变量中。每个变量有一个名字,所以很容易引用它。使用变量可以保存有用信息,使系统获知用户相关设置,变量也可以用于保存暂时信息。

变量设置规则:
  • 变量名称可以由字母、数字和下划线组成,但是不能以数字开头,如果变量名是 2name 则是错误的
  • 在Bash中,变量的默认类型都是字符串型,如果要进行数值运算,则必修指定变量类型为数值型
  • 变量用等号连接值,等号左右两侧不能有空格
  • 变量的值如果有空格,需要使用单引号或双引号括起来
  • 在变量的值中,可以使用 \ 转义符
  • 如果需要增加变量的值,那么可以进行变量值的叠加。不过变量需要用双引号包含 "$变量名" 或用 ${变量名} 包含
  • 如果是把命令的结果作为变量值赋予变量,则需要使用 反引号 或 $() 包含命令
  • 环境变量名建议大写,便于区分
变量分类:
  • 用户自定义变量
  • 环境变量:这种变量中主要保存的是和系统操作环境相关的数据
  • 位置参数变量:这种变量主要是用来向脚本当中传递参数或数据的,变量名不能自定义,变量作用是固定的
  • 预定义变量:是Bash中已经定义好的变量,变量名不能自定义,变量作用也是固定的
本地变量:
  • 变量定义

    [root@peiqi ~]# name=alex
    
  • 变量叠加

    [root@peiqi ~]# a=123
    [root@peiqi ~]# a="$a"456
    [root@peiqi ~]# a=${a}789
    
  • 变量调用

    [root@peiqi ~]# echo $a
    123456789
    
  • 变量查看

    [root@peiqi ~]# set
    
  • 变量删除

    [root@peiqi ~]# unset a
    

2、环境变量

用户自定义变量只在当前的Shell中生效,而环境变量会在当前Shell和这个Shell的所有子Shell当中生效。如果把环境变量写入相应的配置文件,那么这个环境变量就会在所有的Shell中生效 。

设置环境变量:
# 声明变量
export 变量名=变量值 
# 查询变量
env 
# 删除变量
unset 变量名
系统常见环境变量:
# PATH:系统查找命令的路径 
[root@peiqi ~]# echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/opt/mysql/bin:/opt/go/bin:/gopath/bin:/root/bin
# PATH变量叠加 
[root@peiqi ~]# PATH="$PATH":/root/sh
[root@peiqi ~]# echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/opt/mysql/bin:/opt/go/bin:/gopath/bin:/root/bin:/root/sh
# PS1:定义系统提示符的变量
\d:显示日期,格式为“星期 月 日” 
\h:显示简写主机名。如默认主机名“localhost” 
\t:显示24小时制时间,格式为“HH:MM:SS” 
\T:显示12小时制时间,格式为“HH:MM:SS” 
\A:显示24小时制时间,格式为“HH:MM” 
\u:显示当前用户名 
\w:显示当前所在目录的完整名称 
\W:显示当前所在目录的最后一个目录 
\#:执行的第几个命令 
\$:提示符。如果是root用户会显示提示符为“#”,如果是普通用户会显示提示符为“$”

# 示例
[root@peiqi ~]# PS1='[\u@\t \w]\$' 
[root@14:27:42 ~]#PS1='[\u@\@ \h \# \W]\$'
[root@02:28 PM peiqi 12 ~]#PS1='[\u@\h \W]\$'
[root@peiqi ~]#

3、位置参数变量

位置参数变量 作用
$n n 为数字,$0 代表shell脚本本身,$​1 -​ $9 代表第1到第9个参数,十以上的参数需要用大括号包含 $
$* 这个变量代表命令行中所有的参数,$* 把所有的参数看成一个整体
$@ 这个变量也代表命令行中所有的参数,不过 $@ 把每个参数区分对待
$# 这个变量代表命令行中所有参数的个数
示例1:
[root@peiqi ~]#vim test1.sh
    #!/bin/bash
    n1=$1
    n2=$2
    # 变量sum的和是n1加n2
    sum=$(( $n1 + $n2))
    #打印变量sum的值
    echo $sum
[root@peiqi ~]#bash test1.sh 10 20
30
示例2:
[root@peiqi ~]#vim test2.sh
    #!/bin/bash 
    # 使用$#代表所有参数的个数
    echo "$#"
    # 使用$*代表所有的参数  
    echo "$*" 
    # 使用$@也代表所有参数 
    echo "$@" 
[root@peiqi ~]#bash test2.sh 4 5 6
3
4 5 6
4 5 6

示例3:$* 与 $@ 的区别

[root@peiqi ~]#vim test3.sh
    #!/bin/bash
    echo '----------------- "$ *" -----------------'
    # "$*" 中的所有参数看成是一个整体,所以这个for循环只会循环一次 
    for i in "$*" 
        do 
            echo "$i" 
        done
    echo '----------------- "$ @" -----------------'
    # "$@" 中的每个参数都看成是独立的,所以有几个参数,就会循环几次
    for j in "$@" 
        do 
            echo "$j" 
        done
    echo '----------------- $ * -----------------'
    # 注意 "$*" 与 $* 的区别,而 "$@" 与 $@ 则无区别
    for k in $* 
        do 
            echo "$k"
        done
[root@peiqi ~]#bash test3.sh 1 2 3
----------------- "$ *" -----------------
1 2 3
----------------- "$ @" -----------------
1
2
3
----------------- $ * -----------------
1
2
3

4、预定义变量

预定义变量 作用
$? 最后一次执行的命令的返回状态。如果这个变量的值为0,证明上一个命令正确执行;如果这个变量的值为非0(具体是哪个数,由命令自己来决定),则证明上一个命令执行不正确
$$ 当前进程的进程号(PID)
$! 后台运行的最后一个进程的进程号(PID)
[root@peiqi ~]#vim test.sh
    #!/bin/bash
    # 输出当前进程的PID
    # 这个PID就是test.sh这个脚本执行时,生成的进程的PID 
    echo "The current process is $$" 
    # 使用find命令在root目录下查找helloworld.sh文件
    # 符号&的意思是把命令放入后台执行
    find /root -name helloworld.sh & 
    echo "The last one Daemon process is $!"
[root@peiqi ~]#bash test.sh 
The current process is 23943
The last one Daemon process is 23944
[root@peiqi ~]#/root/helloworld.sh
接收键盘输入:
read [选项] [变量名] 
选项: 
	-p "提示信息":在等待read输入时,输出提示信息 
	-t 秒数:read命令会一直等待用户输入,使用此选项可以指定等待时间 
	-n 字符数:read命令只接受指定的字符数,就会执行 
	-s:隐藏输入的数据,适用于机密信息的输入
[root@peiqi ~]# vim read.sh
    #!/bin/bash 
    # 提示"请输入姓名"并等待30秒,把用户的输入保存入变量name中 
    read -t 30 -p "Please input your name: " name
    # 年龄是隐私,所以我们用"-s"选项隐藏输入
    read -s -p "Please enter your age: " age
    echo -e "\n"
    # 使用"-n 1"选项只接收一个输入字符就会执行(都不用输入回车)
    read -n 1 -p "Please select your gender[m/f]: " gender
    echo -e "\n"
    echo "Name:$name"
    echo "Age:$age"  
    echo "Gender:$gender"
[root@peiqi ~]# bash read.sh 
Please input your name: pd
Please enter your age: 
Please select your gender[m/f]: m
Name:pd
Age:18
Gender:m

五、Bash的运算符

1、数值运算与运算符

declare命令用于声明 shell 变量:
declare [+/-][选项] 变量名 
选项: 
	-:给变量设定类型属性 
	+:取消变量的类型属性 
	-i:将变量声明为整数型(integer) 
	-x:将变量声明为环境变量 
	-p:显示指定变量的被声明的类型
	
[root@peiqi ~]# declare -i a // 声明整数型变量
[root@peiqi ~]# a=10		 // 改变变量内容
[root@peiqi ~]# echo $a
10

Linux declare命令

数值运算:

方法1:

[root@peiqi ~]# a=11
[root@peiqi ~]# b=22
[root@peiqi ~]# declare -i c=$a+$b

方法2:expr 或 let 数值运算工具

[root@peiqi ~]# a=11
[root@peiqi ~]# b=22
# d的值是a和b的和。注意+号两侧必须有空格
[root@peiqi ~]# d=$(expr $a + $b)

方法3:"$((运算式))" 或 "$[运算式]"

[root@peiqi ~]# a=11
[root@peiqi ~]# b=22
[root@peiqi ~]# e=$(($a+$b))
[root@peiqi ~]# f=$[$a+$b]
运算符:
优先级 运算符 说明
13 -、+ 单目负、单目正
12 !、~ 逻辑非、按位取反或补码
11 *、/、% 乘、除、取模
10 +、- 加、减
9 <<、>> 按位左移、按位右移
8 <=、>=、<、> 小于或等于、大于或等于、小于、大于
7 ==、!= 等于、不等于
6 & 按位与
5 ^ 按位异或
4 | 按位或
3 && 逻辑与
2 || 逻辑
1 =、+=、-=、*=、/=、%=、&=、 ^==、<<=、>>= 赋值、运算且赋值
# 虽然乘和除的优先级高于加,但是通过小括号可以调整运算优先级 
[root@peiqi ~]# a=$(( (11+3)*3/2 )) 
# 14不能被3整除,余数是2 
[root@peiqi ~]# b=$(( 14%3 ))  
# 逻辑与运算只有相与的两边都是1,与的结果才是1,否则与的结果是0 
[root@peiqi ~]# c=$(( 1 && 0 )) 

2、变量测试与内容替换

变量置换方式 变量y没有设置 变量y为空值 变量y已设置值
x=$ x=新值 x为空 x=$y
x=$ x=新值 x=新值 x=$y
x=$ x为空 x=新值 x=新值
x=$ x为空 x为空 x=新值
x=$ x=新值、y=新值 x为空、y值不变 x=$y、y值不变
x=$ x=新值、y=新值 x=新值、y=新值 x=$y、y值不变
x=$ 新值输出到标准、错误输出(就是屏幕) x为空 x=$y
x=$ 新值输出到标准、错误输出 新值输出到标准、错误输出 x=$y
[root@peiqi ~]# unset y 	# 删除变量y
[root@peiqi ~]# x=${y-new}  # 进行测试
[root@peiqi ~]# echo $x 	# 因为变量y不存在,所以x=new
new
[root@peiqi ~]# y=""		# 给变量y赋值为空
[root@peiqi ~]# x=${y-new}	# 进行测试
[root@peiqi ~]# echo $x		# 因为变量y为空,所以x为空

[root@peiqi ~]# y=old		# 给变量y赋值
[root@peiqi ~]# x=${y-new}	# 进行测试
[root@peiqi ~]# echo $x		# 因为变量y有值,所以x=$y
old

六、环境变量配置文件

1、环境变量配置文件简介

source命令:
[root@peiqi ~]# source 配置文件
或 
[root@peiqi ~]# . 配置文件
环境变量配置文件简介:

环境变量配置文件中主要是定义对系统的操作环境生效的系统默认环境变量,比如PATH、HISTSIZE、PS1、HOSTNAME等默认环境变量。

  • /etc/profile
  • /etc/profile.d/*.sh
  • ~/.bash_profile
  • ~/.bashrc
  • /etc/bashrc

2、环境变量配置文件作用

/etc/profile的作用:
  • USER变量
  • LOGNAME变量
  • MAIL变量
  • PATH变量
  • HOSTNAME变量
  • HISTSIZE变量
  • umask
  • 调用/etc/profile.d/*.sh文件
~/.bash_profile的作用:
  • 调用了~/.bashrc文件
  • 在PATH变量后面加入了 :$HOME/bin 这个目录
~/.bashrc的作用:
  • 定义默认别名
  • 调用/etc/bashrc
/etc/bashrc的作用:
  • PS1变量

  • umask

  • PATH变量

  • 调用/etc/profile.d/*.sh文件

3、其他配置文件和登录信息

注销时生效的环境变量配置文件:
  • ~/.bash_logout
其他配置文件:
  • ~/bash_history
Shell登录信息:
  • 本地终端欢迎信息: /etc/issue

    转义符 作用
    \d 显示当前系统日期
    \s 显示操作系统名称
    \l 显示登录的终端号,这个比较常用
    \m 显示硬件体系结构,如i386、i686等
    \n 显示主机名
    \o 显示域名
    \r 显示内核版本
    \t 显示当前系统时间
    \u 显示当前登录用户的序列号
  • 远程终端欢迎信息: /etc/issue.net

    • 转义符在/etc/issue.net文件中不能使用
    • 是否显示此欢迎信息,由ssh的配置文件/etc/ssh/sshd_config决定,加入"Banner /etc/issue.net"行才能显示(记得重启SSH服务)
  • 登陆后欢迎信息:/etc/motd

    • 不管是本地登录,还是远程登录,都可以显示此欢迎信息

七、基础正则表达式

1、正则表达式与通配符

  • 正则表达式用来在文件中匹配符合条件的字符串。grep、awk、sed等命令可以支持正则表达式。
  • 通配符用来匹配符合条件的文件名,通配符是完全匹配。ls、find、cp这些命令不支持正则表达式,所以只能使用shell自己的通配符来进行匹配了。

2、基础正则表达式

字符 说明
\ 将下一字符标记为特殊字符、文本、反向引用或八进制转义符。例如,"n" 匹配字符 "n","\n" 匹配换行符,"\\" 匹配"\","\(" 匹配 "("
^ 匹配输入字符串开始的位置。如果设置了 RegExp 对象的 Multiline 属性,^ 还会与 "\n" 或 "\r" 之后的位置匹配
$ 匹配输入字符串结尾的位置。如果设置了 RegExp 对象的 Multiline 属性,$ 还会与 "\n" 或 "\r" 之前的位置匹配
* 零次多次 匹配前面的字符或子表达式。例如,"zo*" 匹配 "z" 和 "zoo"。* 等效于
{n} n 是非负整数,正好匹配 n 次。例如,"o{2}" 与 "Bob" 中的 "o" 不匹配,但与 "food" 中的两个 "o" 匹配
{n,} n 是非负整数,至少匹配 n 次。例如,"o{2,}" 不匹配 "Bob" 中的 "o",而匹配 "foooood" 中的所有 "o"。"o{1,}" 等效于 "o+"。"o{0,}" 等效于 "o*"
{n,m} Mn 是非负整数,其中 n <= m。匹配至少 n 次,至多 m 次。例如,"o{1,3}" 匹配 "fooooood" 中的头三个 "o"。"o{0,1}" 等效于 "o?"。注意:不能将空格插入逗号和数字之间
. 匹配除 "\r\n" (即换行符)之外的任何单个字符。若要匹配包括 "\r\n" 在内的任意字符,请使用诸如 "[\s\S]" 之类的模式
[xyz] 字符集,匹配包含的任一字符。例如,"[abc]" 匹配 "plain" 中的 "a"
[^a-z] 反向范围字符,匹配不在指定的范围内的任何字符。例如,"[^a-z]" 匹配任何不在 "a" 到 "z" 范围内的任何字符
# 匹配至少包含有一个a的行 
grep "a*" test_rule.txt
# 匹配最少包含两个连续a的字符串 
grep "aa*" test_rule.txt  
# "s..d"会匹配在s和d这两个字母之间一定有两个字符的单词 
grep  "s..d" test_rule.txt  
# 匹配在s和d字母之间有任意字符 
grep "s.*d" test_rule.txt  
# 匹配所有内容
grep ".*" test_rule.txt  
# 匹配以大写"M"开头的行 
grep "^M" test_rule.txt 
# 匹配以小写"n"结尾的行
grep "n$" test_rule.txt 
# 会匹配空白行 
grep -n "^$" test_rule.txt 
# 匹配s和i字母中,要么是a、要么是o
grep "s[ao]id" test_rule.txt
# 匹配任意一个数字 
grep "[0-9]" test_rule.txt
# 匹配用小写字母开头的行
grep "^[a-z]" test_rule.txt
# 匹配不是小写字母开头的行
grep "^[^a-z]" test_rule
# 匹配不是字母开头的行 
grep "^[^a-zA-Z]" test
# 匹配使用 . 结尾的行 
grep "\.$" test_rule.txt
# 匹配a字母连续出现三次的字符串
grep "a\{3\}" test_rule.txt 
# 匹配包含连续的三个数字的字符串 
grep "[0-9]\{3\}" test_rule.txt
# 匹配最少用连续三个数字开头的行 
grep "^[0-9]\{3,\}[a-z]" test_rule.txt
# 匹配在字母s和字母i之间有最少一个a,最多三个a 
grep "sa\{1,3\}i" test_rule.txt 

八、字符截取命令

1、cut字段提取命令

cut [选项] 文件名 
选项: 
	-f 列号:提取第几列 
	-d 分隔符:按照指定分隔符分割列
vim strudent.txt
    ID Name Gender Mark
    1 peiqi F 86
    2 qiaozhi M 90
    3 pd M 83
[root@peiqi ~]# cut -d " " -f 2 student.txt 
Name
peiqi
qiaozhi
pd
[root@peiqi ~]# cut -d " " -f 3,4 student.txt 
Gender Mark
F 86
M 90
M 83
[root@peiqi ~]# cut -d ":" -f 1,3 /etc/passwd

2、awk命令

# 行匹配语句 awk '' 只能用单引号
awk [选项参数] 'pattern1{action1}  pattern2{action2} ...' filename
选项参数:
	-F fs:指定输入文件折分隔符,fs是一个字符串或者是一个正则表达式,如-F:
	-v var=value:赋值一个用户定义变量
pattern:表示awk在数据中查找的内容,就是匹配模式
action:在找到匹配内容时所执行的一系列命令
案例实操:
  • 数据准备

    [root@peiqi ~]# cp /etc/passwd ./
    
  • 搜索passwd文件以root关键字开头的所有行,并输出该行的第7列

    [root@peiqi ~]# awk -F: '/^root/ {print $7}' passwd
    
  • 搜索passwd文件以/bin/bash结尾的所有行,并输出该行的第1列和第7列,中间以 " - " 号分割

    [root@peiqi ~]# awk -F: '/\/bin\/bash$/ {print $1" - "$7}' passwd 
    
  • 只显示/etc/passwd的第一列和第七列,以制表符分割,且在所有行前面添加列名"user,shell"在最后一行添加"pd,/bin/pd"

    # 注意:BEGIN 在所有数据读取行之前执行;END 在所有数据执行之后执行
    awk -F: 'BEGIN{print "user\tshell"} {print $1"\t"$7} END{print "pd\t/bin/pd"}' passwd
    
  • 将passwd文件中的用户id增加数值1并输出

    [root@peiqi ~]# awk -v i=1 -F: '{print $3+1}' passwd
    
awk的内置变量:
变量 说明
FILENAME 当前文件名
NR 已经读出的记录数,就是行号,从1开始
NF 一条记录的字段的数目
案例实操:
  • 统计passwd文件名,每行的行号,每行的列数

    awk -F: '{print "文件名:" FILENAME "\t" "行:" NR  "\t" "列:" NF}' passwd
    文件名:passwd	行:1		列:7
    ...
    ...
    文件名:passwd	行:23	列:7
    
  • 切割IP

    [root@peiqi ~]# ifconfig eth0 | grep "inet" | awk -F " " '{print $2}'
    172.17.229.160
    
  • 查询 test.txt 中空行所在的行号

    [root@peiqi ~]# awk '/^$/ {print NR}' test.txt 
    4
    6
    
  • 有文件 grade.txt 内容如下:

    张三 40
    李四 50
    王五 60
    

    求第二列的和并输出

    awk -F " " '{sum+=$2} END{print sum}' grade.txt
    
  • 关系运算符

    # a.txt
    ID	Name    PHP	Linux	MySQL	Avg
    1   peiqi	82  95      86      87.66 
    2   pd      74  96      87      85.66 
    3   jaz     99  83      93      91.66
    
    [root@peiqi ~]# cat s.txt | awk '$6>=87.66 {printf $2 "\n"}' | grep -v Name
    peiqi
    jaz
    

3、sed命令

sed是一种流编辑器,它一次处理一行内容。处理时,把当前处理的行存储在临时缓冲区中,称为“模式空间”,接着用sed命令处理缓冲区中的内容,处理完成后,把缓冲区的内容送往屏幕。接着处理下一行,这样不断重复,直到文件末尾。文件内容并没有改变,除非你使用重定向存储输出。

sed [选项参数] '[action]' filename
# 选项参数:
	-e:直接在指令列模式上进行sed的动作编辑
	-n:一般sed命令会把所有数据都输出到屏幕,如果加入此选择,则只会把经过sed命令处理的行输出到屏幕
	-i:用sed的修改结果直接修改读取数据的文件,而不是由屏幕输出
# action:
	a:追加,在当前行后添加一行或多行。添加多行时,除最后一行外,每行末尾需要用 \ 代表数据未完结
	c:行替换,用c后面的字符串替换原数据行,替换多行时,除最后一行外,每行末尾需用 \ 代表数据未完结
	i:插入,在当期行前插入一行或多行。插入多行时,除最后一行外,每行末尾需要用 \ 代表数据未完结
	d:删除,删除指定的行
	p:打印,输出指定的行
	s:字串替换,用一个字符串替换另外一个字符串。格式为"行范围s/旧字串/新字串/g" 
准备数据:
# grade.txt
ID	Name    PHP	Linux	MySQL	Avg
1   peiqi	82  95      86      87.66 
2   pd      74  96      87      85.66 
3   jaz     99  83      93      91.66
  • 查看文件的第二行

    [root@peiqi ~]# sed -n '2p' grade.txt 
    1   peiqi	82  95      86      87.66 
    
  • 将 4a 4 alex 88 93 79 86.66 插入到 grade.txt 第4行的下面

    sed '4a 4 alex 88 93 79 86.66' grade.txt
    
  • 删除文件中所有包含 peiqi 的行

    sed '/peiqi/d' grade.txt
    
  • 删除第2行到第4行的数据,并对内容作出的是真正的删除(-i 参数)

    sed -i '2,4d' grade.txt
    
  • 在第二行前插入两行数据

    sed '2i hello \
    world' grade.txt
    
  • 将 grade.txt 文件中的 peiqi 替换为 qiaozhi

    # g表示global,全部替换
    sed 's/peiqi/qiaozhi/g' grade.txt
    
  • 同时将 grade.txt 文件中的 peiqi 和 jaz 替换为空

    sed 's/peiqi//g;s/jaz//g' grade.txt
    

九、字符处理命令

1、排序命令sort

sort [选项] 文件名
# 选项: 
	-f:忽略大小写 
	-n:以数值型进行排序,默认使用字符串型排序 
	-r:反向排序 
	-t:指定分隔符,默认是分隔符是制表符 
	-k n[,m]:按照指定的字段范围排序。从第n字段开始,m字段结束(默认到行尾)
准备数据:
[root@peiqi ~]# vim sort.txt
    bb:40:5.4
    bd:20:4.2
    xz:50:2.3
    cl:10:3.5
    ss:30:1.6
  • 按照 : 分割后的第三列倒序排序

    [root@peiqi ~]# sort -t : -nr -k 3 sort.txt
    bb:40:5.4
    bd:20:4.2
    cl:10:3.5
    xz:50:2.3
    ss:30:1.6
    

2、统计命令wc

利用wc指令可以计算文件的Byte数、字数、或是列数,若不指定文件名称、或是所给予的文件名为"-",则wc指令会从标准输入设备读取数据。

wc [选项] 文件名
# 选项: 
	-l:只统计行数 
	-w:只统计单词数 
	-m:只统计字符数
[root@peiqi ~]# wc -l -w -m sort.txt 
 5  5 50 sort.txt

十、条件判断

1、两个判断格式

[root@peiqi ~]# test -e /etc/passwd
[root@peiqi ~]# echo $?
0
[root@peiqi ~]# [ -e /etc/passwd ]
[root@peiqi ~]# echo $?
0
# 第一个命令如果正确执行,则打印 yes,否则打印 no
[root@peiqi ~]# [ -e /etc/pwd ] && echo "yes" || echo "no"
no

2、按照文件类型进行判断

测试选项 作用
-b 文件 判断该文件是否存在,并且是否为块设备文件
-c 文件 判断该文件是否存在,并且是否为字符设备文件
-d 文件 判断该文件是否存在,并且是否为目录文件
-e 文件 判断该文件是否存在
-f 文件 判断该文件是否存在,并且是否为普通文件
-L 文件 判断该文件是否存在,并且是否为符号链接文件
-p 文件 判断该文件是否存在,并且是否为管道文件
-s 文件 判断该文件是否存在,并且是否为管道文件
-S 文件 判断该文件是否存在,并且是否为套接字文件

3、按照文件权限进行判断

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

4、两个文件之间进行比较

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

5、两个整数之间比较

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

6、字符串的判断

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

7、多重条件判断

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

十一、流程控制

1、if语句

单分支if条件语句:
if [ 条件判断式 ];then 
	程序 
fi 
# 或者 
if [ 条件判断式 ] 
then 
	程序 
fi

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

  • if语句使用fi结尾,和一般语言使用大括号结尾不同
  • [ 条件判断式 ]就是使用test命令判断,所以中括号和条件判断式之间必须有空格
  • then后面跟符合条件之后执行的程序,可以放在[]之后,用 ; 分割。也可以换行写入,就不需要 ; 了
#!/bin/bash 
# 统计根分区使用率 
# 把根分区使用率作为变量值赋予变量rate 
rate=$(df -h | grep "/dev/vda1" | awk '{print $5}' | cut -d "%" -f 1) 
if [ $rate -ge 80 ];then 
	echo "Warning! /dev/vda1 is full of!" 
fi
双分支if条件语句:
if [ 条件判断式 ];then 
	条件成立时,执行的程序 
else 
	条件不成立时,执行的另一个程序 
fi

示例1:备份mysql数据库

#!/bin/bash 
# 备份mysql数据库

# 同步系统时间 
ntpdate asia.pool.ntp.org &>/dev/null
# 把当前系统时间按照"年月日"格式赋予变量date 
date=$(date +%Y%m%d) 
# 统计mysql数据库的大小,并把大小赋予size变量 
size=$(du -sh /var/lib/mysql) 

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: $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

示例2:判断mysql是否启动

#!/bin/bash
port=$(netstat -anp | grep mysql | grep tcp6 | awk '{print $4}' | cut -d ":" -f 4) 
if [ "$port" == 3306 ];then 
	echo "mysql is running..."
else 
	service mysqld start
	echo "mysql is starting..." 
fi
多分支if条件语句:
if [ 条件判断式1 ];then 
	当条件判断式1成立时,执行程序1 
elif [ 条件判断式2 ];then 
	当条件判断式2成立时,执行程序2
else 
	当所有条件都不成立时,最后执行此程序 
fi

2、case语句

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

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

3、for循环

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

示例1:打印时间

#!/bin/bash
for time in morning noon afternoon evening 
	do 
		echo "This time is $time!" 
	done 

示例2:批量解压缩脚本

#!/bin/bash
cd /opt 
ls *.tar.gz > tar.log 
for i in $(cat tar.log) 
	do 
		tar -zxf $i -C /test &>/dev/null 
	done 
rm -rf /opt/tar.log
语法二:
for (( 初始值;循环控制条件;变量变化 )) 
	do 
		程序 
	done 

示例:从1加到100

#!/bin/bash
total=0 
for (( i=1;i<=100;i=i+1 )) 
	do 
		total=$(( $total+$i )) 
	done 
echo "The sum of 1+2+...+100 is: $total"

4、while循环与until循环

while循环:

while循环是不定循环,也称作条件循环。只要条件判断式成立,循环就会一直继续,直到条件判断式不成立,循环才会停止。这就和for的固定循环不太一样了。

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

示例:从1加到100

#!/bin/bash
i=1 
total=0
while [ $i -le 100 ] 
	do 
		total=$(( $total+$i )) 
		i=$(( $i+1 )) 
	done 
echo "The sum of 1+2+...+100 is: $total"
until循环:

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

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

示例:从1加到100

#!/bin/bash
i=1 
total=0
# 循环直到变量i的值大于100,就停止循环 
until [ $i -gt 100 ] 
	do 
		total=$(( $total+$i )) 
		i=$(( $i+1 )) 
	done 
echo "The sum of 1+2+...+100 is: $total"
posted @ 2020-09-25 12:56    阅读(321)  评论(0编辑  收藏  举报