Shell

概述

Shell是一个命令行解释器,他接收应用程序/用户命令,然后调用操作系统内核

image-20230125165047995

Shell还是一个功能相当强大的编辑语言,易编写,易调试,灵活性强。

Shell解析器

Linux提供的Shell解析器有6种:

1
2
3
4
5
6
7
[root@Shell ~]# cat /etc/Shells
/bin/sh
/bin/bash
/usr/bin/sh
/usr/bin/bash
/bin/tcsh
/bin/csh

bash和sh的关系是

1
2
3
4
[root@Shell ~]# ll /bin/ | grep bash
-rwxr-xr-x. 1 root root 964536 4月 1 2020 bash
lrwxrwxrwx. 1 root root 4 9月 28 07:56 sh -> bash
软连接

Centos默认的解析器是bash

1
2
[root@Shell ~]# echo $Shell
/bin/bash

Shell脚本入门

脚本格式

脚本以#!/bin/bash开头(指向解析器)

第一个Shell脚本:helloworld

需求:创建一个Shell脚本,输出helloworld

实例操作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
[root@Shell ~]# mkdir datas          ---创建一个脚本文件夹
[root@Shell ~]# cd datas/ ---以后所有脚本放在这
[root@Shell datas]# touch helloworld.sh
[root@Shell datas]# vi helloworld.sh
#!/bin/bash
echo "helloworld 严千屹"
~
[root@Shell datas]# sh helloworld.sh ---相对路径
helloworld 严千屹
[root@Shell ~]# bash datas/helloworld.sh ---绝对路径
helloworld 严千屹
#上面都是bash和sh帮你执行脚本,脚本本身不需要执行权限。
#下面本质是脚本需要自己执行,所以需要执行权限
[root@Shell datas]# ./helloworld.sh
-bash: ./helloworld.sh: 权限不够
[root@shell datas]# chmod 777 helloworld.sh ---给予权限
[root@shell datas]# ./helloworld.sh
helloworld 严千屹

第二个Shell脚本:多命令处理

需求:

在/root/datas/目录下创建一个qianyi.txt,在 qianyi.txt 文件中增加“qianyios”

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[root@shell datas]# touch qy.sh
[root@shell datas]# vi qy.sh
#!/bin/bash
cd /root/datas/
touch qianyi.txt
echo "qianyios" >> qianyi.txt
[root@shell datas]# bash qy.sh
#执行完成后会出现qianyi.txt
[root@shell datas]# ll
总用量 12
-rwxrwxrwx 1 root root 43 1月 25 17:06 helloworld.sh
-rw-r--r-- 1 root root 9 1月 25 17:22 qianyi.txt
-rw-r--r-- 1 root root 76 1月 25 17:22 qy.sh
[root@shell datas]# cat qianyi.txt
qianyios

Shell中的变量

常用的系统变量

​ $HOME $PWD $SHELL $USER等

实例操作

查看系统变量的值

1
2
3
[root@shell datas]# echo $HOME
/root
显示家目录

显示当前Shell中所有的变量:set

1
2
3
4
5
6
7
8
[root@shell datas]# set
ABRT_DEBUG_LOG=/dev/null
BASH=/usr/bin/bash
BASH_ALIASES=()
BASH_ARGC=()
BASH_ARGV=()
BASH_CMDS=()
......

自定义变量

基本语法

  1. 定义变量:变量=值
  2. 撤销变量:unset 变量
  3. 声明静态变量:readonly 变量 注意不能unset

变量定义规则

  1. 变量名称可以有字母,数字和下划线组成,但是不能以数字开头,环境变量名建议全部大写
  2. 等号两侧不能有空格
  3. 在bash中,变量默认类型都是字符串类型,无法直接进行数值运算
  4. 变量的值如果有空格,需要使用双引号或单引号括起来

实例操作

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
#自定义变量
[root@shell ~]# QY=qianyi
[root@shell ~]# echo $QY
qianyi
[root@shell ~]# QY="qian yi"
[root@shell ~]# echo $QY
qian yi

变量为空和未定义变量是两个不同的概念
[root@shell ~]# echo $age ---未定义

[root@shell ~]# age=""
[root@shell ~]# echo $age ---已定义

#两个没办法辨认
[root@shell ~]# set -u ---调用为声明变量会报错
[root@shell ~]# echo $add
-bash: add: unbound variable
[root@shell ~]# add=123
[root@shell ~]# echo $add
123
#删除变量 不需要加$add
[root@shell ~]# echo $add
123
[root@shell ~]# unset add
[root@shell ~]# echo $add
-bash: add: unbound variable

环境变量

  1. 环境变量设置
1
2
[root@shell ~]# export qyage="18"
#使用export声明的变量即是环境变量
  1. 环境变量查询 set可以查看所有变量,env只能查看环境变量
1
2
[root@shell ~]# env | grep qyage
qyage=18
  1. 系统默认环境变量
1
2
3
4
5
6
7
[root@shell ~]# env
XDG_SESSION_ID=10
HOSTNAME=shell
TERM=xterm
SHELL=/bin/bash
HISTSIZE=1000
qyage=18
  1. PATH变量:系统查找命令的路径

先查询下PATH环境变量的值:

1
2
3
[root@shell ~]# echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin

PATH 变量的值是用“:”分割的路径,这些路径就是系统查找命令的路径。也就是说当我们输入了一个程序名,如果没有写入路径,系统就会到 PATH 变量定义的路径中去寻找,是否有可以执行的程序。如果找到则执行,否则会报“命令没有发现”的错误。

那么是不是我们把自己的脚本拷贝到 PATH 变量定义的路径中,我们自己写的脚本也可以不输入路径而直接运行呢?

1
2
3
4
5
6
[root@shell ~]# cp datas/helloworld.sh /usr/bin/
[root@shell ~]# helloworld.sh
helloworld 严千屹
[root@shell ~]# rm -rf /usr/bin/helloworld.sh
[root@shell ~]# helloworld.sh
-bash: /usr/bin/helloworld.sh: 没有那个文件或目录

那么我们是不是可以修改 PATH变量的值,而不是把程序脚本复制到/bin/目录中。当然是可以的,我们通过变量的叠加就可以实现了:

