【RHCE】awk文本处理

embedded/2025/3/20 8:02:11/

目录

基本介绍

命令格式

awk基本使用

命令行读取程序脚本

数据字段变量

脚本中使用多个命令

文件中读取程序

处理数据前运行脚本(BEGIN)

处理数据后运行脚本(END)

awk高级用法

变量

内建变量

自定义变量

数组

定义数组变量

遍历数组变量

删除数组变量

使用模式

正则表达式

匹配操作符

数学表达式

结构化命令

if语句

while语句

do-while语句

for语句

内建函数

数学函数

字符串函数

时间函数

自定义函数


基本介绍

AWK是一种强大的文本处理工具,常用于在 Linux/Unix 系统中对文本文件进行模式扫描和处理。它非常适合处理结构化文本数据(如日志文件、CSV 文件等),并支持按行处理、字段提取、条件过滤和计算等操作。awk编程语言中,你可以做下面的事情:

定义变量来保存数据;
使用算术和字符串操作符来处理数据;
使用结构化编程概念(比如 if-then 语句和循环)来为数据处理增加处理逻辑;
通过提取数据文件中的数据元素,将其重新排列或格式化,生成格式化报告。

命令格式

awk 程序的基本格式如下:
awk options program file
常用选项描述
-F
指定行中划分数据字段的字段分隔符
-f
从指定的文件中读取程序
-v
定义 awk 程序中的一个变量及其默认值
-mf
指定要处理的数据文件中的最大字段数
-mr
指定数据文件中的最大数据行数
命令行选项提供了一个简单的途径来定制 awk 程序中的功能。我们会在探索 awk 时进一步了解这些选项。
awk 的强大之处在于程序脚本。可以写脚本来读取文本行的数据,然后处理并显示数据,创建任何类型的输出报告。

awk基本使用

命令行读取程序脚本

awk 程序脚本用一对花括号来定义。你必须将脚本命令放到两个花括号( {} )中。如果你错误地使用了圆括号来包含awk 脚本,就会得到一条类似于下面的错误提示。
[root@localhost ~]# awk '{print "Hello World!"}'
1
Hello World!
1
Hello World!

print命令会将文本打印到标准输出,若没有指定文本文件会从标准输入中接受数据。 

数据字段变量

awk的主要特性之一是其处理文本文件中数据的能力。它会自动给一行中的每个数据元素分配
一个变量。默认情况下, awk会将如下变量分配给它在文本行中发现的数据字段: 
$0
代表整个文本行;
$1
代表文本行中的第 1 个数据字段;
$2
代表文本行中的第 2 个数据字段;
$n
代表文本行中的第 n 个数据字段。

以下为从data2.txt文件中获取每行的第一个字段和第二个字段。

[root@localhost ~]# cat data2.txt
One line of test text.
Two lines of test text.
Three lines of test text.[root@localhost ~]# awk '{print $1,$2}' data2.txt 
One line
Two lines
Three lines

此外也可以用-F选项指定字段分割符

[root@localhost ~]# awk -F : '{print $1}' /etc/passwd | head -3
root
bin
daemon

脚本中使用多个命令

在脚本中运行多个命令,若在同一行中,只需在两条命令中加 ; 即可。

[root@localhost ~]# awk '{print "hello";print "world"}'hello
world

 若在不同行中,则无需 ;。

[root@localhost ~]# awk '{
> print "hello"
> print "world"
> }'hello
world

文件中读取程序

awk使用 -f 参数从指定文件中读取程序

[root@localhost ~]# cat script2.awk 
{print $1,"'s home is",$6
}[root@localhost ~]# awk -F : -f script2.awk /etc/passwd | head -3
root 's home is /root
bin 's home is /bin
daemon 's home is /sbin

处理数据前运行脚本(BEGIN)

awk 还允许指定程序脚本何时运行。默认情况下, awk 会从输入中读取一行文本,然后针对该行的数据执行程序脚本。有时可能需要在处理数据前运行脚本,比如为报告创建标题。
BEGIN关键字就是用来做这个的。它会强制awk 在读取数据前执行 BEGIN 关键字后指定的程序脚本。
[root@localhost ~]# cat data3.txt 
Line 1
Line 2
Line 3
[root@localhost ~]# awk 'BEGIN {print "data3.txt:"}{print $0}' data3.txt 
data3.txt:
Line 1
Line 2
Line 3

