Os/exec elegant, loop compatible stdin and stdout input/output

huangapple go评论93阅读模式
英文:

Os/exec elegant, loop compatible stdin and stdout input/output

问题

示例脚本只是对"wc -m"命令的包装,用于简单的符号计数。我试图只使用"teststrings"切片元素来提供输入,并在输出监听协程中接收每个字符串的符号数量。我正在寻找一种让"wc"一直监听输入的方法。我注意到当我增加睡眠时间到

time.Sleep(6000 * time.Nanosecond)

wc不再等待输入。

package main

import (
	"bytes"
	"fmt"
	"os/exec"
	"time"
)

func main() {
	BashCommand := exec.Command("wc", "-m")
	InputBytes := &bytes.Buffer{}
	OutputBytes := &bytes.Buffer{}
	BashCommand.Stdin = InputBytes
	BashCommand.Stdout = OutputBytes
	e := BashCommand.Start()
	time.Sleep(1 * time.Nanosecond)
	_, _ = InputBytes.Write([]byte("13symbolsting"))
	if e != nil {
		fmt.Println(e)
	}
	fmt.Println("after run")

	teststrings := []string{
		"one",
		"twoo",
		"threeeee",
	}
	for _, s := range teststrings {
		_, _ = InputBytes.Write([]byte(s))

	}

	//result printer
	go func() {
		for {
			line, _ := OutputBytes.ReadString('\n')
			if line != "" {
				fmt.Println(line)
			}
		}
	}()
	var input string
	fmt.Scanln(&input) //dont exit until keypress

}
英文:

Example script is just wrapper to "wc -m" command, simple symbol counter.
I trying just feed input with "teststrings" slice elements. And receive number of symbol of each string at output listener goroutine. Looking for a way to make "wc" listen forever for input at all. I'v notice when i increase sleep to

time.Sleep(6000 * time.Nanosecond)

wc don't wait for input.

package main

import (
	"bytes"
	"fmt"
	"os/exec"
	"time"
)

func main() {
	BashCommand := exec.Command("wc", "-m")
	InputBytes := &bytes.Buffer{}
	OutputBytes := &bytes.Buffer{}
	BashCommand.Stdin = InputBytes
	BashCommand.Stdout = OutputBytes
	e := BashCommand.Start()
	time.Sleep(1 * time.Nanosecond)
	_, _ = InputBytes.Write([]byte("13symbolsting"))
	if e != nil {
		fmt.Println(e)
	}
	fmt.Println("after run")

	teststrings := []string{
		"one",
		"twoo",
		"threeeee",
	}
	for _, s := range teststrings {
		_, _ = InputBytes.Write([]byte(s))

	}

	//result printer
	go func() {
		for {
			line, _ := OutputBytes.ReadString('\n')
			if line != "" {
				fmt.Println(line)
			}
		}
	}()
	var input string
	fmt.Scanln(&input) //dont exit until keypress

}

答案1

得分: 3

如果将睡眠时间增加到一个较大的值,那么通过将InputBytes传输到进程的命令启动的goroutine会在数据被写入InputBytes之前运行。该goroutine关闭了与子进程的管道,并在没有读取任何数据的情况下退出。

使用管道而不是bytes.Buffer:

c := exec.Command("wc", "-m")
w, _ := c.StdinPipe()
r, _ := c.StdoutPipe()
if err := c.Start(); err != nil {
    log.Fatal(err)
}

w.Write([]byte("13symbolsting"))
teststrings := []string{
    "one",
    "twoo",
    "threeeee",
}
for _, s := range teststrings {
    w.Write([]byte(s))
}

w.Close() // 关闭管道以表示输入完成。

var wg sync.WaitGroup
wg.Add(1)

go func() {
    s := bufio.NewScanner(r)
    for s.Scan() {
        fmt.Println(s.Text())
    }
    wg.Done()
}()

wg.Wait()

另一种选择是在启动命令之前将数据写入bytes.Buffer,并在读取输出之前等待命令完成:

c := exec.Command("wc", "-m")
var w, r bytes.Buffer
c.Stdin = &w
c.Stdout = &r

// 在启动命令之前写入数据。

w.Write([]byte("13symbolsting"))
teststrings := []string{
    "one",
    "twoo",
    "threeeee",
}
for _, s := range teststrings {
    w.Write([]byte(s))
}

if err := c.Start(); err != nil {
    log.Fatal(err)
}

// 在读取数据之前等待命令完成。

if err := c.Wait(); err != nil {
    log.Fatal(err)
}

s := bufio.NewScanner(&r)
for s.Scan() {
    fmt.Println(s.Text())
}
英文:

If you increase the sleep to a large value, the goroutine started by the command to pump InputBytes to the process runs before data is written to InputBytes. The goroutine closes the pipe to the child and exits without having read any data.

Use pipes instead of bytes.Buffer:

c := exec.Command("wc", "-m")
w, _ := c.StdinPipe()
r, _ := c.StdoutPipe()
if err := c.Start(); err != nil {
	log.Fatal(err)
}

w.Write([]byte("13symbolsting"))
teststrings := []string{
	"one",
	"twoo",
	"threeeee",
}
for _, s := range teststrings {
	w.Write([]byte(s))

}
w.Close() // Close pipe to indicate input is done.

var wg sync.WaitGroup
wg.Add(1)

go func() {
	s := bufio.NewScanner(r)
	for s.Scan() {
		fmt.Println(s.Text())
	}
	wg.Done()
}()

wg.Wait()

Another option is to write to the bytes.Buffer before starting the command and wait for command to complete before reading the output:

c := exec.Command("wc", "-m")
var w, r bytes.Buffer
c.Stdin = &w
c.Stdout = &r

// Write data before starting command.

w.Write([]byte("13symbolsting"))
teststrings := []string{
	"one",
	"twoo",
	"threeeee",
}
for _, s := range teststrings {
	w.Write([]byte(s))

}

if err := c.Start(); err != nil {
	log.Fatal(err)
}

// Wait for command to complete before reading data.

if err := c.Wait(); err != nil {
	log.Fatal(err)
}

s := bufio.NewScanner(&r)
for s.Scan() {
	fmt.Println(s.Text())
}

huangapple
  • 本文由 发表于 2016年4月15日 21:56:07
  • 转载请务必保留本文链接:https://go.coder-hub.com/36649162.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定