溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

Golang?pipe在不同場景下怎么遠(yuǎn)程交互

發(fā)布時間:2023-03-08 10:47:01 來源:億速云 閱讀:107 作者:iii 欄目:開發(fā)技術(shù)

這篇“Golang pipe在不同場景下怎么遠(yuǎn)程交互”文章的知識點(diǎn)大部分人都不太理解,所以小編給大家總結(jié)了以下內(nèi)容,內(nèi)容詳細(xì),步驟清晰,具有一定的借鑒價值,希望大家閱讀完這篇文章能有所收獲,下面我們一起來看看這篇“Golang pipe在不同場景下怎么遠(yuǎn)程交互”文章吧。

Pipe介紹

pipe實(shí)現(xiàn)從一個進(jìn)程重定向至另一個進(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簡單示例

在實(shí)現(xiàn)遠(yuǎ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寫數(shù)據(jù),然后使用io.Copy函數(shù)從管道Reader中拷貝數(shù)據(jù)至標(biāo)準(zhǔn)輸出:

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

在協(xié)程中寫數(shù)據(jù)是因?yàn)槊看螌慞ipeWriter都阻塞直到PipeReader完全消費(fèi)了數(shù)據(jù)。

運(yùn)行程序:

go run main.go 
Hello there

通過這個簡單示例,展示了管道重定向能力,了解這個基本原理后,下面先看Shell命令的管道,最終我們的目標(biāo)是通過WEB方式實(shí)現(xiàn)遠(yuǎn)程命令行交互。

Go cmd StdoutPipe

當(dāng)命令啟動時,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))
    }
}

上面代碼啟動ping命令,然后從其輸出中讀取4行. 這行代碼啟動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行并輸出到控制臺,運(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

通過這個示例,成功地把命令的執(zhí)行結(jié)果捕獲到buffer中,并能夠增加處理邏輯再輸出到控制臺。

http請求處理中使用管道

下面示例展示在http請求處理中使用管道。運(yùn)行date命令,通過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)錯誤。

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

go io.Copy(w, pr)

然后在協(xié)程中拷貝PipeReader至http.ResponseWriter.最后運(yùn)行程序查看結(jié)果:

$ go run handler.go 
server started on port 8080

使用curl或?yàn)g覽器訪問地址:localhost:8080,可以看到:

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

修改上面程序,把命令作為參數(shù),即可實(shí)現(xiàn)遠(yuǎn)程交互。下面我們看看如何利用管道給輸入端寫入數(shù)據(jù),包括http請求和命令的標(biāo)準(zhǔn)輸入。

利用管道提交post請求json數(shù)據(jù)

下面示例給https://httpbin.org/post請求地址提交json數(shù)據(jù)作為請求體。

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請求提交json數(shù)據(jù),并讀取響應(yīng)內(nèi)容。

首先定義管道,然后在協(xié)程中給管道Writer寫入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ù)傳入請求:

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"
  }, 
  // 省略...
}

通過管道讀標(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 CST

Bytes: 43 Chunks: 1

這里通過|操作傳遞date命令的輸出結(jié)果,顯示內(nèi)容與預(yù)期一致。

Go Stat

Stat函數(shù)返回FileInfo結(jié)構(gòu)體,描述文件信息。我們可以利用其檢查數(shù)據(jù)是否來自終端。

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)
    }
}

這個示例數(shù)據(jù)可能來自終端或管道。為了判斷,通過下面代碼獲取stat:

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

獲取到標(biāo)準(zhǔn)輸入的FileInfo結(jié)構(gòu)體后進(jìn)行判斷:

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

這行判斷數(shù)據(jù)來自管道,反之則為終端。即如果沒有管道提供數(shù)據(jù),則提示用戶輸入數(shù)據(jù)。運(yùn)行程序:

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

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

以上就是關(guān)于“Golang pipe在不同場景下怎么遠(yuǎn)程交互”這篇文章的內(nèi)容,相信大家都有了一定的了解,希望小編分享的內(nèi)容對大家有幫助,若想了解更多相關(guān)的知識內(nèi)容,請關(guān)注億速云行業(yè)資訊頻道。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI