golang标准库SSH操作示例


文章目录

  • 前言
  • 一、了解SSH
  • 二、重要知识点
    • 1.安装ssh库
    • 2.ssh库重要知识牢记
  • 三、模拟连接远程服务器并执行命令
  • 四、SSH与os/exec标准库下执行命令的几种方式对比
  • 五、SSH库下三种执行命令方式演示
    • 5.1. session.CombinedOutput()示例
    • 5.2. session.Run()示例
    • 5.3. session.Start()、session.Wait()示例
  • 六、两种捕获标准输出和标准错误的方法:`StdoutPipe` / `StderrPipe` 和 `session.Stdout` / `session.Stderr`之间的区别
    • 6.1. 使用 `StdoutPipe` 和 `StderrPipe`捕获标准输出和标准错误
    • 6.2. 使用 重定向 `session.Stdout` 和 `session.Stderr` 捕获标准输出和标准错误
    • 6.3.两种方式的区别
  • 七、示例: 连接到多台服务器并执行多个命令返回命令执行结果
  • 总结


前言

SSH 全称为 Secure Shell,是一种用于安全地远程登录到网络上的其他计算机的网络协议。相信做运维的同学没有不了解 SSH的,比较常用的登录服务器的 shell 工具例如 Xshell、SecureCRT、iTerm2 等都是基于 SSH 协议实现的。Golang 中的的 crypto/ssh 库提供了实现 SSH 客户端的功能,本文接下来详细讲解下如何使用 Golang 实现操作 SSH 客户端,为后续运维开发的道路上使用golang编写脚本先夯实一下基础


一、了解SSH

在Golang中,有几个常用的SSH库,如golang.org/x/crypto/ssh和github.com/go-ssh/ssh。
本次将重点介绍golang.org/x/crypto/ssh,因为它是由Go官方维护的.SSH库功能分类:SSH客户端: 允许用户通过SSH协议连接到远程服务器。SSH服务器: 允许远程用户通过SSH协议连接到本地服务器。命令执行: 在远程服务器上执行命令。文件传输: 在本地和远程服务器之间传输文件。交会时会话: 类比xshell,当代码执行后,如同在操作真实的xshell一样

二、重要知识点

1.安装ssh库

代码如下(示例):

go get golang.org/x/crypto/ssh

2.ssh库重要知识牢记

结合演示代码一起更好理解

如下(示例):

1、client 对象(SSH 客户端)在整个程序中只创建一次2、可以通过 client.NewSession() 多次创建多个 session 对象.每个 session 是一个独立的会话,每次执行命令时都会创建一个新的会话3、每次 session.Run() 或 session.Start() 执行命令时,都会用新的会话来执行不同的命令这些会话共享底层的 SSH 连接,但是它们独立执行命令4、当某个会话的命令执行完毕,必须调用session.Close() 释放相关资源。5、切记不能在同一个 session 上并行执行多个命令。如果需要并行执行多个命令,应该创建多个 session

演示代码(示例):

package mainimport ("fmt""golang.org/x/crypto/ssh""log"
)func main() {// SSH 配置config := &ssh.ClientConfig{User: "root", // 替换为远程服务器的用户名Auth: []ssh.AuthMethod{ssh.Password("1"), // 替换为远程服务器密码},HostKeyCallback: ssh.InsecureIgnoreHostKey(), // 忽略主机密钥验证}// 连接远程服务器client, err := ssh.Dial("tcp", "192.168.56.160:22", config) // 替换为远程服务器的IP地址if err != nil {log.Fatalf("Failed to dial: %v", err)}defer client.Close()// 创建第一个会话session1, err := client.NewSession()if err != nil {log.Fatalf("Failed to create session 1: %v", err)}defer session1.Close()// 执行第一个命令fmt.Println("Executing command on session 1-1")err = session1.Run("echo Hello from session 1-1")if err != nil {log.Fatalf("Failed to run command on session 1-1: %v", err)}// 演示在第一个会话中执行第二个命令看是否能成功fmt.Println("Executing command on session 1-2")err = session1.Run("echo Hello from session 1-2")if err != nil {log.Fatalf("Failed to run command on session 1-2: %v", err)}// 创建第二个会话session2, err := client.NewSession()if err != nil {log.Fatalf("Failed to create session 2: %v", err)}defer session2.Close()// 执行第二个命令fmt.Println("Executing command on session 2")err = session2.Run("echo Hello from session 2")if err != nil {log.Fatalf("Failed to run command on session 2: %v", err)}// 创建第三个会话session3, err := client.NewSession()if err != nil {log.Fatalf("Failed to create session 3: %v", err)}defer session3.Close()// 执行第三个命令fmt.Println("Executing command on session 3")err = session3.Run("echo Hello from session 3")if err != nil {log.Fatalf("Failed to run command on session 3: %v", err)}fmt.Println("All commands executed successfully")
}

