概述

unix/linux OS 的一个进程的输出可以是另一个进程的输入,这些进程使用stdin与stdout设备作为通道,在进程之间传递数据。

同样的,GO中有io.Reader与io.Writer两个接口,如果要对一个设备进行读/写,调用实现了这两个接口的方法即可。

GO对管道的支持

func t1()  {
cmd0 := exec.Command("echo","-n","how are you")
fmt.Println(cmd0)
}

exec.Command返回的对象的几个主要方法:

    // Stdin specifies the process's standard input.
//
// If Stdin is nil, the process reads from the null device (os.DevNull).
//
// If Stdin is an *os.File, the process's standard input is connected
// directly to that file.
//
// Otherwise, during the execution of the command a separate
// goroutine reads from Stdin and delivers that data to the command
// over a pipe. In this case, Wait does not complete until the goroutine
// stops copying, either because it has reached the end of Stdin
// (EOF or a read error) or because writing to the pipe returned an error.
Stdin io.Reader // Stdout and Stderr specify the process's standard output and error.
//
// If either is nil, Run connects the corresponding file descriptor
// to the null device (os.DevNull).
//
// If either is an *os.File, the corresponding output from the process
// is connected directly to that file.
//
// Otherwise, during the execution of the command a separate goroutine
// reads from the process over a pipe and delivers that data to the
// corresponding Writer. In this case, Wait does not complete until the
// goroutine reaches EOF or encounters an error.
//
// If Stdout and Stderr are the same writer, and have a type that can
// be compared with ==, at most one goroutine at a time will call Write.
Stdout io.Writer
Stderr io.Writer

示例

/*
参数传入的是命令组
通过buffer一次次读取返回结果,不怕返回的数据量大
如果命令是shell那样有|管道符,则使用Pip方法即可
*/
func PipCmd(cmd []string,useBufferIO... bool) string {
useBufferedIO := false ll := len(useBufferIO)
if ll > 0 {
useBufferedIO = useBufferIO[0]
}
cmd0 := Command(cmd)
stdout0, err := cmd0.StdoutPipe()
if err != nil {
fmt.Printf("Error: Couldn't obtain the stdout pipe for command : %s\n", err)
return ""
}
defer stdout0.Close()
if err := cmd0.Start(); err != nil {
fmt.Printf("Error: The command can not be startup: %s\n", err)
return ""
}
if !useBufferedIO {
var outputBuf0 bytes.Buffer
for {
tempOutput := make([]byte, 1024)
n, err := stdout0.Read(tempOutput)
if err != nil {
if err == io.EOF {
break
} else {
fmt.Printf("Error: Couldn't read data from the pipe: %s\n", err)
return ""
}
}
if n > 0 {
outputBuf0.Write(tempOutput[:n])
}
}
//fmt.Printf("%s\n", outputBuf0.String())
res := fmt.Sprintf("%v",outputBuf0.String())
return res
} else {
outputBuf0 := bufio.NewReader(stdout0)
var resBuffer bytes.Buffer for{
//outputBuf0 默认带一个4K的缓冲区,第二个参数为false表示读取完所有的行
output0, _, err := outputBuf0.ReadLine() if err != nil {
if err == io.EOF{
break
}
fmt.Printf("Error: Couldn't read data from the pipe: %s\n", err)
return ""
}
output0 = append(output0,'\n')
resBuffer.Write(output0)
}
res := fmt.Sprintf("%v",resBuffer.String())
return res
}
} func Command(cmds []string) *exec.Cmd {
name:= cmds[0]
cmd := &exec.Cmd{
Path: name,
Args: cmds,
} if filepath.Base(name) == name {
if lp, err := exec.LookPath(name); err != nil {
//cmd.lookPathErr = err
ErrorHandle(err)
} else {
cmd.Path = lp
}
}
return cmd
}
func a11()  {
//命令行参数不能包含空格,比如-ht 是错的,-ht是对的
cmd := []string{"/opt/wks/go/dbm_go/src/dbm/consistency/consis_0.6.7", "-cht", "192.168.177.67", "-cpt", "3316", "-ht","114.67.105.113,192.168.177.67", "-pt","3306,3316", "-slot", "-json", "-db", "vodb", "-timeStart", "2020-09-11 14:09:27", "-pl", "2"}
res := tools.PipCmd(cmd,true)
fmt.Println(res)
}

管道的返回值

stdout0, err := cmd0.StdoutPipe()
// StdoutPipe returns a pipe that will be connected to the command's
// standard output when the command starts.
//
// Wait will close the pipe after seeing the command exit, so most callers
// need not close the pipe themselves. It is thus incorrect to call Wait
// before all reads from the pipe have completed.
// For the same reason, it is incorrect to call Run when using StdoutPipe.
// See the example for idiomatic usage.
func (c *Cmd) StdoutPipe() (io.ReadCloser, error) {
if c.Stdout != nil {
return nil, errors.New("exec: Stdout already set")
}
if c.Process != nil {
return nil, errors.New("exec: StdoutPipe after process started")
}
pr, pw, err := os.Pipe()
if err != nil {
return nil, err
}
c.Stdout = pw
c.closeAfterStart = append(c.closeAfterStart, pw)
c.closeAfterWait = append(c.closeAfterWait, pr)
return pr, nil
}

