在Golang中执行curl bash命令并实时读取输出

在Golang中执行curl bash命令并实时读取输出 您好,我想通过 Go 语言运行一些 curl bash 脚本,并向用户实时显示这些脚本的输出。

例如,我想在 Go 中运行 curl -Lso- bench.sh | bash 并向用户显示实时输出,我该如何实现?

我已经尝试了以下方法:

package main

import (
	"bufio"
	"fmt"
	"io"
	"os"
	"os/exec"
	"strings"
)

实际上,我想通过 Go 运行一些 bash 脚本和二进制文件,并向用户显示输出(实时输出)。如果逐行读取并显示给用户也可以,但输出应该能快速显示给用户。

我不希望像 bash 脚本执行完毕后才显示输出那样。因为 bash 脚本会安装库和软件包,可能需要一些时间,所以我希望向用户实时显示 bash 脚本的输出。


更多关于在Golang中执行curl bash命令并实时读取输出的实战教程也可以访问 https://www.itying.com/category-94-b0.html

5 回复

我认为你需要重写这个命令,使其不使用 |。 我没有任何头绪,它应该是什么样子。 也许把它拆分成两个命令?…

更多关于在Golang中执行curl bash命令并实时读取输出的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


您可以考虑开发一个守护进程,它可以作为处理请求和响应的 AJAX 接口。

func main() {
        cmd := exec.Command(/* your command */)
        cmd.Stdout = os.Stdout
        cmd.Stderr = os.Stderr
        err := cmd.Run()
        if err != nil {
                log.Fatalf("cmd.Run() failed with %s\n", err)
        }
}

试试这个。

gucio321:

func main() {
    cmd := exec.Command(/* your command */)
    cmd.Stdout = os.Stdout
    cmd.Stderr = os.Stderr
    err := cmd.Run()
    if err != nil {
        log.Fatalf("cmd.Run() failed with %s\n", err)
    }
}

这个方法有效,谢谢,但是无法运行 curl -Lso- bench.sh | bash

在Go中执行外部命令并实时读取输出,可以通过exec.Command结合管道读取实现。以下是两种实现方式:

方法1:使用Scanner逐行读取(推荐)

package main

import (
	"bufio"
	"fmt"
	"io"
	"os"
	"os/exec"
	"strings"
)

func executeCommandWithRealtimeOutput(command string, args ...string) error {
	cmd := exec.Command(command, args...)
	
	// 获取标准输出管道
	stdout, err := cmd.StdoutPipe()
	if err != nil {
		return fmt.Errorf("创建标准输出管道失败: %v", err)
	}
	
	// 获取标准错误管道
	stderr, err := cmd.StderrPipe()
	if err != nil {
		return fmt.Errorf("创建标准错误管道失败: %v", err)
	}
	
	// 启动命令
	if err := cmd.Start(); err != nil {
		return fmt.Errorf("启动命令失败: %v", err)
	}
	
	// 创建读取器
	stdoutReader := bufio.NewReader(stdout)
	stderrReader := bufio.NewReader(stderr)
	
	// 实时读取输出
	done := make(chan bool)
	
	go func() {
		for {
			line, err := stdoutReader.ReadString('\n')
			if err != nil {
				if err != io.EOF {
					fmt.Printf("读取标准输出错误: %v\n", err)
				}
				break
			}
			fmt.Print(line)
		}
		done <- true
	}()
	
	go func() {
		for {
			line, err := stderrReader.ReadString('\n')
			if err != nil {
				if err != io.EOF {
					fmt.Printf("读取标准错误错误: %v\n", err)
				}
				break
			}
			fmt.Print(line)
		}
		done <- true
	}()
	
	// 等待goroutine完成
	<-done
	<-done
	
	// 等待命令执行完成
	if err := cmd.Wait(); err != nil {
		return fmt.Errorf("命令执行失败: %v", err)
	}
	
	return nil
}

func main() {
	// 执行curl命令
	err := executeCommandWithRealtimeOutput("bash", "-c", "curl -Lso- bench.sh | bash")
	if err != nil {
		fmt.Printf("执行失败: %v\n", err)
		os.Exit(1)
	}
}