执行这段代码,返回如下所示,在同一个会话下并行的运行两条命令,发现运行失败
在这里插入图片描述
当将1-2这段代码注释掉后,再次运行代码可以成功运行,跟上述的描述一致
在这里插入图片描述
在这里插入图片描述

三、模拟连接远程服务器并执行命令

演示怎么在golang中使用SSH库连接服务器并执行相应的linux命令

package mainimport ("golang.org/x/crypto/ssh""log"
)func main() {// 创建SSH配置--密码认证config := &ssh.ClientConfig{User: "root",Auth: []ssh.AuthMethod{ssh.Password("1"), //密码认证},HostKeyCallback: ssh.InsecureIgnoreHostKey(),}// 创建SSH配置--SSH密钥认证(生产环境下建议采用该方式) 二选一即可//config := &ssh.ClientConfig{//User: "username",//Auth: []ssh.AuthMethod{//    ssh.PublicKeysFromFile("path/to/private/key", "path/to/public/key"),//},//	HostKeyCallback: ssh.FixedHostKey(hostKey),//}// 连接到远程服务器,并返回一个ssh客户端实例,/*返回值类型:*ssh.Clienterror*/client, err := ssh.Dial("tcp", "192.168.56.160:22", config)if err != nil {log.Fatalf("Failed to dial: %v", err)}defer client.Close()// 使用客户端创建一个ssh会话session, err := client.NewSession()if err != nil {log.Fatalf("Failed to create session: %v", err)}defer session.Close()// 在ssh会话中执行命令并输出命令结果out, err := session.CombinedOutput("ls /export")if err != nil {log.Fatalf("Failed to run: %v", err)}log.Printf("%s", out)
}

在这里插入图片描述

四、SSH与os/exec标准库下执行命令的几种方式对比

方法功能描述阻塞/非阻塞输出捕获使用场景
cmd:=exec.Command(“xx”,“x”)
err:=cmd.Run()
执行本地命令并等待命令完成,返回错误阻塞不捕获输出(需用 Output/CombinedOutput 捕获)本地命令执行,等待命令完成
err:=newsession.Run("xxx")执行远程命令并等待命令完成,返回错误阻塞不捕获输出(需手动捕获)远程 SSH 命令执行,等待完成
cmd:=exec.Command(“xx”,“xx”)
cmd.Start()
启动本地命令异步执行,不等待命令完成非阻塞,如果要阻塞,使用exec.Command().Wait()实现可通过 StdoutStderr 获取输出本地命令异步执行,非阻塞
err:=newsession.Start("xx")启动远程命令异步执行,不等待命令完成非阻塞,适用于需要启动后台进程的场景,如果要阻塞使用,newsession.Wait()实现可通过 StdoutStderr 获取输出远程命令异步执行,非阻塞
cmd:=exec.Command(“xx”,“x”)
out,err:=cmd.CombinedOutput()
执行本地命令并捕获标准输出和标准错误的合并输出阻塞捕获标准输出和标准错误的合并输出本地命令执行,捕获所有输出
out,err:=newsession.CombinedOutput("xx")执行远程命令并捕获标准输出和标准错误的合并输出阻塞捕获标准输出和标准错误的合并输出远程命令执行,捕获所有输出

五、SSH库下三种执行命令方式演示

5.1. session.CombinedOutput()示例