管道返回一个

io.ReadCloser
// ReadCloser is the interface that groups the basic Read and Close methods.
type ReadCloser interface {
Reader
Closer
}

不仅实现了读接口,还可以进行关闭操作

管道的内部实现

pr, pw, err := os.Pipe()

系统创建了一个管道Pipe,内部加了读锁syscall.RorkLock.RLock(),然后返回一个读文件句柄pr,和一个写文件句柄pw

// Pipe returns a connected pair of Files; reads from r return bytes written to w.
// It returns the files and an error, if any.
func Pipe() (r *File, w *File, err error) {
var p [2]int e := syscall.Pipe2(p[0:], syscall.O_CLOEXEC)
// pipe2 was added in 2.6.27 and our minimum requirement is 2.6.23, so it
// might not be implemented.
if e == syscall.ENOSYS {
// See ../syscall/exec.go for description of lock.
syscall.ForkLock.RLock()
e = syscall.Pipe(p[0:])
if e != nil {
syscall.ForkLock.RUnlock()
return nil, nil, NewSyscallError("pipe", e)
}
syscall.CloseOnExec(p[0])
syscall.CloseOnExec(p[1])
syscall.ForkLock.RUnlock()
} else if e != nil {
return nil, nil, NewSyscallError("pipe2", e)
} return newFile(uintptr(p[0]), "|0", kindPipe), newFile(uintptr(p[1]), "|1", kindPipe), nil
}

读方法

// Reader is the interface that wraps the basic Read method.
//
// Read reads up to len(p) bytes into p. It returns the number of bytes
// read (0 <= n <= len(p)) and any error encountered. Even if Read
// returns n < len(p), it may use all of p as scratch space during the call.
// If some data is available but not len(p) bytes, Read conventionally
// returns what is available instead of waiting for more.
//
// When Read encounters an error or end-of-file condition after
// successfully reading n > 0 bytes, it returns the number of
// bytes read. It may return the (non-nil) error from the same call
// or return the error (and n == 0) from a subsequent call.
// An instance of this general case is that a Reader returning
// a non-zero number of bytes at the end of the input stream may
// return either err == EOF or err == nil. The next Read should
// return 0, EOF.
//
// Callers should always process the n > 0 bytes returned before
// considering the error err. Doing so correctly handles I/O errors
// that happen after reading some bytes and also both of the
// allowed EOF behaviors.
//
// Implementations of Read are discouraged from returning a
// zero byte count with a nil error, except when len(p) == 0.
// Callers should treat a return of 0 and nil as indicating that
// nothing happened; in particular it does not indicate EOF.
//
// Implementations must not retain p.
type Reader interface {
Read(p []byte) (n int, err error)
}

实现读接口的对象,有一个Read方法,可以将字节读到字节数组中; 与stdin设备对应,从该设备读取字节到内存。

exec.Cmd这是一个外部命令,它可以是一个静止的命令,或者一个正在运行的命令;提供启动/停止/管道/输入输入定向支持;其中管道返回一个读文件句柄和一个写文件句柄

exec.Cmd输入输出重定向

func runCmdWithPipe() {
fmt.Println("Run command `ps -ef | grep apipe`: ")
cmd1 := exec.Command("ps", "-ef")
cmd2 := exec.Command("grep", "apipe")
var outputBuf1 bytes.Buffer
cmd1.Stdout = &outputBuf1
if err := cmd1.Start(); err != nil {
fmt.Printf("Error: The first command can not be startup %s\n", err)
return
}
if err := cmd1.Wait(); err != nil {
fmt.Printf("Error: Couldn't wait for the first command: %s\n", err)
return
}
cmd2.Stdin = &outputBuf1
var outputBuf2 bytes.Buffer
cmd2.Stdout = &outputBuf2
if err := cmd2.Start(); err != nil {
fmt.Printf("Error: The second command can not be startup: %s\n", err)
return
}
if err := cmd2.Wait(); err != nil {
fmt.Printf("Error: Couldn't wait for the second command: %s\n", err)
return
}
fmt.Printf("%s\n", outputBuf2.Bytes())
}

