mattnさんにシンプルなコードを教えて頂いたので、コメント欄をご欄ください
先日、とあるイベントに参加したときにGo言語から外部プロセスを起動し、起動したプロセスを操作する方法が分からず無駄な時間を過ごしてしまいました。
次回も同じ轍を踏まないようにコマンドを起動してチョメチョメするライブラリを作成しました。
参考サイト
ライブラリの作成にあたり、PythonMatrixJpさんのプログラムを参考に作成しました。
どんなライブラリなのか?
外部プロセスを操作するライブラリ exec.Cmd を拡張したライブラリです。名前をpfprocといいます。
- exec.Commandと同じ引数でインスタを作成します。
- Startメソッドで外部プロセスを起動します。
- 外部プロセスから標準(エラー)出力をターミナルに表示します。
- Inputメソッドを使って外部プロセスにデータを送ります。(標準入力)
- 処理が完了するまで待つには WaitProcメソッドを使います。
pfprocの利用サンプル
package main
import (
"fmt"
"os"
"pfproc"
"strings"
)
func exitOnErr(msg string, err error) {
if err != nil {
fmt.Println(msg, err)
os.Exit(1)
}
}
func main() {
proc, err := pfproc.NewPfProc("bash")
exitOnErr("create process", err)
proc.StdoutFunc = func(text string) {
// ファイル名がmain.goのときは hehehe に置き換える
if strings.Index(text, "main.go") != -1 {
text = "hehehe"
}
fmt.Println(text)
}
proc.Start()
proc.Input("pwd")
proc.Input("ls")
proc.Input("exit")
proc.WaitProc()
}
pfprocのソースコード
package pfproc
import (
"bufio"
"fmt"
"io"
"os/exec"
)
type PfProc struct {
*exec.Cmd
stdin chan string
stdout chan string
stderr chan string
quit chan error
StdoutFunc func(text string)
StderrFunc func(text string)
}
func NewPfProc(name string, args ...string) (*PfProc, error) {
self := &PfProc{
exec.Command(name, args...),
make(chan string),
make(chan string),
make(chan string),
make(chan error),
func(text string) { fmt.Println(text) },
func(text string) { fmt.Println(text) },
}
in, err := self.StdinPipe()
if err != nil {
return nil, err
}
go inprocess(in, self.stdin)
out, _ := self.StdoutPipe()
if err != nil {
return nil, err
}
go outprocess(out, self.stdout)
ste, _ := self.StderrPipe()
if err != nil {
return nil, err
}
go outprocess(ste, self.stderr)
return self, nil
}
func (self *PfProc) Input(text string) {
self.stdin <- text
}
func (self *PfProc) WaitProc() {
go func() {
self.quit <- self.Wait()
close(self.stdin)
close(self.stdout)
close(self.stderr)
}()
for {
select {
case line, ok := <-self.stdout:
if ok {
self.StdoutFunc(line)
}
case line, ok := <-self.stderr:
if ok {
self.StderrFunc(line)
}
case err := <-self.quit:
if err != nil {
fmt.Println(err)
}
return
}
}
}
func inprocess(in io.WriteCloser, std chan string) {
for {
line, ok := <-std
if ok {
in.Write([]byte(line + "\n"))
}
}
}
func outprocess(out io.ReadCloser, std chan string) {
reader := bufio.NewReader(out)
for {
line, _, err := reader.ReadLine()
if err != nil {
return
}
std <- string(line)
}
}