shell编程-AWK

一、awk简介

awk 是一种编程语言,用于在linux/unix下对文本和数据进行处理。

数据可以来自标准输入、一个或多个文件,或其它命令的输出。

支持用户自定义函数和动态正则表达式等先进功能,是linux/unix
下的一个强大编程工具。

在命令行中使用,但更多是作为脚本来使用。

awk的处理文本和数据的方式是这样的,它逐行扫描文件,从第一行到最后一行,寻找匹配的特定模式的行,并在这些行上进行你想要的操作。如果没有指定处理动作,则把匹配的行显示到标准输出(屏幕),如果没有指定模式,则所有被操作所指定的行都被处理。

awk分别代表其作者姓氏的第一个字母。因为它的作者是三个人,分别是Alfred Aho、Brian Kernighan、Peter Weinberger。

gawk是awk的GNU版本,它提供了Bell实验室和GNU的一些扩展。

二、awk的两种形式语法格式

awk [options] 'commands’ file1 file2

awk [options] -f awk-script-file filenames

options:

-F 对于每次处理的内容,可以指定一个子定义的分隔符,默认的分隔符是空白字符(空格或 tab 键 )

command:

BEGIN{}                        {}               END{}

处理所有内容之前的动作       处理内容中的动作   处理所有内容之后的动作

示例

 awk 'BEGIN{print "----开始处理了---"} {print "ok"} END{print "----都处理完毕---"}' /etc/hosts
----开始处理了---
ok
ok
ok
----都处理完毕---

BEGIN{} 通常用于定义一些变量,例如 BEGIN{FS=":";OFS="---"}

三、awk工作原理

[root@5e4b448b73e5 ~]# awk -F:  '{print $1,$3}' /etc/passwd
root 0
bin 1
daemon 2
adm 3
...略...

(1)awk,会处理文件的每一个行,每次处理时,使用一行作为输入,并将这一行赋给内部变量$0,每一行也可称为一个记录,以换行符结束

(2)然后,行被**😗*(默认为空格或制表符)分解成字段(或称为域),每个字段存储在已编号的变量中,从$1开始,
最多达100个字段

(3)awk如何知道用空白字符来分隔字段的呢? 因为有一个内部变量FS来确定字段分隔符。初始时,FS赋为空白字符

(4)awk打印字段时,将以内置的方法使用 print 函数打印,awk 在打印出的字段间加上空格。这个空格是内部的一个变量 OFS 输出字段的分隔符, 逗号 , 会和 OFS 进行映射,通过 OFS 可以控制这个输出分隔符的值。

(5)awk输出之后,将从文件中获取另一行,并将其存储在$0中,覆盖原来的内容,然后将新的字符串分隔成字段并进行处理。该过程将持续到所有行处理完毕

四、记录与字段相关内部变量:

查看帮助:

man awk

$0 : awk变量 $0 保存当前正在处理的行内容
NR : 当前正在处理的行是 awk 总共处理的第几行。
FNR: 当前正在处理的行在其文件中的行号。
NF :每行被处理时的总字段数
$NF: 当前处理行的分隔后的最后一个字段的值
FS : 输入行时的字段分隔符,默认空白字符(空格 tab键)

OFS : 输出字段分隔符,默认是一个 空格

awk 'BEGIN{FS=":"; OFS="+++"} /^root/{print $1,$2,$3,$4}' /etc/passwd

ORS 输出记录分隔符, 默认是换行符.

示例

将文件每一行合并为一行

ORS默认输出一条记录应该回车,但是这里是加了一个空格

awk 'BEGIN{ORS="  "} {print $0}' /etc/passwd 

输出:

root:x:0:0:root:/root:/bin/bash  bin:x:1:1:bin:/bin:/sbin/nologin  daemon:x:2:2:daemon:/sbin:/sbin/nologin  adm:x:3:4:adm:/var/adm:/sbin/nologin  lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin  sync:x:5:0:sync:/sbin:/bin/sync  shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown  halt:x:7:0:halt:/sbin:/sbin/halt  mail:x:8:12:mail:/var/spool/mail:/sbin/nologin  operator:x:11:0:operator:/root:/sbin/nologin  games:x:12:100:games:/usr/games:/sbin/nologin  ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin  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  tss:x:59:59:Account used by the trousers package to sandbox the tcsd daemon:/dev/null:/sbin/nologin  [root@5e4b448b73e5 ~]#