处理数据后运行脚本(END)

BEGIN 关键字类似, END 关键字允许你指定一个程序脚本, awk 会在读完数据后执行它。
可以用END来做页脚、报告总结等。
[root@localhost ~]# awk 'BEGIN {print "data3.txt:"}{print $0}END{print "end of file"}' data3.txt 
data3.txt:
Line 1
Line 2
Line 3
end of file

awk高级用法

变量

所有编程语言共有的一个重要特性是使用变量来存取值。 awk 编程语言支持两种不同类型的变量:
  1. 内建变量
  2. 自定义变量

awk有一些自带的内建变量,用于存放处理数据文件中的数据字段和记录的信息等。

内建变量

  1. 字段和记录分隔符变量
变量描述
FIELDWIDTHS
由空格分隔的一列数字,定义了每个数据字段确切宽度
FS
输入字段分隔符
RS
输入记录分隔符
OFS
输出字段分隔符
ORS
输出记录分隔符

 在BEGIN中用FS指定输入字段分割符,OFS指定输出字段分隔符,若不指定则都为空格。

[root@localhost ~]# cat data1.txt 
data11,data12,data13,data14,data15
data21,data22,data23,data24,data25
data31,data32,data33,data34,data35[root@localhost ~]# awk 'BEGIN{FS=","}{print $1,$2,$3}' data1.txt 
data11 data12 data13
data21 data22 data23
data31 data32 data33[root@localhost ~]# awk 'BEGIN{FS=",";OFS="-"}{print $1,$2,$3}' data1.txt 
data11-data12-data13
data21-data22-data23
data31-data32-data33

 FIELDWIDTHS可以精确的指定每个字段的长度,这种方法不适用与变长的字段。

[root@localhost ~]# awk 'BEGIN{FIELDWIDTHS="3 5 2 5"}{print $1,$2,$3,$4}' data1.txt 
dat a11,d at a12,d
dat a21,d at a22,d
dat a31,d at a32,d

 在awk中,可以根据情况指定RS输入记录分割符,以应对特殊情况。

[root@localhost ~]# cat data4.txt 
Riley Mullen
123 Main Street
Chicago, IL 60601
(312)555-1234Frank Williams
456 Oak Street
Indianapolis, IN 46201
(317)555-9876Haley Snell
4231 Elm Street
Detroit, MI 48201
(313)555-4938[root@localhost ~]# awk 'BEGIN{FS="\n";RS=""}{print $1,$4}' data4.txt 
Riley Mullen (312)555-1234
Frank Williams (317)555-9876
Haley Snell (313)555-4938

  1. 数据变量
除了字段和记录分隔符变量外, awk 还提供了其他一些内建变量来帮助你了解数据发生了什么变化,并提取shell 环境的信息。下表列出了 awk 中的其他内建变量。
变量
描述
ARGC
当前命令行参数个数
ARGIND
当前文件在 ARGV 中的位置
ARGV
包含命令行参数的数组
CONVFMT
数字的转换格式(参见 printf 语句),默认值为 %.6 g
ENVIRON
当前 shell 环境变量及其值组成的关联数组
ERRNO
当读取或关闭输入文件发生错误时的系统错误号
FILENAME
用作 awk 输入数据的数据文件的文件名
FNR
当前数据文件中的数据行数
IGNORECASE
设成非零值时,忽略 awk 命令中出现的字符串的字符大小写
NF
数据文件中的字段总数
NR
已处理的输入记录数
OFMT
数字的输出格式,默认值为 %.6 g
RLENGTH
match 函数所匹配的子字符串的长度
RSTART
match 函数所匹配的子字符串的起始位置

自定义变量

跟其他典型的编程语言一样, awk 允许你定义自己的变量在程序代码中使用。 awk 自定义变量名可以是任意数目的字母、数字和下划线,但不能以数字开头。重要的是,要记住awk 变量名区分大小写。
[root@localhost ~]# awk 'BEGIN{testing="hello world"}{print testing}'hello world

自定义变量除了可以存字符串,也可以存数值,并进行数值运算

[root@localhost ~]# awk 'BEGIN{a=10
> a=a+15
> }
> {
> print a
> }'25

也可以在命令行上给变量赋值