1
2
3
4
5
[root@shell ~]# PATH="$PATH":/root/datas
[root@shell ~]# echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin:/root/datas
[root@shell ~]# helloworld.sh
helloworld 严千屹

PS1变量:命令提示符设置

PS1是一个很有意思的变量,是用来定义命令行的提示符,可以安装我们自己的需求来定义自己喜欢的提示符。PS1可以支持以下这些选项:

1
2
3
4
5
6
7
8
9
10
11
12
\d :#代表日期,格式为weekday month date,例如:"Mon Aug 1"   
\H :#完整的主机名称。
\h :#仅取主机的第一个名字。
\t :#显示时间为24小时格式,如:HH:MM:SS
\T :#显示时间为12小时格式
\A :#显示时间为24小时格式:HH:MM
\u :#当前用户的账号名称
\v :#BASH的版本信息
\w :#完整的工作目录名称。家目录会以 ~代替
\W :#利用basename取得工作目录名称,所以只会列出最后一个目录
\# :#下达的第几个命令
\$ :#提示字符,如果是root时,提示符为:# ,普通用户则为:$
1
格式 PS1='[ ]\$ '     \$空格'  有个空格  一定要单引号
1
2
3
4
5
[root@shell ~]# echo $PS1
[\u@\h \W]\$
[root@shell ~]# PS1='[\u@\t \w]\$ '
[root@20:14:51 ~]# cd datas/
[root@20:14:54 ~/datas]#

位置参数变量

位置参数变量 作用
$n 表示第n个参数,$1则表示第一个参数,$2表示第二个参数……如果有10以上的参数用${10}
$0 当前程序的名称,也就是命令本身。
$* 传递给程序的所有参数组成的字符串(参数)。
$@ 以“参数1”、“参数2”……保存所有的参数。
$# 代表命令行中所有参数个数
$? 上一个代码或者Shell程序在Shell中退出的情况,如果正常退出则返回0,否则返回非0值。
$$ 本程序的(进程ID)PID。
$! 上一个命令的PID。

实例操作

1
2
3
4
5
6
7
8
9
10
11
12
[root@shell datas]# vi count.sh
#!/bin/bash
a=$1
b=$2
sum=$(($a+$b))
#$(( ))双小括号才能进行数字运算,$( )运行命令
echo $sum
echo $0
[root@shell datas]# chmod 755 count.sh
[root@shell datas]# ./count.sh 22 33
55
./count.sh #$0输出命令本身
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
[root@shell datas]# vi para.sh
#!/bin/bash
echo "\$* is $*"
echo "\$@ is $@"
echo "\$# is $#"
[root@shell datas]# chmod 777 para.sh
[root@shell datas]# ./para.sh 11 22 33 44
$* is 11 22 33 44 #{11 22 33 44}看成一个整体
$@ is 11 22 33 44 #{11},{22},{33},{44}
$# is 4 #个数
验证过程
[root@shell datas]# vi para2.sh
#!/bin/bash
#for循环,有多少次出现多少次
for i in "$*"
do
echo $i
done
echo "------------------"
for y in "$@"
do
echo $y
done
[root@shell datas]# ./para2.sh 11 22 33 44
11 22 33 44 #整体,循环1次
------------------
11 #4个,循环4次
22
33
44

预定义变量

$? 最后一次执行的命令的返回状态。如果这个变量的值为 0,证明上一个命令正确执行:如果这个变量的值为非 0(具体是哪个数,由命令自己来决定),则证明上一个命令执行不正确了。
$$ 当前进程的进程号(PID)
$! 后台运行的最后一个进程的进程号(PID)
1
2
3
4
5
6
7
8
[root@shell ~]# ls
anaconda-ks.cfg bin datas repo.bak
[root@shell ~]# echo $?
0
[root@shell ~]# sjkdhasjkd
-bash: sjkdhasjkd: 未找到命令
[root@shell ~]# echo $?
127

read键盘接收

定义

[root@shell datas]# read –help
-bash: read: –: 无效选项
read: 用法: read [选项] [变量名]

read [-ers]

[-a 数组]

[-d 分隔符]

[-i 缓冲区文字]

[-n 读取字符数]

[-N 读取字符数]

[-p 提示符]

[-t 超时]

[-u 文件描述符]

[名称 …]名称指的是变量名

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
[root@shell datas]# vi count2.sh
#!/bin/bash
read -t 30 -p "请输入第一个数字:" num1
read -t 30 -p "请输入第二个数字:" num2
sum=$(($num1+$num2))
echo $sum
[root@shell datas]# chmod 777 count2.sh
[root@shell datas]# ./count2.sh
请输入第一个数字:1
请输入第二个数字:1
2


#########前者需要回车,下面的不需要回车
[root@shell datas]# vi count2.sh
#!/bin/bash
read -n 1 -t 30 -p "请输入第一个数字:" num1 #-n 1 限制输入1个字符数
echo -e "\n"
read -s -t 30 -p "请输入第二个数字:" num2 #-s 是隐藏
echo -e "\n"
sum=$(($num1+$num2))
echo $sum
[root@shell datas]# ./count2.sh
请输入第一个数字:1

请输入第二个数字: #我这里输入的是12

13

declare声明变量类型

定义

既然所有变量的默认类型是字符串型,那么只要我们把变量声明为整数型不就可以运算了吗?

-:给变量设定类型属性

+:取消变量的类型属性

-a:将变量声明为数组型

-i:将变量声明为整数型(integer)

-x:将变量声明为环境变量

-r:将变量声明为只读变量,注意,一旦设置为只读变量,既不能修改变量的值,也不能删除变量,甚至不能通过+r取消只读属性

-p:显示指定变量的被声明的类型、

声明数值进行运算

1
2
3
4
5
6
7
8
[root@shell datas]# a=1
[root@shell datas]# b=2
[root@shell datas]# c=$a+$b
[root@shell datas]# echo $c
1+2
[root@shell datas]# declare -i c=$a+$b #声明整数类型才可以进行数值运行
[root@shell datas]# echo $c
3

数组

