当前位置: 首页 > news >正文

expect脚本详解

一、概述

Expect是一个免费的编程工具语言,用来实现自动和交互式任务进行通信,而无需人的干预。Expect的作者Don Libes在1990年开始编写Expect时对Expect做有如下定义:Expect是一个用来实现自动交互功能的软件套件。通过expect系统管理员可以创建脚本用来实现对命令或程序提供输入,而这些命令和程序是期望从终端(terminal)得到输入,一般来说这些输入都需要手工输入进行的。Expect则可以根据程序的提示模拟标准输入提供给程序需要的输入来实现交互程序执行。甚至可以实现实现简单的BBS聊天机器人。

我们通过Shell可以实现简单的控制流功能,如:循环、判断等。但是对于需要交互的场合则必须通过人工来干预,有时候我们可能会需要实现与SSH、FTP服务器等进行免交互的自动连接功能,而Expect正是用来实现这种功能的工具。

Expect是不断发展的,随着时间的流逝,其功能越来越强大,已经成为系统管理员的的一个强大助手。Expect需要Tcl编程语言的支持,所以要在系统上运行Expect必须首先安装Tcl。

通过ssh远程操控主机时交互解决方式:

  1. 通过ssh的密钥对

  2. 通过sshpass工具提交密码

  3. 通过expect工具提交密码

二、安装expect

[root@localhost ~]# rpm -q expect

[root@localhost ~]# yum -y install expect

三、如何使用expect

在使用expect程序前,我们首先简单说下expect在常规使用中的工作流程:

首先expect的内部命令spawn启动指定进程-->expect获取期待的关键字-->内部命令send向指定进程发送响应内容-->进程执行完成后,退出expect程序。

通过上面expect的工作流程,我们可以认识到,expect的内置命令:spawnexpectsend等非常重要,所以下面我们先来了解下它们的使用。

3-1、spawn命令

spawn作用:启动新的产生交互的进程

spawn命令的语法:

spawn [选项] [需要执行的shell命令或程序等]

下面我们以修改一个已存在账户的密码为例,举例如下:

[root@localhost ~]# useradd tom

[root@localhost ~]# passwd tom

更改用户 tom 的密码 。

新的 密码:

无效的密码: 密码未通过字典检查 - 过于简单化/系统化

重新输入新的 密码:

passwd:所有的身份验证令牌已经成功更新。

基于expect交互界面做法

[root@localhost ~]# expect

expect1.1> spawn passwd tom

spawn passwd tom

1388

expect1.2> expect "密码:"

更改用户 tom 的密码 。

新的 密码:expect1.3> send "123456r"

expect1.4> expect "密码:"

无效的密码: 密码少于 8 个字符

重新输入新的 密码:expect1.5> send "123456r"

expect1.6> expect eof

passwd:所有的身份验证令牌已经成功更新。

expect1.7> exit

3-2、expect命令

expect作用:获取从spawn命令执行的命令和程序后产生的交互信息。看看是否匹配,如果匹配,就开始执行expect进程接收字符串。

expect命令的语法:

expect [选项] 表达式 [动作]

选项:比如"-re"表示使用正则表达式来进行匹配。

案例:

[root@localhost ~]# expect

expect1.1> spawn passwd tom

spawn passwd tom

1437

expect1.2> expect "新的 密码:"

更改用户 tom 的密码 。

新的 密码:expect1.3> send "123456r"

expect1.4> expect "新的 密码:"

无效的密码: 密码少于 8 个字符

重新输入新的 密码:expect1.5> send "123456r"

expect1.6> expect eof

passwd:所有的身份验证令牌已经成功更新。

expect1.7> exit

3-3、send命令

send命令的主要作用是,在expect命令匹配完指定的字符后,发送指定的字符串给系统程序,在字符中可以支持部分特殊转义符,比如:n(回车)r(换行)t(制表符)等。

案例

[root@localhost ~]# expect

expect1.1> spawn passwd tom

spawn passwd tom

1437

expect1.2> expect "新的 密码:"

更改用户 tom 的密码 。

新的 密码:expect1.3> send "123456r"

expect1.4> expect "新的 密码:"

无效的密码: 密码少于 8 个字符

重新输入新的 密码:expect1.5> send "123456r"

expect1.6> expect eof