[root@localhost ~]# cat data1.txt 
data11,data12,data13,data14,data15
data21,data22,data23,data24,data25
data31,data32,data33,data34,data35[root@localhost ~]# awk -F , '{print $n}' n=3 data1.txt 
data13
data23
data33

但这种方法赋值的变量在BEGIN中不生效

[root@localhost ~]# awk -F , 'BEGIN{print n}{print $n}' n=3 data1.txt data13
data23
data33

也可以使用 -v 参数赋值变量,这样声明的变量在BEGIN中也能使用

[root@localhost ~]# awk -F , -v n=3 'BEGIN{print n}{print $n}' data1.txt 
3
data13
data23
data33

数组

为了在单个变量中存储多个值,许多编程语言都提供数组。 awk 编程语言使用关联数组提供数组功能。
关联数组跟数字数组不同之处在于它的索引值可以是任意文本字符串。你不需要用连续的数字来标识数组中的数据元素。相反,关联数组用各种字符串来引用值。每个索引字符串都必须能够唯一地标识出赋给它的数据元素。

定义数组变量

[root@localhost ~]# cat script3.awk 
BEGIN{str["name"] = "xiaoming"str["age"] = 18str["sex"] = "man"	
}
{print str["name"]	
}
[root@localhost ~]# awk -f script3.awk xiaoming

遍历数组变量

在awk中遍历数组,可以使用for in语句

for (var in array)
{
statements
}

 这个for语句会在每次循环时将关联数组array的下一个索引值赋给变量var,然后执行一遍 statements。

[root@localhost ~]# cat script3.awk 
BEGIN{str["name"] = "xiaoming"str["age"] = 18str["sex"] = "man"	
}
{for(s in str){print s,":",str[s]}	
}
[root@localhost ~]# awk -f script3.awk age : 18
sex : man
name : xiaoming

删除数组变量

从关联数组中删除数组索引要用一个特殊的命令。
delete array[index]
删除命令会从数组中删除指定的键值对。
[root@localhost ~]# cat script3.awk 
BEGIN{str["name"] = "xiaoming"str["age"] = 18str["sex"] = "man"	
}
{delete str["age"]for(s in str){print s,":",str[s]}	
}
[root@localhost ~]# awk -f script3.awk sex : man
name : xiaoming

使用模式

awk 程序支持多种类型的匹配模式来过滤数据记录 BEGIN 和END关键字是用来在读取数据流之前或之后执行命令的特殊模式。类似地,你可以创建其他模式在数据流中出现匹配数据时执行一些命令。

正则表达式

可以用基础正则表达式( BRE )或扩展正则表达式( ERE )来选择程序脚本作用在数据流中的哪些行上。在使用正则表达式时,正则表达式必须出现在它要控制的程序脚本的左花括号前。
[root@localhost ~]# cat data5.txt 
Alice 25 Engineer
Bob 30 Doctor
Charlie 22 Student
David 35 Lawyer
Eve 28 Artist[root@localhost ~]# awk '/^C/{print $0}' data5.txt 
Charlie 22 Student

匹配操作符

匹配操作符( matching operator )允许将正则表达式限定在记录中的特定数据字段。匹配操
作符是波浪线( ~ )。可以指定匹配操作符、数据字段变量以及要匹配的正则表达式。(!~)表示取反。
[root@localhost ~]# cat data5.txt 
Alice 25 Engineer
Bob 30 Doctor
Charlie 22 Student
David 35 Lawyer
Eve 28 Artist[root@localhost ~]# awk '$3 ~ /i/{print $0}' data5.txt 
Alice 25 Engineer
Eve 28 Artist[root@localhost ~]# awk '$3 !~ /i/{print $0}' data5.txt 
Bob 30 Doctor
Charlie 22 Student
David 35 Lawyer

数学表达式

除了正则表达式,你也可以在匹配模式中用数学表达式。这个功能在匹配数据字段中的数字值时非常方便。

可以使用常见的数学比较表达式:

