【发布时间】:2015-01-16 23:32:47
【问题描述】:
所以我想在 Go 中创建一个命令 shell。我的目标是在我正在考虑编写的便携式系统工具中使用它的功能。我认为一个很好的起点是可以演示运行系统命令的基础知识并允许用户与系统 shell 交互。
【问题讨论】:
标签: bash shell go interactive
所以我想在 Go 中创建一个命令 shell。我的目标是在我正在考虑编写的便携式系统工具中使用它的功能。我认为一个很好的起点是可以演示运行系统命令的基础知识并允许用户与系统 shell 交互。
【问题讨论】:
标签: bash shell go interactive
我有这样的解决方案!实际上,我已将其发布在 Gist 中 gist.github.com/lee8oi/a8a90f559fe48355f800
这是一个很好的小演示,它使用 Go 的 os/exec 包将输入/输出/错误直接通过管道传递到命令实例。当“bash”运行时,您将坐在一个非常简单的提示符下。您可以像使用 bash 一样与它进行交互(但仍然非常简化)。当您运行常规命令时,将显示输出,然后命令将退出(如“ping”)。我认为这将成为修补与 Go 中的系统 shell 交互方式的一个很好的起点。
package main
import (
"bufio"
"fmt"
"io"
"log"
"os"
"os/exec"
"sync"
)
func getPipes(c *exec.Cmd) (inp io.Writer, outp, errp io.Reader) {
var err error
if inp, err = c.StdinPipe(); err != nil {
log.Fatal(err)
}
if outp, err = c.StdoutPipe(); err != nil {
log.Fatal(err)
}
if errp, err = c.StderrPipe(); err != nil {
log.Fatal(err)
}
return
}
func pipe(wg *sync.WaitGroup, inp io.Reader, outp io.Writer) {
if wg != nil {
defer wg.Done()
}
r := bufio.NewReader(inp)
for {
c, err := r.ReadByte()
if err != nil {
return
}
fmt.Fprintf(outp, "%s", string(c))
}
}
func Command(args ...string) {
var cmd *exec.Cmd
var wg sync.WaitGroup
if len(args) > 1 {
cmd = exec.Command(args[0], args[1:]...)
} else {
cmd = exec.Command(args[0])
}
inp, outp, errp := getPipes(cmd)
wg.Add(1)
go pipe(&wg, errp, os.Stderr)
wg.Add(1)
go pipe(&wg, outp, os.Stdout)
go pipe(nil, os.Stdin, inp)
if err := cmd.Start(); err != nil {
log.Fatal("start ", err)
}
wg.Wait()
}
func main() {
Command("bash")
Command("date")
Command("echo", "some text")
Command("ping", "-c 3", "www.google.com")
}
【讨论】:
interface {} 的使用很奇怪,只需使用io.Reader 和io.Writer。应该检查来自StdinPipe* 的错误,也应该检查来自Wait 的错误。 runPipe` 过于复杂 - 您可以将其全部替换为 io.Copy。