Golang pipe在不同場(chǎng)景下遠(yuǎn)程交互
本文介紹Golang pipe,以及在不同場(chǎng)景下的應(yīng)用。
Pipe介紹
pipe實(shí)現(xiàn)從一個(gè)進(jìn)程重定向至另一個(gè)進(jìn)程,它是雙向數(shù)據(jù)通道,用于實(shí)現(xiàn)進(jìn)行間通信。
io.Pipe函數(shù)創(chuàng)建內(nèi)存同步通道,用于連接io.Reader和io.Writer. 本文示例使用環(huán)境為:
go version
go version go1.19.3 linux/amd64
Go pipe簡(jiǎn)單示例
在實(shí)現(xiàn)遠(yuǎn)程交互之前,先看下面簡(jiǎn)單示例,演示如何使用io.Pipe函數(shù):
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)
}
}
首先創(chuàng)建pipe,然后在協(xié)程中給管道的writer寫(xiě)數(shù)據(jù),然后使用io.Copy函數(shù)從管道Reader中拷貝數(shù)據(jù)至標(biāo)準(zhǔn)輸出:
go func() {
fmt.Fprint(w, "Hello there\n")
w.Close()
}()
在協(xié)程中寫(xiě)數(shù)據(jù)是因?yàn)槊看螌?xiě)PipeWriter都阻塞直到PipeReader完全消費(fèi)了數(shù)據(jù)。
運(yùn)行程序:
go run main.go
Hello there
通過(guò)這個(gè)簡(jiǎn)單示例,展示了管道重定向能力,了解這個(gè)基本原理后,下面先看Shell命令的管道,最終我們的目標(biāo)是通過(guò)WEB方式實(shí)現(xiàn)遠(yuǎn)程命令行交互。
Go cmd StdoutPipe
當(dāng)命令啟動(dòng)時(shí),Cmd的StdoutPipe返回管道連接命令的標(biāo)準(zhǔn)輸出:
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))
}
}
上面代碼啟動(dòng)ping命令,然后從其輸出中讀取4行. 這行代碼啟動(dòng)ping命令:
cmd := exec.Command("ping", "www.baidu.com")
stdout, err := cmd.StdoutPipe()
buf := bufio.NewReader(stdout)
接著獲取命令的標(biāo)準(zhǔn)輸出,并保存輸出之buf中。下面從緩沖中讀取4行:
for {
line, _, _ := buf.ReadLine()
if num > 3 {
os.Exit(0)
}
num += 1
fmt.Println(string(line))
}
讀取4行并輸出到控制臺(tái),運(yùn)行程序,輸出結(jié)果如下:
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
通過(guò)這個(gè)示例,成功地把命令的執(zhí)行結(jié)果捕獲到buffer中,并能夠增加處理邏輯再輸出到控制臺(tái)。
http請(qǐng)求處理中使用管道
下面示例展示在http請(qǐng)求處理中使用管道。運(yùn)行date命令,通過(guò)HTTP輸出結(jié)果,可以實(shí)現(xiàn)元從查看命令執(zhí)行結(jié)果。
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)
}
關(guān)鍵代碼為創(chuàng)建管道,并把PipeWriter賦給命令的標(biāo)準(zhǔn)輸出和標(biāo)準(zhǔn)錯(cuò)誤。
cmd := exec.Command("date")
pr, pw := io.Pipe()
defer pw.Close()
cmd.Stdout = pw
cmd.Stderr = pwgo io.Copy(w, pr)
然后在協(xié)程中拷貝PipeReader至http.ResponseWriter.最后運(yùn)行程序查看結(jié)果:
$ go run handler.go
server started on port 8080
使用curl或?yàn)g覽器訪問(wèn)地址:localhost:8080,可以看到:
2023年 02月 22日 星期三 17:06:11 CST
修改上面程序,把命令作為參數(shù),即可實(shí)現(xiàn)遠(yuǎn)程交互。下面我們看看如何利用管道給輸入端寫(xiě)入數(shù)據(jù),包括http請(qǐng)求和命令的標(biāo)準(zhǔn)輸入。
利用管道提交post請(qǐng)求json數(shù)據(jù)
下面示例給https://httpbin.org/post請(qǐng)求地址提交json數(shù)據(jù)作為請(qǐng)求體。
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))
}
上面示例實(shí)現(xiàn)給post請(qǐng)求提交json數(shù)據(jù),并讀取響應(yīng)內(nèi)容。
首先定義管道,然后在協(xié)程中給管道Writer寫(xiě)入json數(shù)據(jù):
go func() {
defer w.Close()
err := json.NewEncoder(w).Encode(&PayLoad{Content: "Hello there!"})
if err != nil {
log.Fatal(err)
}
}()
然后把管道Reader作為參數(shù)傳入請(qǐng)求:
resp, err := http.Post("https://httpbin.org/post", "application/json", r)
最后讀取響應(yīng)內(nèi)容:
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.Fatal(err)
}
fmt.Println(string(body))
運(yùn)行程序輸出結(jié)果:
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"
},
// 省略...
}
通過(guò)管道讀標(biāo)準(zhǔn)輸入
下面示例利用管道從標(biāo)準(zhǔn)輸入讀取數(shù)據(jù),并打印數(shù)據(jù)及數(shù)據(jù)字節(jié)數(shù)、塊數(shù):
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)
}
首先定義包裝標(biāo)準(zhǔn)輸入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))
然后創(chuàng)建4kb緩沖區(qū),從標(biāo)準(zhǔn)輸入讀數(shù)據(jù)至緩沖區(qū)。然后計(jì)算塊數(shù)和字節(jié)數(shù),最后答應(yīng)緩沖區(qū)內(nèi)容。
date | go run main.go
2023年 02月 22日 星期三 16:08:17 CSTBytes: 43 Chunks: 1
這里通過(guò)|操作傳遞date命令的輸出結(jié)果,顯示內(nèi)容與預(yù)期一致。
Go Stat
Stat函數(shù)返回FileInfo結(jié)構(gòu)體,描述文件信息。我們可以利用其檢查數(shù)據(jù)是否來(lái)自終端。
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)
}
}
這個(gè)示例數(shù)據(jù)可能來(lái)自終端或管道。為了判斷,通過(guò)下面代碼獲取stat:
stat, _ := os.Stdin.Stat()
獲取到標(biāo)準(zhǔn)輸入的FileInfo結(jié)構(gòu)體后進(jìn)行判斷:
if (stat.Mode() & os.ModeCharDevice) == 0 {這行判斷數(shù)據(jù)來(lái)自管道,反之則為終端。即如果沒(méi)有管道提供數(shù)據(jù),則提示用戶輸入數(shù)據(jù)。運(yùn)行程序:
$ echo "golang" | go run main.go
Hello golang!$go run main.go
Enter your name: java
Hello java!
總結(jié)
本文介紹了Golang管道的使用,除了實(shí)現(xiàn)遠(yuǎn)程命令交互,還介紹了獲取標(biāo)準(zhǔn)輸入內(nèi)容、判斷標(biāo)準(zhǔn)輸入數(shù)據(jù)來(lái)源。讀者組合這些簡(jiǎn)單示例,一定能夠編寫(xiě)出炫酷的應(yīng)用。
到此這篇關(guān)于Golang pipe在不同場(chǎng)景下遠(yuǎn)程交互的文章就介紹到這了,更多相關(guān)Golang pipe遠(yuǎn)程交互內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
十個(gè)Golang開(kāi)發(fā)中應(yīng)該避免的錯(cuò)誤總結(jié)
Go是一種靜態(tài)類(lèi)型的、并發(fā)的、垃圾收集的編程語(yǔ)言,由谷歌開(kāi)發(fā)。開(kāi)發(fā)人員在編寫(xiě)Go代碼時(shí)總會(huì)有一些常見(jiàn)的錯(cuò)誤,下面是Go語(yǔ)言中需要避免的十大壞錯(cuò)誤,希望對(duì)大家有所幫助2023-03-03
詳解Go如何優(yōu)雅的對(duì)時(shí)間進(jìn)行格式化
這篇文章主要為大家詳細(xì)介紹了Go語(yǔ)言中是如何優(yōu)雅的對(duì)時(shí)間進(jìn)行格式化的,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起了解一下2023-06-06
Golang操作mongodb的實(shí)現(xiàn)示例
本文主要介紹了Golang操作mongodb的實(shí)現(xiàn)示例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2025-03-03
Go語(yǔ)言defer語(yǔ)句的三種機(jī)制整理
在本篇文章里小編給大家分享的是一篇關(guān)于Go語(yǔ)言defer語(yǔ)句的三種機(jī)制整理,需要的朋友們學(xué)習(xí)下吧。2020-03-03
go-zero接入skywalking實(shí)現(xiàn)鏈路追蹤的詳細(xì)教程
SkyWalking是一個(gè)開(kāi)源的服務(wù)追蹤系統(tǒng),它專(zhuān)注于分布式系統(tǒng)的可觀測(cè)性和服務(wù)性能分析,它的主要目標(biāo)是幫助開(kāi)發(fā)者理解和優(yōu)化微服務(wù)架構(gòu)中的服務(wù)間交互情況,本文介紹了go-zero接入skywalking鏈路追蹤的詳細(xì)教程,需要的朋友可以參考下2024-08-08
Go實(shí)現(xiàn)自動(dòng)解壓縮包以及讀取docx/doc文件內(nèi)容詳解
在開(kāi)發(fā)過(guò)程中,我們常常需要處理壓縮包和文檔文件。本文將介紹如何使用Go語(yǔ)言自動(dòng)解壓縮包和讀取docx/doc文件,需要的可以參考一下2023-03-03