x == y
x 等于 y
x <= y
x 小于等于 y
x < y
x 小于 y
x >= y
x 大于等于 y
x > y
x 大于 y
[root@localhost ~]# cat data5.txt 
Alice 25 Engineer
Bob 30 Doctor
Charlie 22 Student
David 35 Lawyer
Eve 28 Artist[root@localhost ~]# awk '$2>=25{print $0}' data5.txt 
Alice 25 Engineer
Bob 30 Doctor
David 35 Lawyer
Eve 28 Artist[root@localhost ~]# awk 'BEGIN{FS=":"}$1=="root"{print $0}' /etc/passwd
root:x:0:0:root:/root:/bin/bash[root@localhost ~]# awk 'BEGIN{FS=":"}$1>="q"{print $0}' /etc/passwd
root:x:0:0:root:/root:/bin/bash
sync:x:5:0:sync:/sbin:/bin/sync
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
tss:x:59:59:Account used for TPM access:/:/usr/sbin/nologin
systemd-coredump:x:999:997:systemd Core Dumper:/:/sbin/nologin
sssd:x:997:995:User for sssd:/:/sbin/nologin
sshd:x:74:74:Privilege-separated SSH:/usr/share/empty.sshd:/usr/sbin/nologin
redhat:x:1000:1000:redhat:/home/redhat:/bin/bash
rpc:x:32:32:Rpcbind Daemon:/var/lib/rpcbind:/sbin/nologin
rpcuser:x:29:29:RPC Service User:/var/lib/nfs:/sbin/nologin

结构化命令

if语句

[root@localhost ~]# cat data6.txt 
10
5
13
50
34
[root@localhost ~]# awk '{if($1 > 20) print $1}' data6.txt 
50
34
awk if 语句也支持 else 子句,允许在 if 语句条件不成立的情况下执行一条或多条语句。

[root@localhost ~]# cat script4.awk 
{if($1%400==0){print $1 " is Leap Year"}else if($1%4==0 && $1%100!=0)print $1 " is Leap Year"elseprint $1 " not is Leap Year"}
[root@localhost ~]# cat data7.txt 
2014
2020
2017
2019
2024
2026
1900
[root@localhost ~]# awk -f script4.awk data7.txt 
2014 not is Leap Year
2020 is Leap Year
2017 not is Leap Year
2019 not is Leap Year
2024 is Leap Year
2026 not is Leap Year
1900 not is Leap Year

while语句

while 循环允许遍历一组数据,并检查迭代的结束条件。如果在计算中必须使用每条记录中的多个数据值,这个功能能帮得上忙。
[root@localhost ~]# cat data8.txt 
130 120 135
160 113 140
145 170 215
[root@localhost ~]# cat script5.awk 
BEGIN{sum=0
}
{s=0i=1while(i<NF){s+=$ii++}print NR "‘sum: " ssum+=s
}
END{print "sum: " sum	
}
[root@localhost ~]# awk -f script5.awk data8.txt 
1‘sum: 250
2‘sum: 273
3‘sum: 315
sum: 838

 awk编程语言支持在while循环中使用break语句和continue语句,允许你从循环中跳出。

[root@localhost ~]# cat script5.awk 
BEGIN{sum=0
}
{s=0i=1while(i<NF){s+=$ii++if(i==2)break}print NR "‘sum: " ssum+=s
}
END{print "sum: " sum	
}
[root@localhost ~]# awk -f script5.awk data8.txt 
1‘sum: 130
2‘sum: 160
3‘sum: 145
sum: 435

do-while语句

do-while 语句类似于 while 语句,这种格式保证了语句会在条件被求值之前至少执行一次。当需要在求值条件前执行语句时,这个特性非常方便。
[root@localhost ~]# cat data8.txt 
130 120 135
160 113 140
145 170 215
[root@localhost ~]# cat script6.awk 
BEGIN{sum=0
}
{s=0i=1do{s+=$ii++}while(i<NF)print NR "‘sum: " ssum+=s
}
END{print "sum: " sum	
}
[root@localhost ~]# awk -f script6.awk data8.txt 
1‘sum: 250
2‘sum: 273
3‘sum: 315
sum: 838

for语句

for 语句是许多编程语言执行循环的常见方法。 awk 编程语言支持 C 风格的 for 循环。
[root@localhost ~]# cat data8.txt 
130 120 135
160 113 140
145 170 215
[root@localhost ~]# cat script7.awk 
BEGIN{sum=0
}
{s=0for(i=1;i<NF;i++){s+=$i}print NR "‘sum: " ssum+=s
}
END{print "sum: " sum	
}
[root@localhost ~]# awk -f script7.awk data8.txt 
1‘sum: 250
2‘sum: 273
3‘sum: 315
sum: 838