连接192.168.56.160服务器,并执行ls /var/log/命令查看目录下的文件

 注意事项:1、CombinedOutput()函数剖析func (s *ssh.Session) CombinedOutput(cmd string) ([]byte, error)接收参数类型 string返回值类型[]byte,error将[]byte转换为string类型输出的结果为命令的执行结果2、在一个session会话中执行多条命令的操作将多条命令保存在切片中,然后for循环将命令(value)传递给CombinedOutput()函数即可// 示例命令commands := []string{"ls -l /tmp", "uptime", "df -h"} for _, command := range commands {executeCommand(client, command, ip, resultChan, &mu)}out, err := session.CombinedOutput(commands)
package mainimport ("golang.org/x/crypto/ssh""log"
)func main() {// 创建SSH配置--密码认证config := &ssh.ClientConfig{User: "root",Auth: []ssh.AuthMethod{ssh.Password("1"), //密码认证},HostKeyCallback: ssh.InsecureIgnoreHostKey(),}// 连接到远程服务器,并返回一个ssh客户端实例client, err := ssh.Dial("tcp", "192.168.56.160:22", config)if err != nil {log.Fatalf("Failed to dial: %v", err)}defer client.Close()// 使用客户端创建一个ssh会话session, err := client.NewSession()if err != nil {log.Fatalf("Failed to create session: %v", err)}defer session.Close()// 在ssh会话中执行命令并输出命令结果。out, err := session.CombinedOutput("ls /var/log/")if err != nil {log.Fatalf("Failed to run: %v", err)}log.Printf("out:%s\n", out)
}

在这里插入图片描述

5.2. session.Run()示例

