我的编程空间,编程开发者的网络收藏夹
学习永远不晚

Golangpipe在不同场景下远程交互

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

Golangpipe在不同场景下远程交互

本文介绍Golang pipe,以及在不同场景下的应用。

Pipe介绍

pipe实现从一个进程重定向至另一个进程,它是双向数据通道,用于实现进行间通信。

io.Pipe函数创建内存同步通道,用于连接io.Reader和io.Writer. 本文示例使用环境为:

go version
go version go1.19.3 linux/amd64

Go pipe简单示例

在实现远程交互之前,先看下面简单示例,演示如何使用io.Pipe函数:

package main
import (
    "fmt"
    "io"
    "log"
    "os"
)
func main() {
    r, w := io.Pipe()
    go func() {
        fmt.Fprint(w, "Hello there\n")
        w.Close()
    }()
    _, err := io.Copy(os.Stdout, r)
    if err != nil {
        log.Fatal(err)
    }
}

首先创建pipe,然后在协程中给管道的writer写数据,然后使用io.Copy函数从管道Reader中拷贝数据至标准输出:

go func() {
    fmt.Fprint(w, "Hello there\n")
    w.Close()
}()

在协程中写数据是因为每次写PipeWriter都阻塞直到PipeReader完全消费了数据。

运行程序:

go run main.go 
Hello there

通过这个简单示例,展示了管道重定向能力,了解这个基本原理后,下面先看Shell命令的管道,最终我们的目标是通过WEB方式实现远程命令行交互。

Go cmd StdoutPipe

当命令启动时,Cmd的StdoutPipe返回管道连接命令的标准输出:

package main
import (
    "bufio"
    "fmt"
    "log"
    "os"
    "os/exec"
)
func main() {
    cmd := exec.Command("ping", "www.baidu.com")
    stdout, err := cmd.StdoutPipe()
    if err != nil {
        log.Fatal(err)
    }
    cmd.Start()
    buf := bufio.NewReader(stdout) 
    num := 0
    for {
        line, _, _ := buf.ReadLine()
        if num > 3 {
            os.Exit(0)
        }
        num += 1
        fmt.Println(string(line))
    }
}

上面代码启动ping命令,然后从其输出中读取4行. 这行代码启动ping命令:

    cmd := exec.Command("ping", "www.baidu.com")
    stdout, err := cmd.StdoutPipe()
    buf := bufio.NewReader(stdout) 

接着获取命令的标准输出,并保存输出之buf中。下面从缓冲中读取4行:

for {
    line, _, _ := buf.ReadLine()
    if num > 3 {
        os.Exit(0)
    }
    num += 1
    fmt.Println(string(line))
}

读取4行并输出到控制台,运行程序,输出结果如下:

go run main.go
PING www.a.shifen.com (180.101.50.188) 56(84) bytes of data.
64 bytes from 180.101.50.188 (180.101.50.188): icmp_seq=1 ttl=53 time=12.0 ms
64 bytes from 180.101.50.188 (180.101.50.188): icmp_seq=2 ttl=53 time=11.2 ms
64 bytes from 180.101.50.188 (180.101.50.188): icmp_seq=3 ttl=53 time=10.5 ms

通过这个示例,成功地把命令的执行结果捕获到buffer中,并能够增加处理逻辑再输出到控制台。

http请求处理中使用管道

下面示例展示在http请求处理中使用管道。运行date命令,通过HTTP输出结果,可以实现元从查看命令执行结果。

package main
import (
    "fmt"
    "io"
    "net/http"
    "os/exec"
)
func handler(w http.ResponseWriter, r *http.Request) {
    cmd := exec.Command("date")
    pr, pw := io.Pipe()
    defer pw.Close()
    cmd.Stdout = pw
    cmd.Stderr = pw
    go io.Copy(w, pr)
    cmd.Run()
}
func main() {
    http.HandleFunc("/", handler)
    fmt.Println("server started on port 8080")
    http.ListenAndServe(":8080", nil)
}

关键代码为创建管道,并把PipeWriter赋给命令的标准输出和标准错误。

cmd := exec.Command("date")
pr, pw := io.Pipe()
defer pw.Close()
cmd.Stdout = pw
cmd.Stderr = pw

go io.Copy(w, pr)

然后在协程中拷贝PipeReader至http.ResponseWriter.最后运行程序查看结果:

$ go run handler.go 
server started on port 8080

使用curl或浏览器访问地址:localhost:8080,可以看到:

2023年 02月 22日 星期三 17:06:11 CST

修改上面程序,把命令作为参数,即可实现远程交互。下面我们看看如何利用管道给输入端写入数据,包括http请求和命令的标准输入。

利用管道提交post请求json数据

下面示例给https://httpbin.org/post请求地址提交json数据作为请求体。

package main
import (
    "encoding/json"
    "fmt"
    "io"
    "io/ioutil"
    "log"
    "net/http"
)
type PayLoad struct {
    Content string
}
func main() {
    r, w := io.Pipe()
    go func() {
        defer w.Close()
        err := json.NewEncoder(w).Encode(&PayLoad{Content: "Hello there!"})
        if err != nil {
            log.Fatal(err)
        }
    }()
    resp, err := http.Post("https://httpbin.org/post", "application/json", r)
    if err != nil {
        log.Fatal(err)
    }
    body, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println(string(body))
}

上面示例实现给post请求提交json数据,并读取响应内容。

首先定义管道,然后在协程中给管道Writer写入json数据:

go func() {
    defer w.Close()
    err := json.NewEncoder(w).Encode(&PayLoad{Content: "Hello there!"})
    if err != nil {
        log.Fatal(err)
    }
}()

然后把管道Reader作为参数传入请求:

resp, err := http.Post("https://httpbin.org/post", "application/json", r)

最后读取响应内容:

body, err := ioutil.ReadAll(resp.Body)
if err != nil {
    log.Fatal(err)
}
fmt.Println(string(body))

运行程序输出结果:

go run main.go
{
  "args": {}, 
  "data": "{\"Content\":\"Hello there!\"}\n", 
  "files": {}, 
  "form": {}, 
  "headers": {
    "Accept-Encoding": "gzip", 
    "Content-Type": "application/json", 
    "Host": "httpbin.org", 
    "Transfer-Encoding": "chunked", 
    "User-Agent": "Go-http-client/2.0", 
    "X-Amzn-Trace-Id": "Root=1-63f5c8c6-4a14ee9a2dc14e352f234fae"
  }, 
  // 省略...
}

通过管道读标准输入

下面示例利用管道从标准输入读取数据,并打印数据及数据字节数、块数:

package main
import (
    "bufio"
    "fmt"
    "io"
    "log"
    "os"
)
func main() {
    nBytes, nChunks := int64(0), int64(0)
    r := bufio.NewReader(os.Stdin)
    buf := make([]byte, 0, 4*1024)
    for {
        n, err := r.Read(buf[:cap(buf)])
        buf = buf[:n]
        if n == 0 {
            if err == nil {
                continue
            }
            if err == io.EOF {
                break
            }
            log.Fatal(err)
        }
        nChunks++
        nBytes += int64(len(buf))
        fmt.Println(string(buf))
        if err != nil && err != io.EOF {
            log.Fatal(err)
        }
    }
    fmt.Println("Bytes:", nBytes, "Chunks:", nChunks)
}

首先定义包装标准输入Reader:

r := bufio.NewReader(os.Stdin)
buf := make([]byte, 0, 4*1024)
n, err := r.Read(buf[:cap(buf)])
buf = buf[:n]
nChunks++
nBytes += int64(len(buf))
fmt.Println(string(buf))

然后创建4kb缓冲区,从标准输入读数据至缓冲区。然后计算块数和字节数,最后答应缓冲区内容。

date | go run main.go
2023年 02月 22日 星期三 16:08:17 CST

Bytes: 43 Chunks: 1

这里通过|操作传递date命令的输出结果,显示内容与预期一致。

Go Stat

Stat函数返回FileInfo结构体,描述文件信息。我们可以利用其检查数据是否来自终端。

package main
import (
    "bufio"
    "fmt"
    "log"
    "os"
)
func main() {
    stat, _ := os.Stdin.Stat()
    if (stat.Mode() & os.ModeCharDevice) == 0 {
        var buf []byte
        scanner := bufio.NewScanner(os.Stdin)
        for scanner.Scan() {
            buf = append(buf, scanner.Bytes()...)
        }
        if err := scanner.Err(); err != nil {
            log.Fatal(err)
        }
        fmt.Printf("Hello %s!\n", buf)
    } else {
        fmt.Print("Enter your name: ")
        var name string
        fmt.Scanf("%s", &name)
        fmt.Printf("Hello %s!\n", name)
    }
}

这个示例数据可能来自终端或管道。为了判断,通过下面代码获取stat:

stat, _ := os.Stdin.Stat()