1
2
3
4
5
[root@shell datas]# declare -a name[0]="qy"  #声明数组
[root@shell datas]# name[1]="qy1" #也可以不用declare,就知道你在声明数组
[root@shell datas]# name[2]="qy2"
[root@shell datas]# echo ${name[*]}
qy qy1 qy2

环境变量

1
2
3
两个声明变量作用一样
[root@shell datas]# export test
[root@shell datas]# declare -x test="123"

只读属性

注意,一旦设置为只读变量,既不能修改变量的值,也不能删除变量,甚至不能通过+r取消只读属性

1
2
3
4
5
6
7
8
9
10
11
12
[root@shell datas]# declare -r test        #添加只读属性
[root@shell datas]# declare -p
......
declare -rx test="123"
[root@shell datas]# echo $test
123
[root@shell datas]# test=1 #无法改值
-bash: test: 只读变量
[root@shell datas]# unset test #无法删除变量
-bash: unset: test: 无法反设定: 只读 variable
[root@shell datas]# declare +r test #无法删除属性
-bash: declare: test: 只读变量

不过好在这个变量是命令行声明的,所以重启或重新登入,这个变量就会消失

数值运算

使用expr或let数值运算工具

1
2
3
4
5
6
7
8
9
10
[root@shell ~]# aa=11
[root@shell ~]# bb=22
[root@shell ~]# dd=$(expr $aa + $bb)
[root@shell ~]# echo $dd
33
#dd的值是aa和bb的和。注意“+”号两侧比有空格
#如果是let呢
[root@shell ~]# let e=$aa+$bb
[root@shell ~]# echo $e
33

使用双小括号 $((运算式子)) 或$[运算式子] 的方式运算 (看个人习惯,看你喜欢用哪种)

区分单小括号$( )调用的是系统命令 双小括号是计算数学运算的

1
2
3
4
5
6
7
8
[root@shell ~]# aa=11
[root@shell ~]# bb=22
[root@shell ~]# ff=$(($aa+$bb))
[root@shell ~]# echo $ff
33
[root@shell ~]# gg=$[$aa+$bb]
[root@shell ~]# echo $gg
33

Shell常用运算符

优先级 运算符 说明
13 -,+ 单目负,单目正
12 !,~ 逻辑非,按位取反或补码
11 *,/,% 乘、除、取模
10 +,- 加、减
9 <<,>> 按位左移,按位右移
8 < = ,> = ,< , > 小于或等于、大于或等于、小于、大于
7 ==, != 等于
6 & 按位与
5 ^ 按位异或
4 | 按位或
3 && 逻辑与
2 || 逻辑或
1 =,=,-=,=,/=,%=,&=,^=,|=,<<=,>>= 赋值,运算且赋值

取模运算

1
2
3
4
[root@shell ~]# bb=$((14%5))
[root@shell ~]# echo $bb
4
#14不能被5整除,余数是4

逻辑与

1
2
3
4
[root@shell ~]# cc=$((1&&0))
[root@shell ~]# echo $cc
0
逻辑与运算只有想与的两边都是1,与的结果才是1,否则与的结果是0

四则运算练习

1
2
3
4
5
6
7
8
[root@shell ~]# vi count3.sh
#!/bin/bash
vaule=$(($1 $2 $3))
echo $vaule
[root@shell ~]# ./count3.sh 11 + 11
22
[root@shell ~]# ./count3.sh 11 / 11
1

但是上面会有bug不能用

1
2
3
4
5
6
7
8
9
10
11
12
13
[root@shell datas]# vi count4.sh
#!/bin/bash
read -t 30 -p "please input num1:" num1
read -t 30 -p "please input num2:" num2
read -n 1 -t 30 -p "please inpute operato[+-*/]:" oper
echo -e “\n”
[ "$oper" == "+" ] && echo "$(($num1 + $num2))" && exit
[ "$oper" == "-" ] && echo "$(($num1 - $num2))" && exit
[ "$oper" == "*" ] && echo "$(($num1 * $num2))" && exit
[ "$oper" == "/" ] && echo "$(($num1 / $num2))" && exit

echo "please input right oper"
[空格"$oper"空格==空格"+"空格]

变量的测试与内容置换

变量置换方式 变量y没有设置 变量y为空值 变量y设置值
x=${y-新值} x= 新值 x 为空 x=$y
x=${y:-新值} x= 新值 x= 新值 x=$y
x=${y+新值} x 为空 x= 新值 x=新值
x=${y:+新值} x 为空 x 为空 x=新值
x=${y=新值} x= 新值 x 为空 x=$y
同上 y= 新值 y 值不变 y值不变
x=${y:=新值} x= 新值 X= 新值 x=$y
同上 y= 新值 y= 新值 y值不变
x=${y?新值} 新值输出到标准错误输出(屏幕) x 为空 x=$y
x=${y:?新值} 新值输出到标准错误输出 新值输出到标准错误输出 x=$y

假设我们要测b变量,现在b变量我们从来没有设置过

1
2
3
4
5
6
7
8
9
10
11
12
13
以下可以判断变b不存在
[root@shell ~]# x=${b-new}
[root@shell ~]# echo $x
new
以下可以判断是否为空
[root@shell ~]# b=""
[root@shell ~]# x=${b-new}
[root@shell ~]# echo $x
以下可以判断是否有值
[root@shell ~]# b=123
[root@shell ~]# x=${b-new}
[root@shell ~]# echo $x
123

环境变量配置文件

1、让环境变量生效的命令 source 配置文件 或 . 配置文件

2、环境变量配置文件

登录时生效的环境变量配置文件

在 Linux 系统登录时主要生效的环境变量配置文件有以下五个:

/etc/profile

/etc/profile.d/*.sh

/etc/bashrc

~/.bash_profile

~/.bashrc

写在前三个的配置文件对所有用户生效,写在最后两个对当前用户生效

基础正则

基础正则表达式

元字符 作 用
* 前一个字符匹配 0 次或任意多次。
. 匹配除了换行符外任意一个字符。
^ 匹配行首。例如:^hello 会匹配以 hello 开头的行。
$ 匹配行尾。例如:hello&会匹配以 hello 结尾的行。
[] 匹配中括号中指定的任意一个字符,只匹配一个字符。例如:[aoeiu] 匹配任意一个元音字母,[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 位的小写字母。

~/.bashrc 文件中建立这个别名:

实现grep能显示颜色

1
echo "alias grep=\'grep --color=auto\'" >> /root/.bashrc 

建立练习文档

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[root@shell datas]# vi test.txt
Mr. Li Ming said:
he was the most honest man.
123despise him.
google
gooooogle
ggle
gogle
soooooid
But since Mr. shen Chao came, he never saaaid those words. 5555nice!
because,actuaaaally,
Mr. Shen Chao is the most honest man
Later,Mr. Li ming soid his hot body.
hello is
hello was

练习*号

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
# a*意思是最少包含0个a或无数个a
[root@shell datas]# grep "a*" test.txt
Mr. Li Ming said:
he was the most honest man.
123despise him.
But since Mr. shen Chao came, he never saaaid those words. 5555nice!
because,actuaaaally,
Mr. Shen Chao is the most honest man
Later,Mr. Li ming soid his hot body.

# aa*意思是最少包含1个a或无数个a
[root@shell datas]# grep "aa*" test.txt
Mr. Li Ming said:
he was the most honest man.
But since Mr. shen Chao came, he never saaaid those words. 5555nice!
because,actuaaaally,
Mr. Shen Chao is the most honest man
Later,Mr. Li ming soid his hot body.

# aaa*意思是最少包含2个a或无数个a
[root@shell datas]# grep "aaa*" test.txt
But since Mr. shen Chao came, he never saaaid those words. 5555nice!
because,actuaaaally,

grep "a" count4.sh 也行

练习 . 号 正则表达式“.”只能匹配一个字符,这个字符可以是任意字符

1
2
3
[root@shell datas]# grep "s..d" test.txt
Mr. Li Ming said:
Later,Mr. Li ming soid his hot body.

[root@shell datas]# grep “s.*d” test.txt
Mr. Li Ming said:
But since Mr. shen Chao came, he never saaaid those words. 5555nice!
Later,Mr. Li ming soid his hot body.

“[]”会匹配中括号中指定任意一个字符,注意只能匹配一个字符。比如[ao]要不会匹配一个 a

字符,要不会匹配一个 o 字符:

1
2
3
4
5
6
[root@shell datas]# grep "s[ao]id" test.txt
Mr. Li Ming said:
Later,Mr. Li ming soid his hot body.
[root@shell datas]# grep "[0-9]" test.txt
123despise him.
But since Mr. shen Chao came, he never saaaid those words. 5555nice!

扩展正则

扩展元字符 作 用
+ 前一个字符匹配 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”。

grep 参数列表

  1. -i : 忽略大小写
  2. -v : 查找不包含指定字符串的所有行(取反)
  3. -r : 递归查找文件夹下的文件
  4. -n : 显示匹配行所在位置(行号)
  5. -l : 只显示包含搜索字符串的文件名,而非每个匹配行
  6. -c : 统计符合条件的行数
  7. -e pattern : 指定要查找的正则表达式模式
  8. -w : 匹配整个单词,即只匹配独立的单词而非单词内的字符
  9. -A num : 输出匹配行后 N 行内容
  10. -B num : 输出匹配行前 N 行内容
  11. -C[num] 或者 --context[=num]: 输出匹配行前后总共 N 行内容。
  12. --exclude : 排除指定文件类型,多个文件类型用 “,” 隔开
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
[root@localhost sh]# grep -E "go*gle" test.txt
google
gooooogle
ggle
gogle
[root@localhost sh]# grep -E "go+gle" test.txt
google
gooooogle
gogle
[root@localhost sh]# grep -E "go?gle" test.txt
ggle
gogle
[root@localhost sh]# grep -E "go+" test.txt
google
gooooogle
gogle
[root@localhost sh]# grep -E "g(oo)+" test.txt
google
gooooogle
[root@localhost sh]# grep -E "g(ooo)+" test.txt
gooooogle
[root@localhost sh]# grep -E "hello (was|is)" test.txt
hello is
hello was

匹配邮箱

1
grep -E "[0-9a-zA-Z_]+@[0-9a-zA-Z_]+(\.[0-9a-zA-Z_]+){1,3}" test.txt

字符截取和替换命令

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

测试文件

1
2
3
4
5
6
[root@localhost ~]# cat student.txt
ID Name gender Mark
1 Liming M 86
2 Sc M 90
3 Tg M 83
用tab键隔开所有列,不要空格

测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
[root@localhost ~]# cut -f 2 student.txt
Name
Liming
Sc
Tg
#提取多列
[root@localhost ~]# cut -f 2,3 student.txt
Name gender
Liming M
Sc M
Tg M
##cut 可以按照字符进行提取,需要注意“8-”代表的是提取所有行的第八个字符开始到行尾,而 “10-20”代表提取所有行的第十个字符到第二十个字符,而“-8”代表提取所有行从行首到第八个
字符:
[root@localhost ~]# cut -c 9- student.txt
gender Mark
M 86
0
3
[root@localhost ~]# cut -c -9 student.txt
ID Name g
1 Liming
2 Sc M 90
3 Tg M 83

awk编程

printf 格式化输出

1
2
3
4
5
6
7
8
9
10
11
12
[root@localhost ~]# printf ‘输出类型输出格式’ 输出内容输出类型:
%ns: 输出字符串。n 是数字指代输出几个字符
%ni: 输出整数。n 是数字指代输出几个数字
%m.nf: 输出浮点数。m 和 n 是数字,指代输出的整数位数和小数位数。如%8.2f代表 共输出 8 位数,其中 2 位是小数,6 位是整数。
输出格式:
\a: 输出警告声音
\b: 输出退格键,也就是 Backspace 键
\f: 清除屏幕
\n: 换行
\r: 回车,也就是 Enter 键
\t: 水平输出退格键,也就是 Tab 键
\v: 垂直输出退格键,也就是 Tab 键

建立测试文件

1
2
3
4
5
vi student.txt
ID Name PHP Linux MySQL Average
1 Liming 82 95 86 87.66
2 Sc 74 96 87 85.66
3 Tg 99 83 93 91.66

不指定格式输出

1
2
3
[root@localhost ~]# printf '%s' $(cat student.txt)
IDNamePHPLinuxMySQLAverage1Liming82958687.662Sc74968785.663Tg99839391.66
#乱作一锅粥

指定格式输出

1
2
3
4
5
6
[root@localhost ~]# printf '%s\t %s\t %s\t %s\t %s\t %s\t \n' $(cat student.txt)
ID Name PHP Linux MySQL Average
1 Liming 82 95 86 87.66
2 Sc 74 96 87 85.66
3 Tg 99 83 93 91.66

awk条件

1
[root@localhost ~]# awk '条件 1{动作 1} 条件 2{动作 2}…' 文件名

awk条件(Pattern)

一般使用关系表达式作为条件。这些关系表达式非常多,具体参考表 12-3 所示,例如:

x > 10 判断变量 x 是否大于 10

x == y 判断变量 x 是否等于变量 y

A ~ B 判断字符串 A 中是否包含能匹配 B 表达式的子字符串

A !~ B 判断字符串 A 中是否不包含能匹配 B 表达式的子字符串

动作(Action):

格式化输出 流程控制语句

image-20230421143849290

awk内置变量

image-20230421190306772

例子:

1
2
[root@localhost ~]# awk '{printf $2 "\t" $6 "\n"}' student.txt
#输出第二列和第六列
1
2
3
4
5
6
7
8
9
10
11
12
13
假设我要提取根分区/dev/sda1 第五列的使用率
[root@localhost ~]# df -h
Filesystem Size Used Avail Use% Mounted on
devtmpfs 475M 0 475M 0% /dev
tmpfs 487M 0 487M 0% /dev/shm
tmpfs 487M 7.7M 479M 2% /run
tmpfs 487M 0 487M 0% /sys/fs/cgroup
/dev/mapper/centos-root 50G 1.5G 49G 3% /
/dev/mapper/centos-home 47G 33M 47G 1% /home
/dev/sda1 1014M 141M 874M 14% /boot
tmpfs 98M 0 98M 0% /run/user/0
[root@localhost ~]# df -h | grep "/dev/sda1"| awk '{print $5}'| cut -d "%" -f 1
14

Begin

1
2
3
4
5
6
[root@localhost ~]# awk 'BEGIN{printf "11111111\n" } {printf $2 "\t" $6 "\n"}' student.txt
11111111
Name Average
Liming 87.66
Sc 85.66
Tg 91.66

End

1
2
3
4
5
6
[root@localhost ~]# awk 'END{printf "111111111\n" } {printf $2 "\t" $6 "\n"}' student.txt
Name Average
Liming 87.66
Sc 85.66
Tg 91.66
111111111

假设我想看看平均成绩大于等于 87 分的学员是谁

1
2
3
4
5
6
7
8
9
[root@localhost ~]# cat student.txt
ID Name PHP Linux MySQL Average
1 Liming 82 95 86 87.66
2 Sc 74 96 87 85.66
3 Tg 99 83 93 91.66
[root@localhost ~]# cat student.txt | grep -v "Name" | awk '$6 >= 87{print $2}'
Liming
Tg

加入了条件之后,只有条件成立动作才会执行,如果条件不满足,则动作则不运行。通过这个实验,大家可以发现,虽然 awk 是列提取命令,但是也要按行来读入的。这个命令的执行过程是这样的:

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

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

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

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

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

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

1
2
3
[root@localhost ~]# awk '/Liming/ {print}' student.txt
1 Liming 82 95 86 87.66
#打印 Liming 的成绩

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

1
2
3
[root@localhost ~]# df -h | awk '/sda[0-9]/ {printf $1 "\t" $5 "\n"} '
/dev/sda1 14%
#查询包含有 sda 数字的行,并打印第一字段和第五字段

查看/etc/passwd文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
[root@localhost ~]# useradd user1
[root@localhost ~]# useradd user2
[root@localhost ~]# cat /etc/passwd | grep "/bin/bash"
root:x:0:0:root:/root:/bin/bash
user1:x:1000:1000::/home/user1:/bin/bash
user2:x:1001:1001::/home/user2:/bin/bash
[root@localhost ~]# cat /etc/passwd | grep "/bin/bash" | awk '{FS=":"} {print $1}'
root:x:0:0:root:/root:/bin/bash
user1
user2
#第一行root好像没有以冒号为分割读取第一列
是因为awk 先把第一行数据读取了
才进行{FS=":"}
直到上一步之后,才发现要以冒号作为分隔符
后面的user1 user2 才正常
###所以正确写法是BEGIN {FS=":"} 在读取数据之前,先执行{FS=":"}
[root@localhost ~]# cat /etc/passwd | grep "/bin/bash" | awk 'BEGIN {FS=":"} {print $1}'
root
user1
user2
1
2
3
[root@localhost ~]# cat /etc/passwd | grep "/bin/bash" | awk 'BEGIN{FS=":"} $3=="1000" {print $1}'
user1
判断是否相等 要用双=号
1
2
3
4
5
6
7
8
9
[root@localhost ~]# cat /etc/passwd | grep "/bin/bash" | \
> awk 'BEGIN {FS=":"} {printf $1 "\t" $3 "\t 行号:" NR "\t 字段数:" NF "\n"}'
root 0 行号:1 字段数:7
user1 1000 行号:2 字段数:7
user2 1001 行号:3 字段数:7

#如果我只想看sshd
[root@localhost ~]# cat /etc/passwd | awk 'BEGIN {FS=":"} $1=="sshd" {printf $1 "\t" $3 "\t 行号:"NR "\t 字段数:"NF "\n"}'
sshd 74 行号:20 字段数:7

Sed

sed命令

1
2
3
4
5
6
7
8
9
10
11
12
[root@localhost ~]# sed [选项] ‘[动作]’ 文件名
选项:
-n: 一般 sed 命令会把所有数据都输出到屏幕,如果加入此选择,则只会把经过 sed 命令处理 的行输出到屏幕。
-e: 允许对输入数据应用多条 sed 命令编辑。
-f 脚本文件名: 从 sed 脚本中读入 sed 操作。和 awk 命令的-f 非常类似。
-r: 在 sed 中支持扩展正则表达式。
-i: 用 sed 的修改结果直接修改读取数据的文件,而不是由屏幕输出动作:
a \: 追加,在当前行后添加一行或多行。添加多行时,除最后 一行外,
每行末尾需要用“\”代表数据未完结。
c \: 行替换,用 c 后面的字符串替换原数据行,替换多行时,除最后一行外,每行末尾需用“\”代表数据未完结。
i \:插入,在当期行前插入一行或多行。插入多行时,除最后 一行外,每行末尾需要用“\”代表数据未完结。
d:删除,删除指定的行。

打印p

1
2
3
4
5
6
7
8
9
[root@localhost ~]# sed 2p student.txt
ID Name PHP Linux MySQL Average
1 Liming 82 95 86 87.66
1 Liming 82 95 86 87.66
2 Sc 74 96 87 85.66
3 Tg 99 83 93 91.66
#你会发现第二行多打印了一遍,只有加-n才不会
[root@localhost ~]# sed -n '2p' student.txt
1 Liming 82 95 86 87.66

删除d(删除2到4行)

此操作并不会写入文件中,只是输出的时候删除了

1
2
3
4
5
6
7
8
9
10
11
12
[root@localhost ~]# sed '2,4d' student.txt
ID Name PHP Linux MySQL Average
[root@localhost ~]# cat student.txt
ID Name PHP Linux MySQL Average
1 Liming 82 95 86 87.66
2 Sc 74 96 87 85.66
3 Tg 99 83 93 91.66
若想直接修改读取数据到文件的话加入 -i
[root@localhost ~]# sed -i '2,4d' student.txt
[root@localhost ~]# cat student.txt
ID Name PHP Linux MySQL Average

前追加i

1
2
3
4
5
6
7
8
9
#在第二行前面插入内容
[root@localhost ~]# sed '2i 前面新内容1 \
> 前面新内容(大量) 2222222222222222222 ' student.txt
ID Name PHP Linux MySQL Average
前面新内容1
前面新内容(大量) 2222222222222222222
1 Liming 82 95 86 87.66
2 Sc 74 96 87 85.66
3 Tg 99 83 93 91.66

后追加a

1
2
3
4
5
6
7
8
在第二行后追加内容
[root@localhost ~]# sed -i '2a 新内容' student.txt
[root@localhost ~]# cat student.txt
ID Name PHP Linux MySQL Average
1 Liming 82 95 86 87.66
新内容
2 Sc 74 96 87 85.66
3 Tg 99 83 93 91.66

替换c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#把2行新内容替换为其他数据
[root@localhost ~]# cat student.txt
ID Name PHP Linux MySQL Average
1 Liming 82 95 86 87.66
2 Sc 74 96 87 85.66
3 Tg 99 83 93 91.66
[root@localhost ~]# sed '2c 0 xiaoshan 83 55 11 44.2' student.txt
ID Name PHP Linux MySQL Average
0 xiaoshan 83 55 11 44.2
2 Sc 74 96 87 85.66
3 Tg 99 83 93 91.66
#我前面是空格,若对齐用\t且\t后没有空格
[root@localhost ~]# sed '2c 0 \txiaos\t83 \t55 \t11 \t44.2' student.txt
ID Name PHP Linux MySQL Average
0 xiaos 83 55 11 44.2
2 Sc 74 96 87 85.66
3 Tg 99 83 93 91.66

多命令执行e

1
2
3
4
5
6
7
8
9
10
11
12
13
14
[root@localhost ~]# cat student.txt
ID Name PHP Linux MySQL Average
前面新内容1
前面新内容1
1 Liming 82 95 86 87.66
2 Sc 74 96 87 85.66
3 Tg 99 83 93 91.66
##同时删除第二和第三行
[root@localhost ~]# sed -i -e '2d;3d' student.txt
[root@localhost ~]# cat student.txt
ID Name PHP Linux MySQL Average
1 Liming 82 95 86 87.66
2 Sc 74 96 87 85.66
3 Tg 99 83 93 91.66

替换字符串 s/旧内容/新内容/g

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
#替换Liming为Lm
[root@localhost ~]# sed -i 's/Liming/Lm/g' student.txt
[root@localhost ~]# cat student.txt
ID Name PHP Linux MySQL Average
1 Lm 82 95 86 87.66
2 Sc 74 96 87 85.66
3 Tg 99 83 93 91.66
#多命令执行,分号隔开
[root@localhost ~]# sed -i 's/Sc/sc1/g; s/Tg/Eg/g' student.txt
[root@localhost ~]# cat student.txt
ID Name PHP Linux MySQL Average
1 Lm 82 95 86 87.66
2 sc1 74 96 87 85.66
3 Eg 99 83 93 91.66
#也可以多行执行
[root@localhost ~]# sed -i 's/Sc/sc1/g
s/Tg/Eg/g' student.txt
#替换字符串为空值
[root@localhost ~]# sed -i 's/Lm//g' student.txt
[root@localhost ~]# cat student.txt
ID Name PHP Linux MySQL Average
1 82 95 86 87.66
2 sc1 74 96 87 85.66
3 Eg 99 83 93 91.66

字符串处理命令

sort排序命令

1
2
3
4
5
6
7
8
[root@localhost ~]# sort [选项] 文件名选项:
-f: 忽略大小写
-b: 忽略每行前面的空白部分
-n: 以数值型进行排序,默认使用字符串型排序
-r: 反向排序
-u: 删除重复行。就是 uniq 命令
-t: 指定分隔符,默认是分隔符是制表符
-k n[,m]: 按照指定的字段范围排序。从第 n 字段开始,m 字段结束(默认到行尾)
1
2
3
4
5
6
7
8
9
10
11
12
13
#默认按开头字符排序
[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
chrony:x:997:995::/var/lib/chrony:/sbin/nologin
#反向排序
[root@localhost ~]# sort -r /etc/passwd
user2:x:1001:1001::/home/user2:/bin/bash
user1:x:1000:1000::/home/user1:/bin/bash
tcpdump:x:72:72::/:/sbin/nologin
systemd-network:x:192:192:systemd Network Management:/:/sbin/nologin
sync:x:5:0:sync:/sbin:/bin/sync

指定字段排序

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
#如UID
[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
user1:x:1000:1000::/home/user1:/bin/bash
user2:x:1001:1001::/home/user2:/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
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
adm:x:3:4:adm:/var/adm:/sbin/nologin
#你会发现他先排第一位数字1,再排2。、
#就是1 12 13 到 2 22 23 并没有123456排下去
#所以要加上 -n -n:以数值型进行排序,默认使用字符串型排序
[root@localhost ~]# sort -nt ":" -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

uniq取消重复行

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

1
2
[root@localhost ~]# uniq [选项] 文件名选项:
-i: 忽略大小写
1
2
3
4
5
6
7
8
9
10
11
[root@localhost ~]# cat student.txt
ID Name PHP Linux MySQL Average
1 82 95 86 87.66
1 82 95 86 87.66
2 sc1 74 96 87 85.66
3 Eg 99 83 93 91.66
[root@localhost ~]# uniq student.txt
ID Name PHP Linux MySQL Average
1 82 95 86 87.66
2 sc1 74 96 87 85.66
3 Eg 99 83 93 91.66

wc统计命令

1
2
3
4
[root@localhost ~]# wc [选项] 文件名选项:
-l: 只统计行数
-w: 只统计单词数
-m: 只统计字符数
1
2
3
4
5
6
7
8
9
10
11
12
13
[root@localhost ~]# cat student.txt
ID Name PHP Linux MySQL Average
1 82 95 86 87.66
1 82 95 86 87.66
2 sc1 74 96 87 85.66
3 Eg 99 83 93 91.66
[root@localhost ~]# wc -l student.txt
5 student.txt
[root@localhost ~]# wc -w student.txt
28 student.txt
[root@localhost ~]# wc -m student.txt
109 student.txt

条件判断(test命令)

  1. 按照文件类型进行判断(test)
测试选项 作 用
-b 文件 判断该文件是否存在,并且是否为块设备文件(是块设备文件为真)
-c 文件 判断该文件是否存在,并且是否为字符设备文件(是字符设备文件为真)
-d 文件 判断该文件是否存在,并且是否为目录文件(是目录为真)
-e 文件 判断该文件是否存在(存在为真)
-f 文件 判断该文件是否存在,并且是否为普通文件(是普通文件为真)
-L 文件 判断该文件是否存在,并且是否为符号链接文件(是符号链接文件为真)
-p 文件 判断该文件是否存在,并且是否为管道文件(是管道文件为真)
-s 文件 判断该文件是否存在,并且是否为非空(非空为真)
-S 文件 判断该文件是否存在,并且是否为套接字文件(是套接字文件为真)
1
2
3
4
5
6
7
8
9
10
11
12
[root@localhost ~]# ls
anaconda-ks.cfg file.txt file.txtn netstart.bak student.txt
第一种格式:#-e 判断该文件是否存在(存在为真)
[root@localhost ~]# test -e file.txt
#输出为0即为真
[root@localhost ~]# echo $?
0
第一种格式:#-e 判断不存在的文件
[root@localhost ~]# [ -e abc ]
[root@localhost ~]# echo $?
1
#输出为非0即为假
1
2
3
4
5
6
7
###判断该文件是否存在,并且是否为目录文件
[root@localhost ~]# [ -d student.txt ] && echo yes || echo no
no
###判断该文件是否存在,并且是否为普通文件
[root@localhost ~]# [ -f student.txt ] && echo yes || echo no
yes

1
2
3
4
5
6
###判断文件是否有数据
[root@localhost ~]# [ -s abc ] && echo yes || echo no
no
[root@localhost ~]# echo 111 >> abc
[root@localhost ~]# [ -s abc ] && echo yes || echo no
yes
  1. 按照文件权限判断
测试选项 作 用
-r 文件 判断该文件是否存在,并且是否该文件拥有读权限(有读权限为真)
-w 文件 判断该文件是否存在,并且是否该文件拥有写权限(有写权限为真)
-x 文件 判断该文件是否存在,并且是否该文件拥有执行权限(有执行权限为真)
-u 文件 判断该文件是否存在,并且是否该文件拥有 SUID 权限(有 SUID 权限为真)
-g 文件 判断该文件是否存在,并且是否该文件拥有 SGID 权限(有 SGID 权限为真)
-k 文件 判断该文件是否存在,并且是否该文件拥有 SBit 权限(有 SBit 权限为真)
1
2
3
4
5
6
7
8
9
10
11
12
13
##判断文件是否有写的权限
[root@localhost ~]# [ -w abc ] && echo yes || echo no
yes
[root@localhost ~]# [ -u abc ] && echo yes || echo no
no
##判断文件是否有SUID的权限
[root@localhost ~]# chmod u+s abc
[root@localhost ~]# [ -u abc ] && echo yes || echo no
yes
[root@localhost ~]# ll
total 24
-rwSr--r-- 1 root root 4 May 2 03:02 abc

  1. 两个文件之间进行比较
测试选项 作 用
文件 1 -nt 文件 2 判断文件 1 的修改时间是否比文件 2 的新(如果新则为真)
文件 1 -ot 文件 2 判断文件 1 的修改时间是否比文件 2 的旧(如果旧则为真)
文件 1 -ef 文件 2 判断文件 1 是否和文件 2 的 Inode 号一致,可以理解为两个文件是否 为同一个文件。这个判断用于判断硬链接是很好的方法
1
2
3
4
5
6
7
8
##判断两个文件是否是硬链接
[root@localhost ~]# ln /root/abc /tmp/abc
[root@localhost ~]# ll /tmp/abc
-rw-r--r-- 2 root root 4 May 2 03:02 /tmp/abc

[root@localhost ~]# [ /root/abc -ef /tmp/abc ] && echo yes || echo no
yes

  1. 两个整数之间比较
测试选项 作 用
整数 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(小于等于为真)

-eq: equal : 相等 -ne: not equal : 不相等

-gt: greater than : 大于 -lt: less than : 小于

1
2
3
4
5
6
7
8
9
##判断整数1是否等于整数2
[root@localhost ~]# [ 22 -eq 22 ] && echo yes || echo no
yes
##判断整数1是否小于整数2
[root@localhost ~]# [ 22 -lt 22 ] && echo yes || echo no
no
[root@localhost ~]# [ 21 -lt 22 ] && echo yes || echo no
yes

  1. 字符串判断
测试选项 作 用
-z 字符串 判断字符串是否为空(为空返回真)
-n 字符串 判断字符串是否为非空(非空返回真)
字串 1 ==字串 2 判断字符串 1 是否和字符串 2 相等(相等返回真)
字串 1 != 字串 2 判断字符串 1 是否和字符串 2 不相等(不相等返回真)
1
2
3
4
5
6
7
8
9
10
11
12
13
##判断两个字符串相等
[root@localhost ~]# aa=11
[root@localhost ~]# bb=22
[root@localhost ~]# [ "$aa" == 8 ] && echo yes || echo no
no
[root@localhost ~]# [ $aa == $bb ] && echo yes || echo no
no
[root@localhost ~]# [ "$aa" == 11 ] && echo yes || echo no
yes
##判断字符是否为空
[root@localhost ~]# name=xx
[root@localhost ~]# [ -z $name ] && echo yes || echo no
no
  1. 多重条件判断
测试选项 作 用
判断 1 -a 判断 2 逻辑与,判断 1 和判断 2 都成立,最终的结果才为真
判断 1 -o 判断 2 逻辑或,判断 1 和判断 2 有一个成立,最终的结果就为真
!判断 逻辑非,使原始的判断式取反
1
2
3
4
5
#先给aa赋值
[root@localhost ~]# aa=24
#-n先判断是否为空,明显不为空则为真,真就继续判断是否大于23
[root@localhost ~]# [ -n "$aa" -a "$aa" -gt 23 ] && echo yes || echo no
yes
1
2
3
4
5
6
##逻辑非
[root@localhost ~]# [ ! -n "$aa" ] && echo "yes" || echo "no"
no
#本来“-n”选项是变量 aa 不为空,返回值就是真。
#加入!之后,判断值就会取反,所以当变量 aa 有值时,返回值是假

流程控制

if条件判断

  1. 单分支 if 条件语句

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

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

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

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

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

· then 后面跟符合条件之后执行的程序,可以放在[]之后,用“;”分割。也可以换行写入,就不需要“;”了,比如单分支 if 语句还可以这样写:

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

例子:判断sda1并设置警告信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
[root@localhost ~]# df -h
Filesystem Size Used Avail Use% Mounted on
/dev/sda1 1014M 141M 874M 14% /boot
[root@localhost ~]# df -h | grep "/dev/sda1" | awk '{print $5}' | cut -d "%" -f 1
14


[root@localhost ~]# vi if1.sh
#!/bin/bash
#统计分区使用率
rate=$(df -h | grep "/dev/sda1" | awk '{print $5}'| cut -d "%" -f 1)

#把根分区使用率作为变量值赋予变量rate
if [ $rate -ge 14 ]
#判断 rate 的值如果大于等于 14,则执行then 程序
then
echo "Warning! /dev/sda1 快满了!"
#打印警告信息。在实际工作中,也可以向管理员发送邮件。
fi
[root@localhost ~]# sh if1.sh
Warning! /dev/sda1 快满了!
  1. 双分支if语句
1
2
3
4
5
6
if [ 条件判断式 ]
then
条件成立时,执行的程序
else
条件不成立时,执行的另一个程序
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
36
#备份mysql数据库(并不完善,但可以使用)
[root@localhost ~]# vi sh/bakmysql.sh
#!/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 dbinfo.txt /var/lib/mysql &>/dev/null
#压缩备份数据库与临时文件
rm -rf /tmp/dbbak/dbinfo.txt
#删除临时文件
fi

常用字符表

echo

在 echo 命令中如果使用了“-e”选项,则可以支持控制字符,如表 11-2 所示:

控制字符 作 用
\ 输出\本身
\a 输出警告音
\b 退格键,也就是向左删除键
\c 取消输出行末的换行符。和“-n”选项一致
\e ESCAPE 键
\f 换页符
\n 换行符
\r 回车键
\t 制表符,也就是 Tab 键
\v 垂直制表符
\0nnn 按照八进制 ASCII 码表输出字符。其中 0 为数字零,nnn 是三位八进制数
\xhh 按照十六进制 ASCII 码表输出字符。其中 hh 是两位十六进制数

Bash常用快捷键

快捷键 作 用
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 恢复屏幕输出。

基础正则表达式

元字符 作 用
* 前一个字符匹配 0 次或任意多次。
. 匹配除了换行符外任意一个字符。
^ 匹配行首。例如:^hello 会匹配以 hello 开头的行。
$ 匹配行尾。例如:hello&会匹配以 hello 结尾的行。
[] 匹配中括号中指定的任意一个字符,只匹配一个字符。例如:[aoeiu] 匹配任意一个元音字母,[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 位的小写字母。

扩展正则

扩展元字符 作 用
+ 前一个字符匹配 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”。

grep 参数列表

  1. -i : 忽略大小写
  2. -v : 查找不包含指定字符串的所有行(取反)
  3. -r : 递归查找文件夹下的文件
  4. -n : 显示匹配行所在位置(行号)
  5. -l : 只显示包含搜索字符串的文件名,而非每个匹配行
  6. -c : 统计符合条件的行数
  7. -e pattern : 指定要查找的正则表达式模式
  8. -w : 匹配整个单词,即只匹配独立的单词而非单词内的字符
  9. -A num : 输出匹配行后 N 行内容
  10. -B num : 输出匹配行前 N 行内容
  11. -C[num] 或者 --context[=num]: 输出匹配行前后总共 N 行内容。
  12. --exclude : 排除指定文件类型,多个文件类型用 “,” 隔开

cut

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

awk条件(Pattern)

image-20230421143849290

awk内置变量

image-20230421190306772

特别声明
千屹博客旗下的所有文章,是通过本人课堂学习和课外自学所精心整理的知识巨著
难免会有出错的地方
如果细心的你发现了小失误,可以在下方评论区告诉我,或者私信我!
非常感谢大家的热烈支持!