passwd:所有的身份验证令牌已经成功更新。

expect1.7> exit

3-4、exp_continue命令

exp_continue命令的主要作用是,如果需要一次匹配多个字符串,那么多次匹配字符串并执行不同的动作中,可以让expect程序实现继续匹配的效果。

案例:

[root@localhost ~]# vim chang_pass2.exp

#!/usr/bin/expect

# Filename : change_pass2.exp

spawn passwd tom

expect {

"新的 密码:" { send "123456r"; exp_continue }

"新的 密码:" { send "123456r" }

eof

}

[root@localhost ~]# chmod +x chang_pass2.exp

[root@localhost ~]# ./chang_pass2.exp

spawn passwd tom

更改用户 tom 的密码 。

新的 密码:

无效的密码: 密码少于 8 个字符

重新输入新的 密码:

passwd:所有的身份验证令牌已经成功更新。

3-5、send_user命令

send_user命令的作用是:用来打印expect脚本信息,类似shell里的echo命令。

[root@localhost ~]# vim send_user.exp

#!/usr/bin/expect

#Filename: send_user.exp

send_user "beijingn"

send_user "shanghait"

send_user "guangzhoun"

[root@localhost ~]# chmod +x send_user.exp

[root@localhost ~]# ./send_user.exp

beijing

shanghai guangzhou

3-6、expect变量

3-6-1、普通变量

expect中定义普通变量的语法如下:

set 变量名 变量值

例如:

set chengshi "beijing"

调取变量的方法是:

puts $变量名

#或者

send_user "$变量名"

3-6-2、expect中位置参数变量

如何向expect脚本中像shell一样传递类似于$0、$1等位置参数,用于接收及控制expect脚本传递位置参数变量呢?

expect是通过如下语法来进行的:

set <变量名称> [lindex $argv <param index> ]

当然,除了基本的位置参数外,expect还支持其它的特殊参数,比如:

$argc 表示传入参数的个数

$argv0 表示当前执行脚本的名称。

案例:

[root@localhost ~]# vim expect_var2.exp

#!/usr/bin/expect

# Filename: expect_var2.exp

set file1 [lindex $argv 0]

set file2 [lindex $argv 1]

set file3 [lindex $argv 2]

puts "The files are : $file1 $file2 $file3"

puts "The number of files are: $argc"

puts "The expect script name is: $argv0"

[root@localhost ~]# chmod +x expect_var2.exp

[root@localhost ~]# ./expect_var2.exp 1.txt 2.txt 3.txt

The files are : 1.txt 2.txt 3.txt

The number of files are: 3

The expect script name is: ./expect_var2.exp

3-7、expect中if条件语句

expect中if 条件语句的语法结构如下:

if {条件表达式} {

commands;

}

if {条件表达式} {

commands;

} else {

commands;

}

注意事项:上面条件语句中每个"{"前要至少保证有一个空格。

案例:

[root@localhost ~]# vim expect-if.exp

#!/usr/bin/expect

# Filename: expect-if.exp

if {$argc <= 3} {

puts "The IP numbers <= 3"

} else {

puts "The IP numbers > 3"

}

[root@localhost ~]# chmod +x expect-if.sh

[root@localhost ~]# ./expect-if.sh 192.168.200.{1..3}

The IP numbers <= 3

[root@localhost ~]# ./expect-if.sh 192.168.200.{1..5}

The IP numbers > 3

3-8、expect中常用关键字

3-8-1 eof关键字

eof是和spawn对应的,当spawn发送指令到终端执行起始会有一个eof,等指令在终端完毕后,在返回时eof被expect捕捉,就好比在shell中 cat >>file <<OEFrr content rr EOF一样,在结束时也要有EOF,这样是对应的。因前面案例中已有举例,这里就不再举例说明。

Interact允许用户交互,由管理员结束进程。

3-8-2 timeout关键字

expect脚本我们都知道,首先spawn我们要执行的命令,然后就给出一堆expect的屏幕输出,如果输出匹配了我们的expect的正则匹配内容,我们就会send一个命令上去,模拟用户输入。

但是expect中等待命令的输出信息是有一个timeout的设定的,默认是10秒。这个特性是防止那些执行死机的命令的。一旦到了这个timeout,还是没有屏幕输出的话,expect脚本中下面的代码就会执行。或者我们在expect脚本中如果定义了timeout的响应代码的话,这些代码就会被执行。