注意事项:session.Run(cmd string )errorfunc (s *ssh.Session) Run(cmd string) error接收参数类型 string返回类型  error<如果要想获取到执行的结果和错误,即区分标准输出和标准错误,则使用下方的方法>
package mainimport ("bytes""fmt""log""golang.org/x/crypto/ssh"
)// setupSSHClient 配置并返回一个SSH客户端
func setupSSHClient(user, password, host string, port int) (*ssh.Client, error) {config := &ssh.ClientConfig{User: user,Auth: []ssh.AuthMethod{ssh.Password(password),},HostKeyCallback: ssh.InsecureIgnoreHostKey(), // 注意:这里使用了不安全的回调,仅用于示例。在实际应用中,你应该验证主机密钥。}client, err := ssh.Dial("tcp", fmt.Sprintf("%s:%d", host, port), config)if err != nil {return nil, err}return client, nil
}
func main() {user := "root"password := "1"host := "192.168.56.162"port := 22 // 默认SSH端口是22client, err := setupSSHClient(user, password, host, port)if err != nil {log.Fatalf("Failed to setup SSH client: %v", err)}defer client.Close()if host == "192.168.56.162" {newsession, _ := client.NewSession()defer newsession.Close()//Run()// 创建一个缓冲区来捕获命令的输出var outputBuf bytes.Buffer// 将标准输出和标准错误都重定向到同一个缓冲区newsession.Stdout = &outputBufnewsession.Stderr = &outputBuferr := newsession.Run("ls /var/log/audit/")if err != nil {// 输出执行命令时的错误fmt.Printf("Error executing command: %v\n", err)}// 打印命令的输出(包括标准输出和标准错误)fmt.Printf("Command output:\n%s\n", outputBuf.String())}
}

在这里插入图片描述

5.3. session.Start()、session.Wait()示例

注意事项:func (s *ssh.Session) Start(cmd string) error接收参数类型 string返回类型  error如果要想获取到执行的结果和错误,即区分标准输出和标准错误,则使用下方的方法func (s *ssh.Session) Wait() error返回类型  error等待session.Start() 单独使用时,命令会在后台执行,程序不会等待命令的完成,立即继续执行后续代码。session.Start() 和 session.Wait() 一起使用时,程序会在 Wait() 处等待命令执行完成,之后才会继续执行后续的代码。
package mainimport ("fmt""golang.org/x/crypto/ssh""log""time"
)func main() {// SSH 配置config := &ssh.ClientConfig{User: "root", // 替换为远程服务器的用户名Auth: []ssh.AuthMethod{ssh.Password("1"), // 替换为远程服务器密码},HostKeyCallback: ssh.InsecureIgnoreHostKey(), // 忽略主机密钥验证}// 连接远程服务器client, err := ssh.Dial("tcp", "192.168.56.160:22", config) // 替换为远程服务器的IP地址if err != nil {log.Fatalf("Failed to dial: %v", err)}defer client.Close()// 创建会话session, err := client.NewSession()if err != nil {log.Fatalf("Failed to create session: %v", err)}defer session.Close()// 示例 1:使用 session.Start() 启动命令,但不等待fmt.Println("=== 示例 1: 使用 session.Start() 启动命令,不等待 ===")err = session.Start("sleep 5") // 启动一个后台命令if err != nil {log.Fatalf("Failed to start command: %v", err)}// 程序不会等待 sleep 5 执行完成,立即继续执行下一行fmt.Println("命令已启动,程序继续执行,不等待命令结束")// 等待一段时间,观察命令是否执行完time.Sleep(2 * time.Second)fmt.Println("程序在等待2秒后继续执行。")// 示例 2:使用 session.Start() 启动命令,并等待命令执行完毕// 创建新的会话用于第二个命令session2, err := client.NewSession()if err != nil {log.Fatalf("Failed to create session for second command: %v", err)}defer session2.Close()fmt.Println("\n=== 示例 2: 使用 session.Start() 启动命令,并调用 session.Wait() 等待 ===")err = session2.Start("sleep 5") // 启动一个后台命令if err != nil {log.Fatalf("Failed to start second command: %v", err)}// 程序会在这里等待命令执行完成err = session2.Wait() // 等待命令完成if err != nil {log.Fatalf("Failed to wait for command to finish: %v", err)}fmt.Println("命令执行完成,程序继续执行")// 结束fmt.Println("\n所有命令已执行完毕")
}

在这里插入图片描述

六、两种捕获标准输出和标准错误的方法:StdoutPipe / StderrPipesession.Stdout / session.Stderr之间的区别

6.1. 使用 StdoutPipeStderrPipe捕获标准输出和标准错误

重要代码示例

// 获取 标准输出和标准错误
stdout, _ := session.StdoutPipe()
output := make([]byte, 1024)
for {n, err := stdout.Read(output)if err != nil {break}fmt.Sprintf("STDOUT from %s: %s", ip, string(output[:n]))
}stderr, err := session.StderrPipe()
output := make([]byte, 1024)
for {n, err := stderr.Read(output)if err != nil {break}fmt.Sprintf("STDERR from %s: %s", ip, string(output[:n]))
}

解释

1. 使用 `StdoutPipe` 和 `StderrPipe`:- `StdoutPipe()` 和 `StderrPipe()` 返回一个 `io.Reader`,可以用来读取远程命令的标准输出(stdout)和标准错误输出(stderr)- 可以通过从这些管道中读取数据来获取命令的输出,通常会使用协程来异步读取这些管道中的数据2. 工作原理:- 首先通过 `session.StdoutPipe()` 和 `session.StderrPipe()` 获取输出的管道(`io.Reader`)- 然后在程序中手动读取这些管道的内容,通常通过 `io.Copy` 或者 `bufio.Reader` 来处理流。- 这种方式适用于需要处理较大输出或需要实时读取命令输出的场景。3. 优点:- 可以实时读取输出,因为管道是持续开放的,适合需要处理大量数据或逐行输出的情况。- 可以分别处理标准输出和标准错误,提供更多灵活性。4. 缺点:- 需要异步读取标准输出和标准错误,可能需要更多的代码来确保并发处理和同步。- 适用于需要实时处理输出的场景,不适合简单的命令输出捕获。

6.2. 使用 重定向 session.Stdoutsession.Stderr 捕获标准输出和标准错误

重要代码示例

....
// 创建一个缓冲区来捕获命令的输出
var outputBuf bytes.Buffer
// 将标准输出和标准错误都重定向到同一个缓冲区
session.Stdout = &outputBuf
session.Stderr = &outputBuf
err := newsession.Run("ls /var/log/audit/")
if err != nil {// 输出执行命令时的错误fmt.Printf("Error executing command: %v\n", err)
}
// 打印命令的输出(包括标准输出和标准错误)
fmt.Printf("Command output:\n%s\n", outputBuf.String())
...

解释

1. 使用 `newsession.Stdout` 和 `newsession.Stderr`:- `session.Stdout` 和 `session.Stderr` 分别是 `io.Writer` 类型,允许将命令的标准输出和标准错误直接写入一个缓冲区(如 `bytes.Buffer`)。- 可以通过 `outputBuf.String()` 获取完整的命令输出。这里,`Stdout` 和 `Stderr` 都被重定向到同一个 `bytes.Buffer`,- 这样就能捕获命令的所有输出(无论是标准输出还是标准错误)。2. 工作原理:- `session.Run()` 会直接执行命令并把标准输出和标准错误都写入到指定的缓冲区。- 不需要异步读取输出,命令执行完成后,只需要读取 `outputBuf` 即可获取所有输出。3. 优点:- 代码简单,易于实现,适合捕获简单的命令输出。- 不需要显式地管理异步读取标准输出和错误流,适用于不需要实时处理输出的场景。- 适合于简单的任务(例如调试、输出日志等)并且输出数据量较小的情况。4. 缺点:- 如果命令输出量大或者需要实时处理输出,可能会遇到缓冲区的限制或延迟。- 不能实时读取输出,必须等命令执行完毕才能获取所有输出。

6.3.两种方式的区别

1. 实时性:- `StdoutPipe` 和 `StderrPipe`:适合实时读取标准输出和标准错误。可以在命令执行的过程中动态处理输出数据。- `Stdout` 和 `Stderr`:适合捕获命令执行后的完整输出,并不实时读取。如果需要完整的命令输出,一次性获取比较简单。2. 使用场景:- `StdoutPipe` 和 `StderrPipe`:适合输出较大、需要流式处理的场景,比如你需要逐行读取或实时处理命令输出的场景。- `Stdout` 和 `Stderr`:适合捕获命令的完整输出并一次性处理,代码简单,适合小规模的输出捕获。3. 复杂性:- `StdoutPipe` 和 `StderrPipe`:稍微复杂,因为需要处理并发读取输出流,可能涉及协程。- `Stdout` 和 `Stderr`:简单易懂,适合不需要实时读取输出的情况。根据实际需求,可以选择适合的方式:如果需要并发处理或实时处理输出流,使用 `StdoutPipe` 和 `StderrPipe`如果需要一次性获取完整输出,使用 `Stdout` 和 `Stderr` 会更加简洁。

七、示例: 连接到多台服务器并执行多个命令返回命令执行结果

先看代码再分析

package mainimport ("bufio""fmt""log""os""strings""sync""golang.org/x/crypto/ssh"
)func executeCommand(client *ssh.Client, command string, ip string, resultChan chan<- string, mu *sync.Mutex) {// 创建一个新的 SSH 会话session, err := client.NewSession()if err != nil {log.Println("Failed to create session:", err)resultChan <- fmt.Sprintf("Error on %s: Failed to create session", ip)return}defer session.Close()// 获取 Stdout 和 Stderr 输出stdout, err := session.StdoutPipe()if err != nil {log.Println("Failed to get StdoutPipe:", err)resultChan <- fmt.Sprintf("Error on %s: Failed to get StdoutPipe", ip)return}stderr, err := session.StderrPipe()if err != nil {log.Println("Failed to get StderrPipe:", err)resultChan <- fmt.Sprintf("Error on %s: Failed to get StderrPipe", ip)return}// 启动命令err = session.Start(command)if err != nil {log.Println("Failed to start command:", err)resultChan <- fmt.Sprintf("Error on %s: Failed to start command", ip)return}// 使用锁来确保对共享资源(如输出的打印)是串行的mu.Lock()defer mu.Unlock()// 读取命令输出并打印到管道go func() {output := make([]byte, 1024)for {n, err := stdout.Read(output)if err != nil {break}resultChan <- fmt.Sprintf("STDOUT from %s: %s", ip, string(output[:n]))}}()go func() {output := make([]byte, 1024)for {n, err := stderr.Read(output)if err != nil {break}resultChan <- fmt.Sprintf("STDERR from %s: %s", ip, string(output[:n]))}}()// 等待命令执行完毕err = session.Wait()if err != nil {log.Println("Error executing command:", err)resultChan <- fmt.Sprintf("Error on %s: %v", ip, err)} else {resultChan <- fmt.Sprintf("Command executed successfully on %s", ip)}
}func main() {// 加载 IP 地址文件file, err := os.Open("/export/test/ips.txt")if err != nil {log.Fatal("Failed to open file:", err)}defer file.Close()// 读取 IP 地址var ips []stringscanner := bufio.NewScanner(file)for scanner.Scan() {ip := strings.TrimSpace(scanner.Text())if ip != "" {ips = append(ips, ip)}}if err := scanner.Err(); err != nil {log.Fatal("Failed to read file:", err)}// 设置 SSH 客户端配置,使用密码认证sshConfig := &ssh.ClientConfig{User: "root", // SSH 用户名Auth: []ssh.AuthMethod{ssh.Password("1"), // 密码认证},HostKeyCallback: ssh.InsecureIgnoreHostKey(), // 注意:生产环境中不建议使用此选项}// 创建一个管道用于接收结果resultChan := make(chan string, len(ips)*3) // 每台机器执行多个命令,调整管道容量var wg sync.WaitGroupvar mu sync.Mutex // 创建锁// 遍历 IP 地址,并为每个 IP 地址启动一个 goroutinefor _, ip := range ips {wg.Add(1)go func(ip string) {defer wg.Done()// 建立 SSH 连接client, err := ssh.Dial("tcp", ip+":22", sshConfig)if err != nil {log.Printf("Failed to connect to %s: %v", ip, err)resultChan <- fmt.Sprintf("Failed to connect to %s", ip)return}defer client.Close()// 对每台机器执行多个命令commands := []string{"ls -l /tmp", "uptime", "df -h"} // 示例命令for _, command := range commands {executeCommand(client, command, ip, resultChan, &mu)}}(ip)}// 在所有任务完成之后关闭 resultChango func() {wg.Wait()close(resultChan)}()// 输出所有结果for result := range resultChan {fmt.Println(result)}
}

在这里插入图片描述

涉及到的知识点:1、管道2、互斥锁3、goroutine并发4、SSH5、session.Start/Wait6、分开捕获标准输出和标准错误7、按行读取文件内容上述代码示例演示了如何在多台机器上并发执行多个命令,并使用 sync.Mutex 来保护共享资源(如管道)的访问具体流程:1、从文件中按行读取IP并保存到切片ips中2、设置ssh配置,从管道中读取IP,将每个服务器连接和每个要执行的命令都放在一个 goroutine中。主程序继续启动新的 goroutine 执行任务,而不会因为某一台服务器的命令执行而导致整个程序阻塞3、将连接信息和捕获的标准输出和标准错误信息都写入到管道中4、当服务器连接成功后,调用执行命令函数executeCommand,再该代码中的锁用于保护共享资源(resultChan)的访问因为如果多个 goroutine 同时向通道发送数据(比如日志输出)没有锁会导致输出混乱(多个 goroutine 的日志可能会交错,难以看清)使用 sync.Mutex 来确保每次只有一个 goroutine 向通道发送数据,从而保证输出日志的顺序和一致性保证了多个 goroutine 在写入 resultChan 时不会互相干扰,避免了并发写入导致的数据不一致或错乱5、当所有远程机器的命令执行完成后,关闭会话、关闭通道,最终再打印出通道中所有的日志信息

总结

以上就是SSH标准库自己整理的知识,故不积跬步,无以至千里;不积小流,无以成江海,慢慢整理golang中运维可以使用到的相关库,向运维逐渐靠拢

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/65240.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

替代传统FTP传输,镭速大数据传输系统实现安全高效数据流转!

信息技术的快速进步让大数据成为了企业决策的关键支撑&#xff0c;但同时也带来了巨大的挑战。企业在运营过程中产生的数据量急剧增加&#xff0c;这对数据传输的速度、安全性和效率提出了更高的要求。然而&#xff0c;传统的FTP传输方式在处理大规模数据时显得力不从心&#x…

MyBatis如何处理延迟加载?

大家好&#xff0c;我是锋哥。今天分享关于【MyBatis如何处理延迟加载&#xff1f;】面试题。希望对大家有帮助&#xff1b; MyBatis如何处理延迟加载&#xff1f; 1000道 互联网大厂Java工程师 精选面试题-Java资源分享网 MyBatis 支持 延迟加载&#xff08;Lazy Loading&am…

Matlab环形柱状图

数据准备&#xff1a; 名称 数值 Aa 21 Bb 23 Cc 35 Dd 47 保存为Excel文件后&#xff1a; % Load data from Excel file filename data.xlsx; % Ensure the file is in the current folder or provide full path dataTable readtable(filena…

论文研读:AnimateDiff—通过微调SD,用图片生成动画

1.概述 AnimateDiff 设计了3个模块来微调通用的文生图Stable Diffusion预训练模型, 以较低的消耗实现图片到动画生成。 论文名&#xff1a;AnimateDiff: Animate Your Personalized Text-to-Image Diffusion Models without Specific Tuning 三大模块&#xff1a; 视频域适应…

洛谷 P1014:Cantor 表

【题目来源】https://www.luogu.com.cn/problem/P1014https://www.acwing.com/problem/content/5510/【题目描述】 现代数学的著名证明之一是 Georg Cantor 证明了有理数是可枚举的。 他是用下面这一张表来证明这一命题的&#xff1a; 1/1 1/2 1/3 1/4 1/5 … 2/1 2/2 …

【网络安全零基础入门】PHP环境搭建、安装Apache、安装与配置MySQL(非常详细)零基础入门到精通,收藏这一篇就够(01)_php安装配置教程

这是大白给粉丝朋友准备的网络安全零基础入门第八章PHP入门的知识章节里的环境搭建。 喜欢的朋友们&#xff0c;记得给大白点赞支持和收藏一下&#xff0c;关注我&#xff0c;学习黑客技术。 一、php简介 php定义&#xff1a;一种服务器端的 HTML脚本/编程语言&#xff0c;是…

RBTree(红黑树)

目录 红黑树的概念 红黑树的性质 红黑树节点的定义 红黑树的插入 1. 按照二叉搜索的树规则插入新节点 2. 检测新节点插入后&#xff0c;红黑树的性质是否造到破坏 红黑树的检测 红黑树的删除 红黑树和AVL树的比较 红黑树的概念 红黑树&#xff0c;是一种二叉搜索树&…

JS 三种添加元素的方式、区别( write、createElement、innerHTML )

文章目录 1. 区别结论2. write 不同场合的效果3. createElement 和 innerHTML 耗时对比 1. 区别结论 方式说明document.write不建议使用, 使用时要小心, 不同场合, 效果不同document.createElement添加少量元素时建议使用, 结构清晰易读innerHTML添加大量元素时建议使用 2. wr…

300多种复古手工裁剪拼贴艺术时尚字母、数字、符号海报封面Vlog视频MOV+PNG素材

300复古时尚大小写字母、数字、符号拼贴海报封面平面设计Vlog视频标题动画 Overlay - Cut-Out Letters Animations Pack - Animated Letters, Numbers, and Symbols 使用 Cut-Out Letters Animations Pack 提升您的内容&#xff01;包含 300多个高品质动画资源&#xff0c;包括…

SpringCloudAlibaba技术栈-Dubbo

1、什么是Dubbo? 简单来说&#xff0c;dubbo就像是个看不见的手&#xff0c;负责专门从注册中心nacos调用注册到nacos上面的服务的&#xff0c;因为在微服务环境下不同的功能模块可能在不同的服务器上。dubbo调用服务就像是在调用本地的服务一样。 分布式调用与高并发处理 Du…

剪映学习01

1.剪映界面介绍 1.点击左上角的的登录账户可以登录剪映&#xff0c;它可以和抖音账号共用&#xff0c;所以我们剪辑完视频后可以直接从抖音发布。 左侧的导航栏有一些功能&#xff0c;我们点击模板&#xff0c;剪映它会显示当下比较火的模板&#xff0c;如果我们剪视频需要用到…

OpenLinkSaas使用手册-简介

OpenLinkSaas是针对软件研发人员/团队的效能工具。对个人而言是工具加成长导航路线&#xff0c;对团队而言是团队管理和项目管理。 OpenLinkSaas虽然功能众多&#xff0c;但可以按需配置所需功能&#xff0c;也可以制作自己的发行版。 OpenLinkSaas的由来 软件研发是一个比较…

QT调用Sqlite数据库

QT设计UI界面&#xff0c;后台访问数据库&#xff0c;实现数据库数据的增删改查。 零售商店系统 数据库表&#xff1a; 分别是顾客表&#xff0c;订单详情表&#xff0c;订单表&#xff0c;商品表 表内字段详情如下&#xff1a; 在QT的Pro文件中添加sql&#xff0c;然后添加头…

vue3使用vant日历组件(calendar),自定义日历下标的两种方法

在vue3中使用vant日历组件&#xff08;calendar&#xff09;自定义下标的两种方法&#xff0c;推荐使用第二种&#xff1a; 日期下方加小圆点&#xff1a; 一、使用伪元素样式实现(::after伪元素小圆点样式会被覆盖&#xff0c;只能添加一个小圆点) 代码如下&#xff08;示例…

STM32学习之 按键/光敏电阻 控制 LED/蜂鸣器

STM32学习之 按键/光敏电阻 控制 LED/蜂鸣器 1、按键控制 LED 按键:常见的输入设备&#xff0c;按下导通&#xff0c;松手断开 按键抖动:由子按键内部使用的是机械式弹簧片来进行通断的、所以在按下和松手的瞬间会伴随有一连串的抖动 按键控制LED接线图&#xff1a; 要有工程…

2024金融大模型实践方案的概览(附实践资料合集)

金融大模型实践方案的全面总结&#xff1a; 金融大模型应用评测&#xff1a; 在金融评测的五大能力维度中&#xff0c;各模型整体表现基本满足当下场景需求&#xff0c;其中金融安全与价值对齐表现优异&#xff0c;但金融专业认知和多模态处理能力仍存在较大提升空间。 金融大模…

设计模式之享元模式:看19路棋盘如何做到一子千面

~犬&#x1f4f0;余~ “我欲贱而贵&#xff0c;愚而智&#xff0c;贫而富&#xff0c;可乎&#xff1f; 曰&#xff1a;其唯学乎” 一、享元模式概述 \quad 在软件设计中&#xff0c;享元模式(Flyweight Pattern)的核心思想是通过共享来有效地支持大量细粒度对象的重用。这里的…

英语单词拼读小程序开发制作介绍

英语单词拼读小程序开发制作介绍本英语单词拼读小程序系统开发的主要功能有&#xff1a; 1、按年级分类展示每个年级阶段的英语单词信息。 2、点击选择的单词进入单词拼读页面&#xff0c;展示英语单词的拼读音标、中文意思、单词发音、拆分词汇发音、用户通过朗读发音对比。通…

华为管理变革之道:管理制度创新

目录 华为崛起两大因素&#xff1a;管理制度创新和组织文化。 管理是科学&#xff0c;150年来管理史上最伟大的创新是流程 为什么要变革&#xff1f; 向世界标杆学习&#xff0c;是变革第一方法论 体系之一&#xff1a;华为的DSTE战略管理体系&#xff08;解决&#xff1a…

基于Spring Boot的中国戏曲文化传播系统

一、系统背景与意义 中国戏曲作为中华民族的文化瑰宝&#xff0c;具有深厚的历史底蕴和艺术价值。然而&#xff0c;随着现代生活节奏的加快和娱乐方式的多样化&#xff0c;传统戏曲文化的传播和普及面临诸多挑战。因此&#xff0c;开发一个基于Spring Boot的中国戏曲文化传播系…