go channel 概述 - 管道的更多相关文章

  1. Channel概述

    前言 前两篇文章介绍了NIO核心部分部分之一的缓冲区的相关内容,接下来我们继续学习NIO中另一个重要的核心部分--Channel(通道). 在学习这篇文章之前,先做下简单的说明,本文是一篇关于通道的概 ...

  2. go channel 概述

    精髓 将资源读进内存-->共享内存,一个个进程/线程进行处理,这是常见模式.go channel 是一种直接在进程/线程之间传递资源的方式,即以通信来共享内存.这便是go的精髓. 扩展-一些名词 ...

  3. [Go] 利用channel形成管道沟通循环内外

    这个要解决的问题是,比如如果有一个大循环,取自一个大的文件,要进行逻辑处理,那么这个逻辑的代码要放在循环每一行的循环体里面,这样有可能会出现一个for循环的逻辑嵌套,一层又一层,类似俄罗斯套娃.如果放 ...

  4. GO 总章

    GO 学习资源 go 代理 GO 语言结构 GO 数字运算 GO 时间处理 GO 定时器 GO 异常处理 go recover让崩溃的程序继续执行 GO Exit Fatal panic GO 通过进 ...

  5. GoLang的概述

    GoLang的概述 1.什么是程序 完成某个功能的指令的集合 2.Go语言的诞生小故事 2.1. 开发团队-三个大牛 2.2.Google创造Golang的原因 2.3.Golang 的发展历程 20 ...

  6. 第四章:管道与FIFO

    4.1:概述 管道是最初的Unix IPC形式,可追溯到1973年的Unix第三版.尽管对于许多操作来说很有用,但它们的根本局限在于没有名字,从而只能由亲缘关系的进程使用.这一点随FIFO的加入得改正 ...

  7. 第4章 管道与FIFO

    4.1 概述 管道只在亲缘进程间使用,FIFO在任意进程间使用 4.2 管道 #include <unistd.h> ]) fd[0]用来读管道,fd[1]用来写管道 1)命令who |  ...

  8. Go语言学习笔记(七)杀手锏 Goroutine + Channel

    加 Golang学习 QQ群共同学习进步成家立业工作 ^-^ 群号:96933959 Goroutine Go语言的主要的功能在于令人简易使用的并行设计,这个方法叫做Goroutine,通过Gorou ...

  9. go语言之行--golang核武器goroutine调度原理、channel详解

    一.goroutine简介 goroutine是go语言中最为NB的设计,也是其魅力所在,goroutine的本质是协程,是实现并行计算的核心.goroutine使用方式非常的简单,只需使用go关键字 ...

随机推荐

  1. Android现有工程使用Compose

    Android现有工程使用Compose 看了Compose的示例工程后,我们也想使用Compose.基于目前情况,在现有工程基础上添加Compose功能. 引入Compose 首先我们安装 Andr ...

  2. Docker多机网络

    前言 前面的文章主要聚焦于单机网络上,对于生产环境而言,单机环境不满足高可用的特点,所以是不具备上生产的条件,因此在开始Docker Swarm篇的时候我们先来聊聊多机网络之间Docker的通信如何做 ...

  3. 深入理解Spring IOC容器

    本文将从纯xml模式.xml和注解结合.纯注解的方式讲解Spring IOC容器的配置和相关应用. 纯XML模式 实例化Bean的三种方式: 使用无参构造函数 默认情况下,会使用反射调用无参构造函数来 ...

  4. 手把手教你基于Netty实现一个基础的RPC框架(通俗易懂)

    阅读这篇文章之前,建议先阅读和这篇文章关联的内容. [1]详细剖析分布式微服务架构下网络通信的底层实现原理(图解) [2][年薪60W的技巧]工作了5年,你真的理解Netty以及为什么要用吗?(深度干 ...

  5. Python-Unittest多线程生成报告

    前言 selenium多线程跑用例,这个前面一篇已经解决了,如何生成一个测试报告这个是难点,刚好在github上有个大神分享了BeautifulReport,完美的结合起来,就能生成报告了. 环境必备 ...

  6. 高德地图 JS API (jsp + miniui(子页面数据返回父页面并设值) + 单个点标记 + 点标记经纬度 + 回显 + 限制地图显示范围+搜索)

    -*-  父页面js function mapFocus(){ //console.log("-*-"); var longitude = mini.get("jd&qu ...

  7. Python 爬取 书籍

    ... import requests from bs4 import BeautifulSoup def gethtml(url,h): r = requests.get(url,headers=h ...

  8. opa gatekeeper笔记:AdmissionReview input.request请求对象结构

    官方:https://v1-17.docs.kubernetes.io/zh/docs/reference/access-authn-authz/extensible-admission-contro ...

  9. tomcat更改端口号and设置cmd别名

    1.修改端口号 打开tomcat的conf\server.xml 这个是项目访问的端口号了 这个也要注意更改一下,避免冲突 2.接下来要设置cmd别名 用文本编辑器打开bin\catalina.bat ...

  10. YAPI接口自动鉴权功能部署详解

    安装准备 以下操作,默认要求自己部署过yapi,最好是部署过yapi二次开发环境. 无论是选择在线安装或者是本地安装,都需要安装client工具. 1.yapi-cli:npm install yap ...