解决这样的问题非常简单,最简单的办法就是在expect脚本的开头定义

set timeout -1 -- 永久不超时

set timeout 0 -- 立即执行

set timeout XX -- 设定具体的timeout时间(秒),默认是10秒。

3-9、expect中for循环语句

{ set i 1 } 定义i的值为1

{ $i <= 10 }循环的条件

{ incr i 1} 制定$i的增量值,必须写在这行的末尾处,默认增量值为1

[root@localhost ~]# cat expect-for.exp

#!/usr/bin/expect

for { set i 1 } { $i <= 10 } { incr i 1 } {

puts "$i"

}

[root@localhost ~]# expect expect-for.exp

1

2

3

4

5

6

7

8

9

10

四、Shell脚本调用expect 的方法

  1. 在shell脚本中使用expect --c "..."可以在shell中调用expect编程语言;

[root@localhost ~]# cat shell-expect1.sh

#!/bin/bash

for i in 192.168.200.{112..113}

do

expect -c "

spawn ssh root@$i ifconfig ens32

expect {

"yes/no" { send "yes\r";exp_continue }

"password" { send "123456\n" }

}

expect eof

"

done

[root@localhost ~]# bash shell-expect1.sh

spawn ssh root@192.168.200.112 ifconfig ens32

root@192.168.200.112's password:

ens32: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500

inet 192.168.200.112 netmask 255.255.255.0 broadcast 192.168.200.255

inet6 fe80::20c:29ff:fe8c:f2d9 prefixlen 64 scopeid 0x20<link>

ether 00:0c:29:8c:f2:d9 txqueuelen 1000 (Ethernet)

RX packets 3512 bytes 1656192 (1.5 MiB)

RX errors 0 dropped 0 overruns 0 frame 0

TX packets 2808 bytes 365154 (356.5 KiB)

TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0

spawn ssh root@192.168.200.113 ifconfig ens32

root@192.168.200.113's password:

ens32: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500

inet 192.168.200.113 netmask 255.255.255.0 broadcast 192.168.200.255

inet6 fe80::20c:29ff:fe0b:7eab prefixlen 64 scopeid 0x20<link>

ether 00:0c:29:0b:7e:ab txqueuelen 1000 (Ethernet)

RX packets 2722 bytes 1511052 (1.4 MiB)

RX errors 0 dropped 0 overruns 0 frame 0

TX packets 2196 bytes 258197 (252.1 KiB)

TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0

2、在shell脚本中使用/usr/bin/expect <<-EOF ... EOF的方式可以调用绝大多数的其它脚本语言,这种方式执行命令建议使用绝对路径,而且要严格遵守expect 的脚本格式

[root@localhost ~]# cat shell-expect2.sh

#!/bin/bash

for i in 192.168.200.{112..113}

do

/usr/bin/expect << EOF

spawn ssh root@$i ifconfig ens32

expect {

"yes/no" { send "yes\r";exp_continue }

"password" { send "123456\n" }

}

expect eof

EOF

done

[root@localhost ~]# bash shell-expect2.sh

spawn ssh root@192.168.200.112 ifconfig ens32

root@192.168.200.112's password:

ens32: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500

inet 192.168.200.112 netmask 255.255.255.0 broadcast 192.168.200.255

inet6 fe80::20c:29ff:fe8c:f2d9 prefixlen 64 scopeid 0x20<link>

ether 00:0c:29:8c:f2:d9 txqueuelen 1000 (Ethernet)

RX packets 3329 bytes 1632688 (1.5 MiB)

RX errors 0 dropped 0 overruns 0 frame 0

TX packets 2609 bytes 340022 (332.0 KiB)

TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0

spawn ssh root@192.168.200.113 ifconfig ens32

root@192.168.200.113's password:

ens32: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500

inet 192.168.200.113 netmask 255.255.255.0 broadcast 192.168.200.255

inet6 fe80::20c:29ff:fe0b:7eab prefixlen 64 scopeid 0x20<link>

ether 00:0c:29:0b:7e:ab txqueuelen 1000 (Ethernet)

RX packets 2561 bytes 1488208 (1.4 MiB)

RX errors 0 dropped 0 overruns 0 frame 0