方法2:使用io.Copy直接复制输出

package main

import (
	"fmt"
	"io"
	"os"
	"os/exec"
	"sync"
)

func executeCommandWithRealtimeOutput2(command string, args ...string) error {
	cmd := exec.Command(command, args...)
	
	// 获取标准输出和标准错误管道
	stdout, err := cmd.StdoutPipe()
	if err != nil {
		return fmt.Errorf("创建标准输出管道失败: %v", err)
	}
	
	stderr, err := cmd.StderrPipe()
	if err != nil {
		return fmt.Errorf("创建标准错误管道失败: %v", err)
	}
	
	// 启动命令
	if err := cmd.Start(); err != nil {
		return fmt.Errorf("启动命令失败: %v", err)
	}
	
	var wg sync.WaitGroup
	wg.Add(2)
	
	// 实时复制标准输出到控制台
	go func() {
		defer wg.Done()
		_, err := io.Copy(os.Stdout, stdout)
		if err != nil && err != io.EOF {
			fmt.Printf("复制标准输出错误: %v\n", err)
		}
	}()
	
	// 实时复制标准错误到控制台
	go func() {
		defer wg.Done()
		_, err := io.Copy(os.Stderr, stderr)
		if err != nil && err != io.EOF {
			fmt.Printf("复制标准错误错误: %v\n", err)
		}
	}()
	
	// 等待复制完成
	wg.Wait()
	
	// 等待命令执行完成
	if err := cmd.Wait(); err != nil {
		return fmt.Errorf("命令执行失败: %v", err)
	}
	
	return nil
}

func main() {
	// 执行curl命令
	err := executeCommandWithRealtimeOutput2("bash", "-c", "curl -Lso- bench.sh | bash")
	if err != nil {
		fmt.Printf("执行失败: %v\n", err)
		os.Exit(1)
	}
}

方法3:使用exec.CommandContext支持超时控制

package main

import (
	"context"
	"fmt"
	"io"
	"os"
	"os/exec"
	"time"
)

func executeCommandWithTimeout(command string, args ...string) error {
	// 设置30秒超时
	ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
	defer cancel()
	
	cmd := exec.CommandContext(ctx, command, args...)
	
	// 获取标准输出和标准错误管道
	stdout, err := cmd.StdoutPipe()
	if err != nil {
		return fmt.Errorf("创建标准输出管道失败: %v", err)
	}
	
	stderr, err := cmd.StderrPipe()
	if err != nil {
		return fmt.Errorf("创建标准错误管道失败: %v", err)
	}
	
	// 启动命令
	if err := cmd.Start(); err != nil {
		return fmt.Errorf("启动命令失败: %v", err)
	}
	
	// 实时读取输出
	go io.Copy(os.Stdout, stdout)
	go io.Copy(os.Stderr, stderr)
	
	// 等待命令完成
	if err := cmd.Wait(); err != nil {
		if ctx.Err() == context.DeadlineExceeded {
			return fmt.Errorf("命令执行超时")
		}
		return fmt.Errorf("命令执行失败: %v", err)
	}
	
	return nil
}

func main() {
	// 执行curl命令
	err := executeCommandWithTimeout("bash", "-c", "curl -Lso- bench.sh | bash")
	if err != nil {
		fmt.Printf("执行失败: %v\n", err)
		os.Exit(1)
	}
}

关键点说明:

  1. 使用StdoutPipe()StderrPipe():分别获取命令的标准输出和标准错误管道
  2. cmd.Start()后立即读取:确保在命令执行过程中就能读取到输出
  3. 使用goroutine并发读取:同时读取stdout和stderr,避免阻塞
  4. 使用io.Copybufio.Scanner:两种方式都能实现实时输出,io.Copy更简单,Scanner可以逐行处理

对于你的具体需求,建议使用方法1或方法2,它们都能实现实时显示curl -Lso- bench.sh | bash的输出。

回到顶部