Golang pipe在不同场景下远程交互
作者:梦想画家 发布时间:2024-05-09 09:45:58
本文介绍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 = pwgo 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 CSTBytes: 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!
来源:https://blog.csdn.net/neweastsun/article/details/129176591
猜你喜欢
- 1.分包背景这里首先介绍下MultiDex的产生背景。当Android系统安装一个应用的时候,有一步是对Dex进行优化,这个过程有一个专门的
- 简介在某些场景下,我们需要初始化一些资源,例如单例对象、配置等。实现资源的初始化有多种方法,如定义 package 级别的变量、在 init
- 今日上课,有位同学问到:w和w+有何区别呢。说实话,我们经常只是用一种权限,没用在意之间的区别,实际上,w+具有可读可写权限,而w只有可写权
- 本文代码将一些简单常用的SQL语句,拆分、封装成链式函数与终结函数,链式操作没有先后之分,实现傻瓜式mysql数据库操作。 同时学习下静态成
- 1. 题目编写程序, 4名牌手打牌,计算机随机将52张牌(不含大小鬼)发给4名牌手,在屏幕上显示每位牌手的牌。提示:设计出3个类:Card类
- NumPy广播(Broadcast),广播(Broadcast)是 numpy 对不同形状(shape)的数组进行数值计算的方式, 对数组的
- 如下所示:distances = np.sqrt(np.sum(np.asarray(airportPosition - x_vals)**
- 1、PandasPython Data Analysis Library 或 pandas 是基于NumPy 的一种工具,相当于这是Pyth
- 一、需求需求很简单,就是需要查询一个报表,只有1个表,数据量大约60万左右,但是中间有些逻辑。先说明一下服务器配置情况:1核CPU、2GB内
- 输入汉字提示拼音,试试下面这个函数,不知是不是你要的那个:查询汉字便宜到词典网<%function getpychar(ch
- 概述在之前的风资源分析文章中,有提到过用widrose包来进行玫瑰图的绘制,目前的可视化绘图包有很多,但是最基础和底层的,本人认为还是mat
- 因为要批量用某软件处理一批eps文件,所以要模拟鼠标及键盘动作,使其能够自动化操作。#-*-coding:utf-8-*-import os
- Unet是一个最近比较火的网络结构。它的理论已经有很多大佬在讨论了。本文主要从实际操作的层面,讲解pytorch从头开始搭建UNet++的过
- 前记Asyncio的同步原语可以简化我们编写资源竞争的代码和规避资源竞争导致的Bug的出现。 但是由于协程的特性,在大部分业务代码中并不需要
- 基于python+OpenCV的车牌号码识别,供大家参考,具体内容如下车牌识别行业已具备一定的市场规模,在电子警察、公路卡口、停车场、商业管
- 本文较为深入的探究了php中in_array函数用法。分享给大家供大家参考。具体如下:今天突然想到php中的in_array函数有个其怪的用
- 本文实例讲述了MySQL基于DOS命令行登录操作方法。分享给大家供大家参考,具体如下:常用的MySQL命令行登录语句如下:mysql -h
- phpqrcode类库官网下载地址: https://sourceforge.net/projects/phpqrcode/1.我们先看看p
- 我们知道现实中的数据通常是杂乱无章的,需要大量的预处理才能使用。Pandas 是应用最广泛的数据分析和处理库之一,它提供了多种对原始数据进行
- QQ医生在广大用户心中一直以来都是清爽便捷的一款安全工具,随着QQ医生的不断发展,QQ医生团队一直在思考,怎样能够给QQ医生用户带来性能更优