五、格式化输出:

printf 函数

awk -F: '{printf "%-15s %-10s %-15s\n", $1,$2,$3}' /etc/passwd
awk -F: '{printf "|%-15s| %-10s| %-15s|\n", $1,$2,$3}' /etc/passwd
  • %s 字符类型
  • %d 十进制整数
  • %f 浮点类型 ,保留小数点后 2 位printf "%.2f\n" 10
  • %-15s占15字符 - 表示左对齐,默认是右对齐
  • printf 默认不会在行尾自动换行,加 \n

六、awk模式和动作

任何 awk 语句都由 模式动作 组成。

模式部分 决定动作语句何时被触发。
如果省略模式部分,命令中的动作讲作用于每一行。

模式可以是

  • 正则表达式
  • 逻辑表达式
  • 包含正则表达式和逻辑表达式的复合语句

1 正则表达式:

  • 将整行进行正则匹配(包含):

就是当前处理的行有没有包含 指定的模式(书写的正则表达式)
/正则/ 正则需要写在双斜线内

! 用于取反,就是找到不匹配正则模式的行

AWK默认的动作就是打印出整行,所以当打印整行内容的时候,打印的动作命令可以省略。

awk '/^root/' /etc/passwd
awk '!/^root/' /tec/ passwd
  • 将某一字段进行正则匹配:

可以使用的匹配操作符(~!~
字段 ~ /正则/

awk -F: '$3 ~ /^1/' /etc/passwd
awk -F: '$NF !~ /bash$/' /etc/passwd
  • 匹配开头是 bin 的或者开头是 root 的行
awk -F: '/^(bin|root)/' /etc/passwd
# 输出
root:x:0:0:root:/root:/bin/zsh
bin:x:1:1:bin:/bin:/sbin/nologin

2 逻辑表达式

逻辑表达式采用对文本进行比较,只有当条件为真,才执行指定的动作。
逻辑表达式使用***关系运算符***进行两个值的比较,可以用于比较数字与字符串。

实现 字符串的完全相等需要使用 ==!=

其中字符串需要使用双引号引起来

awk -F: '$NF == "/bin/bash"' /etc/passwd
awk -F: '$1 == "root"' /etc/passwd

其他的运算符号:

关系运算符有
< 小于 例如 x<y
> 大于 例如 x>y
<= 小于或等于 例如 x<=y
== 等于 例如 x==y
!= 不等于 例如 x!=y
>= 大于等于 例如x>=y`

示例

awk -F: '$3 == 0' /etc/passwd
awk -F: '$3 < 10' /etc/passwd

df -P | grep '/' |awk '$4 > 25000 {print $0}'
  • 算术运算:+, -, *, /, %(模: 取余), ^(幂:2^3)

可以在逻辑表达式中执行计算,awk都将按浮点数方式执行算术运算

awk -F: '$3 * 10 > 5000{print $0}' /etc/passwd

3 复合模式

符合模式中,通常会使用逻辑运算符号

&& 逻辑与, 相当于 并且
||逻辑或,相当于 或者
! 逻辑非 , 取反

awk -F: '$1~/root/ && $3<=15' /etc/passwd
awk -F: '$1~/root/ || $3<=15' /etc/passwd
awk -F: '!($1~/root/ || $3<=15)' /etc/passwd

4 范围模式

模式之间用逗号 , 隔开

使用语法是: 起始表达式, 终止表达式

下面的意思是: 从开头是 bin 的行开始匹配成功一直到含有 adm 的行结束匹配
也就是 开头是 bin 的行到含有 adm 的行 的所有内容都符合匹配条件。

awk -F: '/^bin/,/adm/ {print $0 }' /etc/passwd
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

范围模式不会与其他模式相结合

下面d 操作是无效的

[root@shark ~]# echo Yes | awk '/1/,/2/ || /Yes/'
[root@shark ~]#

综合练习

机构号  机构名称        省别    尾箱号  尾箱状态   领用柜员号   领用柜员姓名    币种    余额
11007eee        北京东城区街支行   北京    03      未领用           156     19001.68
11007fff        北京东城区街支行   北京    03      未领用           840     2672.00
11007aaa        北京东城区街支行   北京    04      未领用           156     7261.31
11007ccc        北京朝阳区路支行   北京    02      未领用           156     161490.08
110088ee        北京朝阳区路支行   北京    03      未领用           840     19711.00
34009eff         山西煤矿区路支行   山西    03      未领用           156     282370.23
11007eee        山西煤矿区路支行   山西    03      未领用           156     282370.23
11007eee        山西煤矿区路支行   山西    03      未领用           156     282370.23
11007264        山东平阴县支行     山东    02      未领用          156     304516.23
11007889        山东济阳县支行   北京    04      未领用           840     24551.00
11007264        北京朝阳区支行     北京    02      未领用           156     304516.23
11007284        北京朝阳区支行     北京    02      领用     1002  巫妖王           156     304516.23
11007194        北京朝阳区行       北京    02      未领用           156     304516.23
11007264        河南中原区支行     河南    02      未领用          156     304516.23
11007284        河南二七支行     河南    03      领用     1003 钟馗     156     9046.23
  1. 找到 河南省的 尾箱未领用的,机构号,和余额 #
  2. 找到 所有北京的 结构号,
  3. 找到 北京的机构并且尾箱没有领用的
  4. 找到县级支行 #
  5. 找到平阴县支行
  6. 找到余额大于 8000 的北京支行 #
  7. 找到哪些柜员领用了尾箱,打印出柜员号和柜员姓名
  8. 都要求格式化输出 #

七、awk 脚本编程

1 if语句

格式 { if (表达式) {语句; 语句; ...}}

awk -F: '{ if ($3==0) {print $1 " is administrator."}}' /etc/passwd
输出: root is administrator.

# 统计系统级别用户的数量
awk -F: '{ if ($3>0 && $3<1000) {count++}  } END{print count}' /etc/passwd 
输出: 22

2 if…else语句

格式 { if (表达式){语句;语句;...}else {语句;语句;...}}

awk -F: '{ if ($3==0){print $1} else {print $7} }' /etc/passwd

awk -F: '{ if ($3==0){count++} else{i++} } END{print "管理员个数: "count "系统用户数: "i}' /etc/passwd
输入:
管理员个数: 1系统用户数: 24

awk -F: '{ if($3==0){count++} else{i++} } END{print "管理员个数: "count ; print  "系统用户数: "i}' /etc/passwd
输出:
管理员个数: 1
系统用户数: 24

3 if…else if…else语句

格式
{ if(表达式1) {语句;语句;...} else if (表达式2) {语句;语句;...} else if(表达式3){语句;语句;...} else {语句;语句;...} }

awk -F: '{if($3==0){i++} else if($3>999){k++} else{j++}} END{print i; print k; print j}' /etc/passwd

输出:
1
2
22
awk -F: '{if($3==0){i++} else if($3>999){k++} else{j++}} END{print "管理员个数: "i; print "普通用个数: "k; print "系统用户: "j}' /etc/passwd

输出:
管理员个数: 1
普通用个数: 2
系统用户: 22


八、 awk使用外部变量:

1 使用自定义的 shell 变量

方法一:awk参数-v(推荐使用,易读)

[root@shark ~]# read -p ">>:" user
>>:root
[root@shark ~]# awk -F: -v awk_name=$user '$1==awk_name {print "用户存在"}' /etc/passwd
用户存在
[root@shark ~]#

2 使用 shell 的环境变量

[root@shark ~]# read -p ">>:" user
>>:root
[root@shark ~]# export user
[root@shark ~]# awk -F: '$1==ENVIRON["user"] {print "用户存在"}' /etc/passwd
用户存在
[root@shark ~]# unset user

九、指定多个分隔符:[]

echo "a b|c d| ||||e | |" |awk -F'[ |]' '{print $10}'
e
echo "a b|c d| ||||e | |" |awk -F'[ |]+' '{print $5}'
e
[root@shark ~]# echo "110.183.58.144 - - [10/May/2018:23:49:27 +0800] GET http://app." |awk -F'[][ ]'  '{print $5 }'
10/May/2018:23:49:27
[root@shark ~]# echo "110.183.58.144 - - [10/May/2018:23:49:27 +0800] GET http://app." |awk -F'[][ ]+'  '{print $4 }'
10/May/2018:23:49:27

注意: 中括号内的任意字符均视为普通字符, 比如 . * 都被看做是 普通字符。
例如:

$ echo "a.b*c" |awk -F'[.*]' '{print $1, $2,$3}'
a b c

作业:
1. 取得网卡IP(除ipv6以外的所有IP)
2. 获得内存使用情况
3. 获得磁盘使用情况
5. 打印出/etc/hosts文件的最后一个字段(按空格分隔)
6. 打印指定目录下的目录名

十、生产实例:

统计日志中某个时间范围的 IP 访问量,并进行排序

部分日志

110.183.58.144 - - [10/May/2018:23:49:27 +0800] "GET http://app.znds.com/html/20180504/y222sks_2.2.3_dangbei.dangbei HTTP/1.1" 200 14306614 "-" "okhttp/3.4.1"
1.69.17.127 - - [10/May/2018:23:49:31 +0800] "GET http://app.znds.com/down/20180205/ttjs_3.0.0.1_dangbei.apk HTTP/1.1" 200 13819375 "-" "okhttp/3.4.1"
1.69.17.127 - - [10/May/2018:23:49:40 +0800] "GET http://app.znds.com/down/20180416/ttyj_1.1.6.0_dangbei.apk HTTP/1.1" 200 16597231 "-" "okhttp/3.4.1"
1.69.17.127 - - [10/May/2018:23:50:00 +0800] "GET http://app.znds.com/down/20170927/jydp_1.06.00_dangbei.apk HTTP/1.1" 200 36659203 "-" "okhttp/3.4.1"

具体实现

日志文件名:app.log

$ start_dt='10/May/2018:23:47:43
$ end_dt='10/May/2018:23:49:05'
$ awk -v st=${start_dt} -v ent=${end_dt} -F'[][ ]' '$5 == st,$5 == ent  {print $1}' app.log  |sort |uniq -c |sort -nr |head -n 10
     66 223.13.142.15
      6 110.183.13.212
      4 1.69.17.127
      1 113.25.94.69
      1 110.183.58.144

时间转换工具

[root@shark ~]# unset months_array
[root@shark ~]# declare -A month_array
[root@shark ~]# month_array=([01]="Jan" [02]="Feb" [03]="Mar" [04]="Apr" [05]="May" [06]="Jun" [07]="Jul" [08]="Aug" [09]="Sept" [10]="Oct" [11]="Nov" [12]="Dec")
[root@shark ~]# echo ${month_array[01]}
[root@shark ~]# Jan
[root@shark ~]# m=10
[root@shark ~]# echo ${month_array[$m]}
[root@shark ~]# Oct

字符串切片

语法: ${var:从这个索引号开始:取出多少个字符:}

索引号从 0 开始

[root@shark ~]# st="20180510234931"
[root@shark ~]# m=${st:4:2}
[root@shark ~]# echo $m
[root@shark ~]# 05

常用日志分析语句

# 访问TOP 20 的IP
16348 58.16.183.52
awk  '$9==200 {print $1}'  2018-05-10-0000-2330_app.log | sort |uniq -c |sort -r |head -20


# 访问状态码为20X的  TOP 10的IP
2097 125.70.184.99
2000 183.225.69.158
awk  '$9 > 200 && $9 < 300{print $1}' 2018-05-10-0000-2330_app.log  | sort |uniq -c |sort -r |head


# 访问TOP 20 的url
250563 http://app.xxx.com/update/2018-04-04/dangbeimarket_4.0.9_162_znds.apk
awk  '$9 == 200{print $7,$9}' 2018-05-10-0000-2330_app.log | sort |uniq -c |sort -r |head -20

# 访问状态码为20X的  TOP 10的url
248786 http://app.znds.com/update/2018-04-04/dangbeimarket_4.0.9_162_znds.apk
awk  '$9 > 200 && $9 < 300{print $7,$9}' 2018-05-10-0000-2330_app.log | sort |uniq -c |sort -r |head


# 访问次数超过1W的IP
58.16.184.247
58.16.183.52
awk  '{print $1}' 2018-05-10-0000-2330_app.log| sort |uniq -c |sort -r |awk '$1 > 10000 {print $1}'


# 访问状态码为404的  TOP 10的url
1017 http://app.xxx.com/update/fixedaddress/kuaisou_qcast.apk.md5
awk  '$9 == 404 {print $1}' 2018-05-10-0000-2330_app.log | sort |uniq -c |sort -r |head

十一、提高篇

1 拆分信息到文件中

awk拆分文件很简单,使用重定向就好了。

下面这个例子,是按第 3 例分隔文件,相当的简单(其中的NR!=1表示不处理表头)。

$ awk 'NR!=1{print > $3}' gy.txt
$ ls
gy.txt  北京   山西     山东  河南

你也可以把指定的列输出到文件:

awk 'NR!=1{print $2,$3,$4,$5 > $3}' gy.txt

再复杂一点:(注意其中的if-else-if语句,可见awk其实是个脚本解释器)

$ awk 'NR!=1 {if($3 ~ /北京|山东/) print > "1.txt";
else if($3 ~ /山西/) print > "2.txt";
else print > "3.txt" }' gy.txt
 
$ ls ?.txt
1.txt  2.txt  3.txt

2 AWK 数组

语法: array_name[index]=value

  • array_name 数组名称
  • index 索引
  • value

awk 中的数组和 shell 中的关联数组是一样的,索引都是可以是任意的字符串,索引也可以使用对应有效的变量。

[root@shark ~]# awk 'BEGIN{
arr["a"]=1;
arr["b"]=2+3;
print arr["a"];
print arr["b"]
}'
1
5
[root@shark ~]# echo "a b" |awk 'BEGIN{
arr["a"]=1;
arr["b"]=2+3;
print arr["a"];
print arr["b"]
}'
1
5

示例文件

虚空行者 数学 68
虚空行者 英语 88
黑暗之女 语文 98
黑暗之女 数学 68
无极剑圣 语文 78
无极剑圣  数学 48
琴瑟仙女 语文 90
琴瑟仙女 数学 68
琴瑟仙女 英语 61
影流之主  语文 68
影流之主  数学 88
影流之主  英语 98
[root@shark ~]# awk '{arr[$1]++;print $1,arr[$1]}' hero
虚空行者 1
虚空行者 2
黑暗之女 1
黑暗之女 2
无极剑圣 1
无极剑圣 2
琴瑟仙女 1
琴瑟仙女 2
琴瑟仙女 3
影流之主 1
影流之主 2
影流之主 3
[root@shark ~]# awk '{arr[$1]++} END{print $1,arr[$1]}' hero
影流之主 3
[root@shark ~]# awk '{arr[$1]++} END{for (i in arr) print i, arr[i]}' hero
黑暗之女 2
虚空行者 2
琴瑟仙女 3
影流之主 3
无极剑圣 2
[root@shark ~]#

我们再来看一个统计各个各省份网点数量的用法:

$ awk 'NR!=1 {a[$3]++;} END {for (i in a) print i ", " a[i];}' gy.txt
北京, 69
山西, 20
江苏, 10
山东, 16

3 内置函数示例

#从file文件中找出长度大于80的行
awk 'length>80' file

# length 是 awk 的内置函数,作业是统计每行的字符串长度

4 随便玩玩

下面的命令计算所有的 txt 文件的文件大小总和。

$ ls -l  *.txt | awk '{sum+=$5} END {print sum}'
2511401

其他

# 再来看看统计每个用户的进程的占了多少内存(注:sum的RSS那一列)

$ ps aux | awk 'NR!=1 {a[$1]+=$6;} END { for(i in a) print i ", " a[i]"KB";}'

#按连接数查看客户端IP
netstat -ntu | awk '{print $5}' | cut -d: -f1 | sort | uniq -c | sort -nr
 
#打印99乘法表
seq 9 | sed 'H;g' | awk -v RS='' '{for(i=1;i<=NF;i++)printf("%dx%d=%d%s", i, NR, i*NR, i==NR?"\n":"\t")}'

示例数据

110.183.58.144 - - [10/May/2018:23:49:27 +0800] "GET http://app.znds.com/html/20180504/y222sks_2.2.3_dangbei.dangbei HTTP/1.1" 200 14306614 "-" "okhttp/3.4.1"
1.69.17.124 - - [10/May/2018:23:49:31 +0800] "GET http://app.znds.com/down/20180205/ttjs_3.0.0.1_dangbei.apk HTTP/1.1" 200 13819375 "-" "okhttp/3.4.1"
1.69.17.125 - - [10/May/2018:23:49:40 +0800] "GET http://app.znds.com/down/20180416/ttyj_1.1.6.0_dangbei.apk HTTP/1.1" 200 16597231 "-" "okhttp/3.4.1"
1.69.17.126 - - [10/May/2018:23:50:00 +0800] "GET http://app.znds.com/down/20170927/jydp_1.06.00_dangbei.apk HTTP/1.1" 200 36659203 "-" "okhttp/3.4.1"
1.69.17.127 - - [10/May/2018:23:50:00 +0800] "GET http://app.znds.com/down/20170927/jydp_1.06.00_dangbei.apk HTTP/1.1" 200 36659203 "-" "okhttp/3.4.1"
1.69.17.128 - - [10/May/2018:23:50:00 +0800] "GET http://app.znds.com/down/20170927/jydp_1.06.00_dangbei.apk HTTP/1.1" 200 36659203 "-" "okhttp/3.4.1"
1.69.17.129 - - [10/May/2018:23:50:00 +0800] "GET http://app.znds.com/down/20170927/jydp_1.06.00_dangbei.apk HTTP/1.1" 200 36659203 "-" "okhttp/3.4.1"
#!/bin/bash

declare -A month_array

month_array=([01]="Jan" [02]="Feb" [03]="Mar" [04]="Apr" [05]="May" [06]="Jun" [07]="Jul" [08]="Aug" [09]="Sept" [10]="Oct" [11]="Nov" [12]="Dec")

st=$1
end=$2
logfile=$3

start_yer=${st:0:4}
start_month=${st:4:2}
start_day=${st:6:2}
start_h=${st:8:2}
start_m=${st:10:2}
start_s=${st:12:2}

# 获取到映射月份
start_month=${month_array[$start_month]}

# 把用户输入的日期时间替换日志中的格式: 10/May/2018:23:49:31
start_dt="$start_day/$start_month/${start_yer}:${start_h}:${start_m}:$start_s"

echo $start_dt

end_yer=${end:0:4}
end_month=${end:4:2}
end_day=${end:6:2}
end_h=${end:8:2}
end_m=${end:10:2}
end_s=${end:12:2}

end_month=${month_array[$end_month]}
end_dt="$end_day/$end_month/${end_yer}:${end_h}:${end_m}:$end_s"

echo $end_dt
echo $logfile

export start_dt end_dt

awk -F '[][  ]+' '{
if ($4==ENVIRON["start_dt"]){flag=1}
else if($4==ENVIRON["end_dt"]){flag=0};
if (flag || $4==ENVIRON["end_dt"]){print $1}
}'   $logfile

unset start_dt
unset end_dt
[root@shark ~]# sh search-log.sh 20180510234931 20180510235000 access.log
10/May/2018:23:49:31
10/May/2018:23:50:00
access.log
1.69.17.124
1.69.17.125
1.69.17.126
1.69.17.127
1.69.17.128
1.69.17.129

十二、参考资料

内建变量,参看:http://www.gnu.org/software/gawk/manual/gawk.html#Built_002din-Variables

流控方面,参看:http://www.gnu.org/software/gawk/manual/gawk.html#Statements

内建函数,参看:http://www.gnu.org/software/gawk/manual/gawk.html#Built_002din

正则表达式,参看:http://www.gnu.org/software/gawk/manual/gawk.html#Regexp

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值