TX packets 1997 bytes 233005 (227.5 KiB)

TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0

[root@localhost ~]# cat zhixing.sh

#!/bin/bash

for i in 192.168.200.{112..113}

do

/usr/bin/expect << EOF

spawn ssh root@$i wget http://192.168.200.111/user20.sh -O
/tmp/user20.sh && bash /tmp/user20.sh

expect {

"yes/no" { send "yesr";exp_continue }

"password" { send "123456n" }

}

expect eof

EOF

done

五、expect在生产环境中的案例

案例要求:

手边现在有一个统计CPU信息的脚本(cpuinfo.sh),该脚本在单独的物理服务器上或虚拟主机上执行后,能统计该机器CPU的五个相关信息,比如:

[root@localhost ~]# sh cpuinfo.sh

logical CPU number in total: 32 //当前机器中逻辑CPU个数为:32

phycical CPU number in total: 2 //当前机器中物理CPU颗数为: 2

core number in a physical CPU: 8 //机器中每颗物理CPU有8核心

logical CPU number in a phycical CPU:16 //机器中每颗物理CPU 有16
个逻辑CPU

Hyper threading is enabled. //每颗CPU的超线程已经打开

具体代码如下:

[root@localhost ~]# cat cpuinfo.sh

#!/bin/bash

# filename: cpu-info.sh

#this script only works in a Linux system which has one or more
indentical physical CPU(S)

echo -n "logical CPU number in total: "

#逻辑CPU个数

cat /proc/cpuinfo |grep "^processor" |wc -l

#有些系统没有多核也没有打开超线程,就直接退出脚本

cat /proc/cpuinfo |grep -qi "core id"

if [ $? -ne 0 ]; then

echo "Warning. No multi-core or hyper-threading is enabled."

exit 0;

fi

echo -n "phycical CPU number in total: "

#物理CPU个数

cat /proc/cpuinfo |grep "physical id" |sort |uniq |wc -l

echo -n "core number in a physical CPU: "

#每个物理CPU上的core的个数(未计入超线程)

core_per_phy_cpu=$(cat /proc/cpuinfo |grep "core id"|sort |uniq
|wc -l)

echo $core_per_phy_cpu

echo -n "logical CPU number in a phycical CPU:"

#每个物理CPU中逻辑CPU(可能是core,threads或both)的个数

logical_cpu_per_phy_cpu=$(cat /proc/cpuinfo |grep "siblings"|sort
|uniq |awk -F: '{print $2}')

echo $logical_cpu_per_phy_cpu

#是否打开有超线程

#如果在同一个物理CPU上有两个逻辑CPU具有相同的"core
id",那么超线程是打开的

#此处根据前面计算的 core_per_phy_cpu和
logical_cpu_per_phy_cpu的比较来查看超线程

if [ $logical_cpu_per_phy_cpu -gt $core_per_phy_cpu ]; then

echo "Hyper threading is enabled."

elif [ $logical_cpu_per_phy_cpu -eq $core_per_phy_cpu ]; then

echo "Hyper threading is NOT enabled."

else

echo "Error. There's something wrong."

fi

下面我们要做的是将这个脚本通过expect批量分发到指定范围的机器上进行自动化跑批量,以便能通过shell脚本自动统计批量采集所有被指定机器上的CPU相关信息。具体脚本实现思路如下:

1)通过expect实现将单一脚本自动免交互上传至指定客户机。

2)通过shell脚本中的循环语句,调取expect单机自动免交互上传文件脚本。从而实现将cpuinfo.sh脚本批量上传到客户机指定目录下。

3)通过expect实现自动免交互方式在指定单台客户机上执行指定命令或脚本程序。

4)通过shell脚本中的循环语句,调取单机免交互自动执行脚本程序的expect脚本。从而实现在多台机器上免交互自动执行shell脚本。

脚本案例如下:

1)通过expect实现将单一脚本自动免交互上传至指定客户机。

[root@localhost ~]# vim auto_upload_file.exp

#!/usr/bin/expect

# Filename: auto_upload_file.exp

if { $argc != 3 } {

puts "usage: expect $argv0 file host dir"

exit

}

set file [lindex $argv 0]

set host [lindex $argv 1]

set dir [lindex $argv 2]

set password "123456"

spawn scp -P22 -rp $file root@$host:$dir