获取到标准输入的FileInfo结构体后进行判断:

if (stat.Mode() & os.ModeCharDevice) == 0 {

这行判断数据来自管道,反之则为终端。即如果没有管道提供数据,则提示用户输入数据。运行程序:

$ echo "golang" | go run main.go
Hello golang!

$go run main.go
Enter your name: java
Hello java!

总结

本文介绍了Golang管道的使用,除了实现远程命令交互,还介绍了获取标准输入内容、判断标准输入数据来源。读者组合这些简单示例,一定能够编写出炫酷的应用。

到此这篇关于Golang pipe在不同场景下远程交互的文章就介绍到这了,更多相关Golang pipe远程交互内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

Golangpipe在不同场景下远程交互

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

Golangpipe在不同场景下远程交互

这篇文章主要介绍了Golangpipe在不同场景下远程交互,pipe实现从一个进程重定向至另一个进程,它是双向数据通道,用于实现进行间通信
2023-03-08

Golang pipe在不同场景下怎么远程交互

这篇“Golang pipe在不同场景下怎么远程交互”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“Golang pipe在不
2023-07-05

号和(|)在不同场景下的使用方法

号和(|)在不同场景下的使用方法,很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。掌握连接各个命令之间的连接符号用法也是很重要的。实际上,命令的用法并不难,例如 m
2023-06-05

在不同场景下选择适当的 C++ 函数

在 c++++ 中,根据不同场景选择适当的函数至关重要。常见函数包括 i/o 函数 (cin、cout)、字符串操作函数 (strlen、strcmp)、数学函数 (abs、sqrt) 和存储/动态分配函数 (new、delete)。选择函
在不同场景下选择适当的 C++ 函数
2024-04-12

C++ 内联函数在不同场景下的性能比较

内联函数通过消除函数调用开销优化性能:编译器可将内联函数内联到调用点,提升效率。基准测试表明,内联函数比非内联函数快约20%。编译器考虑函数大小、复杂度和调用频率等因素决定是否内联。C++ 内联函数在不同场景下的性能比较内联函数是一种编译
C++ 内联函数在不同场景下的性能比较
2024-04-28

golang匿名函数和闭包在不同场景下的性能差异对比

匿名函数和闭包在 go 语言中的性能差异主要源于内存分配和变量捕获。匿名函数直接分配在栈上,而闭包分配在堆上,闭包还捕获局部变量,可能导致额外的内存分配和引用计数,减慢执行速度。匿名函数适用于一次性操作和处理大量数据,而闭包则适用于跟踪状态
golang匿名函数和闭包在不同场景下的性能差异对比
2024-04-30

PHP 函数的参数传递方式在不同编程场景中的适用性?

php函数支持的参数传递方式有:引用传递:变量内存地址共享,函数修改直接影响原始变量。值传递:创建变量副本,函数修改不会影响原始变量。默认参数:预定义参数值,函数调用时无需提供。混合传递:同时支持引用和值传递,提供灵活性。PHP 函数的参数
PHP 函数的参数传递方式在不同编程场景中的适用性?
2024-04-15

Python中的多进程编程和多线程编程在不同场景下的适用性和性能差异是怎样的?

Python中的多进程编程和多线程编程在不同场景下的适用性和性能差异是怎样的?在Python中,多进程编程和多线程编程都是为了实现并行计算而存在的。然而,它们在适用性和性能上有一些不同之处。为了更好地理解它们的区别,我们将从适用性和性能两个
2023-10-26

阿里云服务器端口选择如何在不同场景下选择合适的端口

阿里云服务器是云计算服务的重要组成部分,其端口选择直接影响到服务器的稳定性和安全性。本文将详细介绍如何在不同场景下选择合适的阿里云服务器端口。一、阿里云服务器端口选择阿里云服务器的端口选择主要取决于服务器的应用场景和需求。常见的端口有22、80、443、222、25、587、1433、111、3306等。22端口
阿里云服务器端口选择如何在不同场景下选择合适的端口
2023-11-01

Java Lambda 表达式在不同场景中的应用:函数式编程的无限可能

Java Lambda 表达式是函数式编程的重要组成部分,它允许开发者使用更简洁、更富有表现力的代码来编写应用程序。在本文中,我们将介绍 Java Lambda 表达式的基本概念和语法,并通过一些常见的场景来演示 Lambda 表达式的应用。
Java Lambda 表达式在不同场景中的应用:函数式编程的无限可能
2024-02-26

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录