来源:https://www.cnblogs.com/Alpacapyer/p/11857950.html
```bash
[root@CodeSheep ~]# ll /usr/bin/awk
lrwxrwxrwx. 1 root root 4 Feb 15 2019 /usr/bin/awk -> gawk
```
## 关于"三剑客"的特长
- grep 更适合单纯的查找和匹配文本
- sed 更适合编辑匹配的文本
- awk 更适合格式化文本,对文本进行较复杂格式处理
## awk基础
**语法**
awk [options] '{pattern + action}' {filenames}
其中的action 我们最常用的就是print以及prinf,对于action来说,每次经过一行,都会当前行执行一边action
比如
```bash
awk '{print "1",NR}' /etc/passwd
```
你会发现有多少行,他就输出了多少个1 每行一个
## awk的工作流程
首先awk并不是列操作,而是行操作,同样的他也是一行一行的处理的,其中$0表示当前行,比如在正常情况下我们输出全文是用cat来查看的,那么用awk的操作是这样的,另外awk的接受标准输入和文件
```bash
cat /etc/passwd
awk '{print $0}' /etc/passwd
# 如上的内容是一样的
```
## awk中的分隔符
为了处理好每一行中的每一个字段,awk引入了分隔符的概念,分隔符有三种表现的形式,
一种是不输入任何不指定任何,则默认为空格
一种是自定义的分隔符 使用 -F 指定 比如 -F:
另外一种是使用 内置变量指定 -v FS='#'
**自定义分隔符**
既然默认的分隔符是空格,那么当然还有自定义的分隔符咯。我们使用-F选项来指定我们的分隔符
```bash
# 使用#分隔符
[root@CodeSheep ~]# cat demo3.text
123#123#dsdshj#dlsj#
[root@CodeSheep ~]# awk -F# '{print $1}' demo3.text
123
[root@CodeSheep ~]# cat demo3.text
123#123#dsdshj#dlsj#
[root@CodeSheep ~]# awk -v FS='#' '{print $1}' demo3.text
123
```
其实在awk中不仅仅有输入的分割符,还有输出的分隔符,默认也为空格
```bash
[root@CodeSheep ~]# awk -F# '{print $1,$2}' demo3.text
123 123
```
OFS(output field separator)可以指定输出的分隔符,用法与FS相同
```bash
[root@CodeSheep ~]# awk -v FS='#' -v OFS='++++' '{print $1,$2}' demo3.text
123++++123
```
## awk的内建变量
awk支持内建变量和自定义变量
**内建变量**
- $0 当前行
- $1~$n 第n个字段
- $NF 最后一个字段
- NF 当前行被分割字段总数
- NR 当前行行号
- FNR 各文件分别计数的行号
- FS 输入分隔符
- OFS 输出分隔符
- RS 输入换行符
- ORS 输出换行符
- FILENAME 当前文件名
- ARGC 命令行参数的个数
- ARGV 数组,保存的命令行所给定的各参数
```bash
# 给每一行添加行号,输出当前行号,以及当前行的内容
[root@CodeSheep ~]# df | awk '{print NR,$0}'
1 Filesystem 1K-blocks Used Available Use% Mounted on
2 /dev/vda1 41147472 6848400 32395580 18% /
3 devtmpfs 930656 0 930656 0% /dev
4 tmpfs 941116 0 941116 0% /dev/shm
5 tmpfs 941116 452 940664 1% /run
6 tmpfs 941116 0 941116 0% /sys/fs/cgroup
7 tmpfs 188224 0 188224 0% /run/user/0
# 多个文件使用NR时,会根据的文件顺序累加序号
# FNR则会分开标序
# ARGV,ARGC
[root@CodeSheep ~]# awk 'BEGIN{print ARGV[0],ARGV[1],ARGV[2],ARGC}' demo1.txt demo2.text
awk demo1.txt demo2.text 3
# 其中ARGV作为数组,第一个参数是awk
```
**自定义变量名**
自定义变量的两种形式,一种是在action外面 使用选项指定变量,比如 -v name="hzj"
另外一种则是在action前的pattern中定义,比如 {name="hzj"; action->print name}
```bash
[root@CodeSheep ~]# awk -v name="hzj" 'BEGIN{print name}'
hzj
[root@CodeSheep ~]# awk 'BEGIN{name="hzj";print name}'
hzj
[root@CodeSheep ~]# name=hzj
[root@CodeSheep ~]# awk -v name=$name 'BEGIN{print name}'
hzj
```
## awk 参数
### options
-v name="xx" -v外部定义变量
### pattern+action
**抛开Pattern 我们先来使用{action}**
```bash
# 输出文本内容
[root@CodeSheep ~]# df
Filesystem 1K-blocks Used Available Use% Mounted on
/dev/vda1 41147472 6848500 32395480 18% /
devtmpfs 930656 0 930656 0% /dev
tmpfs 941116 0 941116 0% /dev/shm
tmpfs 941116 452 940664 1% /run
tmpfs 941116 0 941116 0% /sys/fs/cgroup
tmpfs 188224 0 188224 0% /run/user/0
[root@CodeSheep ~]# df | awk '{print $1}'
Filesystem
/dev/vda1
devtmpfs
tmpfs
tmpfs
tmpfs
tmpfs
```
awk是逐行处理的,也就是说当awk处理一个文本的时候,他会一行一行的处理内容。其中awk默认以 换行符 为标记识别每一行。另外对于每一行的处理中 awk会按照用户指定的 分隔符 去分隔当前行,如果没有指定,则默认使用空格作为分隔符,当出现多个空格的时候,awk会自动将连续的空格理解成为一个分隔符。
其中 我们将被awk处理后的每一行都使用了特定的变量标记
$0表示当前处理的整行
$1表示第一个字段
$2表示第二个字段
$NF表示最后一个字段
NF表示当前行被分割后字段总数
因此 假设一行文本被空格分为8段,则$NF表示$8 NF=8 则$7=$(NF-1)
```bash
# 输出多行
[root@CodeSheep ~]# df | awk '{print $1 $2 $3}'
Filesystem1K-blocksUsed
/dev/vda1411474726848536
devtmpfs9306560
tmpfs9411160
tmpfs941116452
tmpfs9411160
tmpfs1882240
[root@CodeSheep ~]# df | awk '{print $1, $2 ,$3}'
Filesystem 1K-blocks Used
/dev/vda1 41147472 6848536
devtmpfs 930656 0
tmpfs 941116 0
tmpfs 941116 452
tmpfs 941116 0
tmpfs 188224 0
# 自己添加字段
[root@CodeSheep ~]# df | awk '{print $1 ,"this print test"}'
Filesystem this print test
/dev/vda1 this print test
devtmpfs this print test
tmpfs this print test
tmpfs this print test
tmpfs this print test
tmpfs this print test
[root@CodeSheep ~]# df | awk '{print "$1="$1 , "testfield:""this print test"}'
$1=Filesystem testfield:this print test
$1=/dev/vda1 testfield:this print test
$1=devtmpfs testfield:this print test
$1=tmpfs testfield:this print test
$1=tmpfs testfield:this print test
$1=tmpfs testfield:this print test
$1=tmpfs testfield:this print test
#看上面的案例我们可以发现,当$1被双引号包裹的时候,他就是一个字符串,不再是变量
```
awk中的Pattern
其实action的主要操作就是print输出,简单来用就是输出内容,更复杂的就是对输出的内容进行格式化的操作。而Pattern所存在的两种模式,愈加加强了awk的能力。
## awk中的逻辑·
为了更好的格式化,awk中也带有逻辑参数,其中包括开始BEGIN和结尾END,他们之间用{}分隔,比如
```bash
awk -v test="test" 'BEGIN{print "1",NR}{print test}END{print "input end" }' /etc/passwd
```
你会发现他输出了很多个test,首先begin进入,然后输出1和行号0 紧接着不断的进入行输出test 在最后一行输入时 执行input end
**特殊模式下的BEGIN与END**
- BEGIN 模式指定了处理文本之前需要执行的操作
- END 模式指定了处理完所有行之后所需要执行的操作
```bash
[root@CodeSheep ~]# df | awk 'BEGIN{print "$1","$2"}'
$1 $2
[root@CodeSheep ~]# df | awk 'BEGIN{print "$1","$2"} {print $1,$2}'
$1 $2
Filesystem 1K-blocks
/dev/vda1 41147472
devtmpfs 930656
tmpfs 941116
tmpfs 941116
tmpfs 941116
tmpfs 188224
[root@CodeSheep ~]# df | awk 'BEGIN{print "$1","$2"} {print $1,$2} END{print "end for file"}'
$1 $2
Filesystem 1K-blocks
/dev/vda1 41147472
devtmpfs 930656
tmpfs 941116
tmpfs 941116
tmpfs 941116
tmpfs 188224
end for file
```
在BEGIN的模式下,首先awk会执行BEGIN中的action 之后才做去执行其他的action,同理,在执行完所有的action之后,awk回去执行END模式下面的action。需要注意的是两个特殊的模式BEGIN以及END都需要大写
于是 awk的格式化能力表露无疑了,提取字段再加上BEGIN与END的两种特殊模式,一张完整的表不就出来了嘛
、
### awk的格式化
在了解awk的格式化之前
### 聊一聊awk的Pattern模式
在之前我们已经了解了两个特殊的模式BEGIN 和 END,接下里我们说一说普通模式
awk解析文本的大致流程为:
1.接受文本后逐行处理
2.把当前的pattern模式当成条件去匹配当前行,如果匹配成功,则处理当前行,如果不成功,则不处理.
3.执行下一行
```bash
[root@CodeSheep ~]# cat demo4.txt
aaa
bbb ccc ddd eee
qqq ccc
ddd zzz qqq fff
[root@CodeSheep ~]# awk 'NF==4{print $0}' demo4.txt
bbb ccc ddd eee
ddd zzz qqq fff
```
**关系运算符**
```text
= 大于等于 x>=y
> 大于 x>y
~ 正则匹配 x ~ /正则/
!~ 正则不匹配 x !~ /正则/
```
### awk中的正则模式
既然上面的关系运算符中出现了正则,那我们就来讲一讲正则模式
需要注意的是 当使用{x,y}这种次数匹配的正则表达式时,需要配合--posix选项或者--re-interval选项
```bash
# 匹配/etc/passwd 中以 tss开头的行
[root@CodeSheep ~]# cat /etc/passwd | awk '/tss/{print $0}'
tss:x:59:59:Account used by the trousers package to sandbox the tcsd daemon:/dev/null:/sbin/nologin
# 匹配/etc/passwd 中以 /bin/bash结尾的行
[root@CodeSheep ~]# cat /etc/passwd | awk '/\/bin\/bash$/{print $0}'
root:x:0:0:root:/root:/bin/bash
# 匹配/etc/passwd 中以/bin/bash结尾的行 到以 tss开头的行
[root@CodeSheep ~]# cat /etc/passwd | awk '/\/bin\/bash$/,/^tss/{print $0}'
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
sync:x:5:0:sync:/sbin:/bin/sync
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
halt:x:7:0:halt:/sbin:/sbin/halt
mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
operator:x:11:0:operator:/root:/sbin/nologin
games:x:12:100:games:/usr/games:/sbin/nologin
ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin
nobody:x:99:99:Nobody:/:/sbin/nologin
systemd-network:x:192:192:systemd Network Management:/:/sbin/nologin
dbus:x:81:81:System message bus:/:/sbin/nologin
polkitd:x:999:998:User for polkitd:/:/sbin/nologin
sshd:x:74:74:Privilege-separated SSH:/var/empty/sshd:/sbin/nologin
postfix:x:89:89::/var/spool/postfix:/sbin/nologin
chrony:x:998:996::/var/lib/chrony:/sbin/nologin
ntp:x:38:38::/etc/ntp:/sbin/nologin
tcpdump:x:72:72::/:/sbin/nologin
nscd:x:28:28:NSCD Daemon:/:/sbin/nologin
mysql:x:27:27:MySQL Server:/var/lib/mysql:/bin/false
dockerroot:x:997:994:Docker User:/var/lib/docker:/sbin/nologin
tss:x:59:59:Account used by the trousers package to sandbox the tcsd daemon:/dev/null:/sbin/nologin
# 匹配行号大于2 且小于6的行
[root@CodeSheep ~]# cat /etc/passwd | awk 'NR>2 && NR https://www.baidu.com
echo '${url}' -------> ${url}
将命令结果附给变量
var=`command`
var=$(command)
将文件log.txt附加给log
echo $`cat log.txt`
echo $(cat log.txt)
只读变量
url="https://wwww.baidu.com"
readonly url
删除变量
url="https://www.baidu.com"
unset url
获取变量长度
url="https://www.baidu.com"
echo {#url}
字符串截取
${string:start:length}
截取位置2到位置9的内容
url="https://www.baidu.com"
echo ${url:2:9}
截取位置2到末尾的内容
echo ${url:2}
```
### shell脚本的数组
```
用()表示数组
array=(array1,array2,array3...)
获取数组内容
${array1[1]}
获取所有内容
${array1[*]} 或者 ${array1[@]}
无需逐个元素地给数组赋值,下面的代码就是只给特定元素赋值
args=([3]=23 [5]=19 [10]=12)
删除数组元素
unset nums[1]
```
### shell脚本的基本指令
let 命令是 BASH 中用于计算的工具,用于执行一个或多个表达式,变量计算中不需要加上 $ 来表示变量。如果表达式中包含了空格或其他特殊字符,则必须引起来。
```tex
自加操作:let a++
自减操作:let c--
递加操作:let num+=10
递减操作:let num-=10
```
--------------
declare为shell指令,在第一种语法中可用来声明变量并设置变量的属性([rix]即为变量的属性),在第二种语法中可用来显示shell函数。若不加上任何参数,则会显示全部的shell变量与函数(与执行set指令的效果相同)。
语法:
declare [+/-][rix][变量名称=设置值]
- +/- 可以用来设定变量的属性 + 取消属性 -附加属性
- [rix] r 将变量设置为只读 i[设置值]可以是数值 字符串 或者运算式 x 指定变量会成为环境变量,可供shell以外的程序来使用
shell编成中在定义变量的时候,=附近不能出现空格。
```tex
用declare定义变量
declare -i name_index1="小明"
定义变量
name_index2="小红"
echo $name_index2
echo $name_index1
echo ${name_index1}is studying 加{}可以作为区分其他字符的边界
```
-----------------
read从键盘读取变量的值,该命令可以一次读取多个变量的值,但是变量和输入的值都需要使用空格隔开。如果read后面没有指定的变量名,则自动复制给变量REPLY
语法:
read(选项)(参数)
-p 指定数值时的提示符
-t 指定读取值时等待的时间
-a 读取内容到数组中,元素之间用空格隔开
-r 允许包含反斜杠
-d 指定符号结束输入
-s 输入时不现实内容
```tex
读取并赋值给 xx
read xx
读取内容到数组中
read -a array
输出数组中的内容
echo ${array[0]},${array[1]}
输入前给出提示
read -p "test" xx
输入等待时间3s
read -t 3 xx
指定 ";" 结束输入
read -d ";" var
```
----------------------------------------------------------------
### shell脚本之while循环
```tex
多行
while 条件;do
循环体
done
单行
while 条件;do 循环体;done
```
**常用方法**
```tex
无限循环输出累加
num=1;while ((1));do let num+=1; echo $num;done
计算1到10的和
#!/bin/bash
declare a=1
declare sum=0
while ((a