expect {

"yes/no" {send "yes\r"; exp_continue}

"*password*" {send "$password\r"}

}

expect eof

[root@localhost ~]# chmod +x auto_upload_file.exp

[root@localhost ~]# ./auto_upload_file.exp

usage: expect ./auto_upload_file.exp file host dir

[root@localhost ~]# ./auto_upload_file.exp cpuinfo.sh
192.168.200.112 /opt

spawn scp -P22 -rp cpuinfo.sh root@192.168.200.112:/opt

root@192.168.200.112's password:

cpuinfo.sh 100% 1470 393.6KB/s 00:00

2)通过shell脚本中的循环语句,调取expect单机自动免交互上传文件脚本。从而实现将cpuinfo.sh脚本批量上传到客户机指定目录下。

[root@localhost ~]# vim auto_upload_files.sh

#!/bin/bash

# Filename : auto_upload_files.sh

if [ $# -ne 2 ]; then

echo "useage : sh $0 file dir"

exit 1

fi

file=$1

dir=$2

for IP in 192.168.200.{112..113} ; do

expect auto_upload_file.exp $file $IP $dir

done

[root@localhost ~]# chmod +x auto_upload_files.sh

[root@localhost ~]# ./auto_upload_files.sh

useage : sh ./auto_upload_files.sh file dir

[root@localhost ~]# ./auto_upload_files.sh cpuinfo.sh /opt/

spawn scp -P22 -rp cpuinfo.sh root@192.168.200.112:/opt/

root@192.168.200.112's password:

cpuinfo.sh 100% 1470 783.5KB/s 00:00

spawn scp -P22 -rp cpuinfo.sh root@192.168.200.113:/opt/

root@192.168.200.113's password:

cpuinfo.sh 100% 1470 662.7KB/s 00:00

3)通过expect实现自动免交互方式在指定单台客户机上执行指定命令或脚本程序。

[root@localhost ~]# vim auto_run_host.exp

#!/usr/bin/expect

# Filename: auto_run_1host.sh

if { $argc !=2 } {

puts "usage: expect $argv0 ip command"

exit

}

set ip [lindex $argv 0]

set cmd [lindex $argv 1]

set password "123456"

spawn ssh root@$ip $cmd

expect {

"yes/no" {send "yes\r"; exp_continue}

"*password*" {send "$password\r"}

}

expect eof

[root@localhost ~]# chmod +x auto_run_host.exp

[root@localhost ~]# ./auto_run_host.exp 192.168.200.112 "ifconfig
ens32"

spawn ssh root@192.168.200.112 ifconfig ens32

root@192.168.200.112's password:

ens32: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500

inet 192.168.200.112 netmask 255.255.255.0 broadcast 192.168.200.255

inet6 fe80::20c:29ff:fe8c:f2d9 prefixlen 64 scopeid 0x20<link>

ether 00:0c:29:8c:f2:d9 txqueuelen 1000 (Ethernet)

RX packets 2650 bytes 1541675 (1.4 MiB)

RX errors 0 dropped 0 overruns 0 frame 0

TX packets 1874 bytes 236034 (230.5 KiB)

TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0

[root@localhost ~]# ./auto_run_host.exp 192.168.200.112 "source
/opt/cpuinfo.sh"

spawn ssh root@192.168.200.112 source /opt/cpuinfo.sh

root@192.168.200.112's password:

logical CPU number in total: 1

phycical CPU number in total: 1

core number in a physical CPU: 1

logical CPU number in a phycical CPU:1

Hyper threading is NOT enabled.

4)通过shell脚本中的循环语句,调取单机免交互自动执行脚本程序的expect脚本。从而实现在多台机器上免交互自动执行shell脚本。

[root@localhost ~]# vim auto_run_hosts.sh

#!/bin/bash

# Filename: auto_run_hosts.sh