内建函数

awk 编程语言提供了不少内置函数,可进行一些常见的数学、字符串以及时间函数运算。你可以在awk 程序中利用这些函数来减少脚本中的编码工作。

数学函数

函数描述
atan2(x, y)
x/y 的反正切, x y 以弧度为单位
cos(x)
x 的余弦, x 以弧度为单位
exp(x)
x 的指数函数
int(x)
x 的整数部分,取靠近零一侧的值
log(x)
x 的自然对数
rand( )
0 大比 1 小的随机浮点值(不包括0或1)
sin(x)
x 的正弦, x 以弧度为单位
sqrt(x)
x 的平方根
srand(x)
为计算随机数指定一个种子值

字符串函数

函数描述
asort(s [,d])
将数组 s 按数据元素值排序。索引值会被替换成表示新的排序顺序的连续数字。另外, 如果指定了d ,则排序后的数组会存储在数组 d
asorti(s [,d])
将数组 s 按索引值排序。生成的数组会将索引值作为数据元素值,用连续数字索引来表明排序顺序。另外如果指定了d ,排序后的数组会存储在数组d
gensub(r, s, h [, t])
查找变量 $0 或目标字符串 t (如果提供了的话)来匹配正则表达式r 。如果 h 是一个以 g或G 开头的字符串,就用 s 替换掉匹配的文本。如果 h 是一个数字,它表示要替换掉第h处r 匹配的地方
gsub(r, s [,t])
查找变量 $0 或目标字符串 t (如果提供了的话)来匹配正则表达式r 。如果找到了,就
全部替换成字符串 s
index(s, t)
返回字符串 t 在字符串 s 中的索引值,如果没找到的话返回 0
length([s])
返回字符串 s 的长度;如果没有指定的话,返回 $0 的长度
match(s, r [,a])
返回字符串 s 中正则表达式 r 出现位置的索引。如果指定了数组a ,它会存储 s 中匹配正则表达式的那部分
split(s, a [,r])
s FS 字符或正则表达式 r (如果指定了的话)分开放到数组a 中。返回字段的总数
sprintf(format,variables)
用提供的 format variables 返回一个类似于 printf 输出的字符串
sub(r, s [,t])
在变量 $0 或目标字符串 t 中查找正则表达式 r 的匹配。如果找到了,就用字符串s 替换
掉第一处匹配
substr(s, i [,n])
返回 s 中从索引值 i 开始的 n 个字符组成的子字符串。如果未提供n ,则返回 s 剩下的部
tolower(s)
s 中的所有字符转换成小写
toupper(s)
s 中的所有字符转换成大写

[root@localhost ~]# awk '{print length($1)}'
hello
5
red
3[root@localhost ~]# cat script8.awk 
BEGIN{a[0] = 45a[1] = 12a[2] = 34a[3] = 5asort(a)for(i in a){print a[i]}	
}
[root@localhost ~]# awk -f script8.awk 
5
12
34
45//split可以很方便的将字段放进数组中[root@localhost ~]# cat script9.awk 
BEGIN{FS=":"
}
{split($0,var)print var[1],var[NF]
}
[root@localhost ~]# head -3 /etc/passwd | awk -f script9.awk 
root /bin/bash
bin /sbin/nologin
daemon /sbin/nologin

时间函数

函数
描述
mktime(datespec)
将一个按 YYYY MM DD HH MM SS [DST] 格式指定的日期转换成时间戳值
strftime(format[,timestamp])
将当前时间的时间戳或 timestamp (如果提供了的话)转化格式化日期(采用shell函数date() 的格式)
systime( )
返回当前时间的时间戳

 下面是在awk程序中使用时间函数的例子。

[root@localhost ~]# cat script10.awk 
{date=systime()print strftime("%Y %T",date)	
}
[root@localhost ~]# awk -f script10.awk 2025 21:23:542025 21:23:562025 21:23:57

自定义函数

自定义函数基本格式

function name([variables])
{
statements
return value
}

 在定义函数时,它必须出现在所有代码块之前(包括BEGIN代码块)。

[root@localhost ~]# cat script12.awk 
function leap(year){if(year%400==0){print year " is Leap Year"}else if(year%4==0 && year%100!=0)print year " is Leap Year"elseprint year " not is Leap Year"}
BEGIN{for(i=2025;i<=2050;i++){leap(i)}
}[root@localhost ~]# awk -f script12.awk 
2025 not is Leap Year
2026 not is Leap Year
2027 not is Leap Year
2028 is Leap Year
2029 not is Leap Year
2030 not is Leap Year
2031 not is Leap Year
2032 is Leap Year
2033 not is Leap Year
2034 not is Leap Year
2035 not is Leap Year
2036 is Leap Year
2037 not is Leap Year
2038 not is Leap Year
2039 not is Leap Year
2040 is Leap Year
2041 not is Leap Year
2042 not is Leap Year
2043 not is Leap Year
2044 is Leap Year
2045 not is Leap Year
2046 not is Leap Year
2047 not is Leap Year
2048 is Leap Year
2049 not is Leap Year
2050 not is Leap Year


http://www.ppmy.cn/embedded/174082.html

相关文章

flutter 专题 一百零三

前不久&#xff0c;谷歌官方正式发布了Flutter的首个发布预览版&#xff08;Release Preview 1&#xff09;&#xff0c;这标志着谷歌进入了Flutter正式版&#xff08;1.0&#xff09;发布前的最后阶段&#xff0c;同时作为Google的重量级跨平台开发方案&#xff0c;此次更新也…

分页优化之——游标分页

游标分页&#xff08;Cursor-based Pagination&#xff09; 是一种高效的分页方式&#xff0c;特别适用于大数据集和无限滚动的场景。与传统的基于页码的分页&#xff08;如 page1&size10&#xff09;不同&#xff0c;游标分页通过一个唯一的游标&#xff08;通常是时间戳或…

三分钟掌握视频分辨率修改 | 在 Rust 中优雅地使用 FFmpeg

前言 在视频处理领域&#xff0c;调整视频分辨率是一个绕不过去的需求。比如&#xff0c;你可能需要将一段视频适配到手机、平板或大屏电视上&#xff0c;或者为了节省存储空间和网络带宽而压缩视频尺寸。然而&#xff0c;传统的FFmpeg命令行工具虽然功能强大&#xff0c;但复…

centos 7误删/bash 拯救方法

进入救援模式 1. 插入CentOS 7安装光盘&#xff0c;重启系统。在开机时按BIOS设置对应的按键&#xff08;通常是F2等&#xff09;&#xff0c;将启动顺序调整为CD - ROM优先。 2. 系统从光盘启动后&#xff0c;选择“Troubleshooting”&#xff0c;然后选择“Rescue a Cent…

C语言 第五章 指针(3)

目录 指针常用运算 定义&#xff1a; 指针与整数值的加减运算 格式&#xff1a; 举例&#xff1a; 说明&#xff1a; 实例 举例&#xff1a; 实例 指针的自增、自减运算 定义 实例1&#xff1a; 实例2&#xff1a; 指针常用运算 定义&#xff1a; 指针本质上就是一…

免费看付费电影网站制作,高清电影集合搜索引擎网站

引言 在当今数字化时代&#xff0c;电影已经成为人们日常生活中不可或缺的一部分。然而&#xff0c;随着各大视频平台推出付费会员制度&#xff0c;许多用户开始寻找免费观看付费电影的途径。本文将详细介绍如何制作一个免费看付费电影的网站&#xff0c;并打造一个高清电影集…

东隆科技携手PRIMES成立中国校准实验室,开启激光诊断高精度新时代

3月12日&#xff0c;上海慕尼黑光博会期间&#xff0c;东隆科技正式宣布与德国PRIMES共同成立“中国校准实验室”。这一重要合作标志着东隆科技在本地化服务领域的优势与PRIMES在激光光束诊断领域的顶尖技术深度融合&#xff0c;旨在为中国客户提供更快速、更高精度的服务以及本…

python:music21 与 AI 结合应用探讨

Python 的 music21 库与人工智能&#xff08;AI&#xff09;技术结合应用具有广泛的可能性&#xff0c;尤其是在音乐生成、分析和风格模拟等领域。以下是具体的结合方向与示例&#xff1a; 1. 音乐生成与 AI AI 模型驱动音乐生成&#xff1a; 使用深度学习模型&#xff08;如 …