您好,登錄后才能下訂單哦!
這篇“Golang pipe在不同場景下怎么遠程交互”文章的知識點大部分人都不太理解,所以小編給大家總結了以下內容,內容詳細,步驟清晰,具有一定的借鑒價值,希望大家閱讀完這篇文章能有所收獲,下面我們一起來看看這篇“Golang pipe在不同場景下怎么遠程交互”文章吧。
pipe實現從一個進程重定向至另一個進程,它是雙向數據通道,用于實現進行間通信。
io.Pipe函數創建內存同步通道,用于連接io.Reader和io.Writer. 本文示例使用環境為:
go version
go version go1.19.3 linux/amd64
在實現遠程交互之前,先看下面簡單示例,演示如何使用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方式實現遠程命令行交互。
當命令啟動時,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請求處理中使用管道。運行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請求和命令的標準輸入。
下面示例給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命令的輸出結果,顯示內容與預期一致。
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 pipe在不同場景下怎么遠程交互”這篇文章的內容,相信大家都有了一定的了解,希望小編分享的內容對大家有幫助,若想了解更多相關的知識內容,請關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。