if [ $# -ne 1 ]; then

echo "useage: $0 cmd"

exit 3

fi

cmd=$1

for IP in 192.168.200.{112..113}; do

expect auto_run_host.exp $IP "$cmd"

done

[root@localhost ~]# chmod +x auto_run_hosts.sh

[root@localhost ~]# ./auto_run_hosts.sh

useage: ./auto_run_hosts.sh cmd

[root@localhost ~]# ./auto_run_hosts.sh "source
/opt/cpuinfo.sh"

spawn ssh root@192.168.200.112 source /opt/cpuinfo.sh

root@192.168.200.112's password:

logical CPU number in total: 1

phycical CPU number in total: 1

core number in a physical CPU: 1

logical CPU number in a phycical CPU:1

Hyper threading is NOT enabled.

spawn ssh root@192.168.200.113 source /opt/cpuinfo.sh

root@192.168.200.113's password:

logical CPU number in total: 1

phycical CPU number in total: 1

core number in a physical CPU: 1

logical CPU number in a phycical CPU:1

Hyper threading is NOT enabled.


文章转载自:

http://cIFPJIIT.xdjwh.cn
http://iXwbl7aO.xdjwh.cn
http://D7rC6Rlm.xdjwh.cn
http://dCzjWE7f.xdjwh.cn
http://SiHrVc6R.xdjwh.cn
http://4P9GxXt9.xdjwh.cn
http://o5qPIWcB.xdjwh.cn
http://aDQzpDm5.xdjwh.cn
http://nkHrvLzh.xdjwh.cn
http://RCoNKr5l.xdjwh.cn
http://hbiRCMfP.xdjwh.cn
http://B11FNxL6.xdjwh.cn
http://EwrI9yxF.xdjwh.cn
http://ZJ8ybxAt.xdjwh.cn
http://0jnPqNRM.xdjwh.cn
http://8ckk0EBR.xdjwh.cn
http://2kkEjTPM.xdjwh.cn
http://TKhV44iw.xdjwh.cn
http://ff6NKO2p.xdjwh.cn
http://CXDCu5I8.xdjwh.cn
http://v7JtPrVF.xdjwh.cn
http://T3q817Ck.xdjwh.cn
http://guRWNTVU.xdjwh.cn
http://VV56z3Gy.xdjwh.cn
http://CaAbMG89.xdjwh.cn
http://LoAWs31n.xdjwh.cn
http://jHzWlUUO.xdjwh.cn
http://wkF07B4J.xdjwh.cn
http://g2UFUZK1.xdjwh.cn
http://2o0W88Fh.xdjwh.cn
http://www.dtcms.com/a/375913.html

相关文章:

  • 交通识别摄像头以及带AI算法
  • SpringMVC通过注解实现全局异常处理
  • Linux基础知识(四)
  • 向量化与嵌入模型:RAG系统背后的隐形英雄
  • 你知道zip()和zip(*)怎么用吗?
  • 工业领域企业CRM常用的有哪些系统?
  • Git cherry-pick 与分支重置技术实现代码健全性保障下的提交记录精简
  • 【Nginx 运维实战】版本替换:强制 vs 平滑升级全解析
  • HTTPS加解密流程解析
  • Android 升级minSdkVersion 导致 包体积变大的处理
  • Linux系统 Python3.12版本连接达梦数据库dmPython和django_dmPython
  • 零知开源——ESP32驱动OV7670摄像头实现简易照相机系统
  • 前端开发工具trae的使用
  • Coze源码分析-资源库-创建插件-前端源码-核心组件
  • 数据集成平台怎么选?从ETL到CDC再到iPaaS的全景对比
  • 【Linux基础】Linux系统配置IP详解:从入门到精通
  • 2025版基于springboot的企业考勤管理系统
  • 【计算机毕业设计选题】2025-2026年计算机毕业设计选题经验与项目推荐
  • Python数据处理管道完全指南:从基础到高并发系统实战
  • VMware安装CentOS 7教程
  • SpringBoot + MinIO/S3 文件服务实现:FileService 接口与 FileServiceImpl 详解
  • 如何确定丝杆升降机的额定负载和峰值负载?
  • AI 与 Web3 技术写作大赛,瓜分 2000RMB
  • git 合并多条commit
  • 联邦学习指导、代码、实验、创新点
  • 开源 C++ QT Widget 开发(十五)多媒体--音频播放
  • 绿算技术闪耀智博会 赋能乡村振兴与产业升级
  • 差分数组(Difference Array)
  • 【硬核测评】格行ASR芯片+智能切网算法源码级解析(附高铁场景切换成功率99%方案)
  • 【git】首次clone的使用采用-b指定了分支,还使用了--depth=1 后续在这个基础上拉取所有的分支代码方法