11
11

More than 5 years have passed since last update.

Goで入力と出力をくっつける練習

Posted at

具体的には、標準入力を取って、外部コマンド(今回はRuby、まあなんでもいい。docker execとかにつないだら夢がありそう)をそのまま流し込んで、出力を一行ずつ取る。

bufio.Scanner が、node.jsのcarrierみたいな感じで一行ずつ取るのに向いていた。あと、nodeの感覚でパイプをつなげられるgo-pipeを使った。これは便利だった。

  • スキャンし続けるgoroutineを回す
  • コマンドの終了を監視するgoroutineを回す
  • 標準入力を取るメインループを回す

と言う感じでうまく行ったっぽい。

pipe_spike.go
package main

import (
    "bufio"
    "fmt"
    "os"
    "os/exec"

    "gopkg.in/pipe.v2"
)

func runScanLoop(scanner *bufio.Scanner) {
    for scanner.Scan() {
        if text := scanner.Text(); text != "\"\"" {
            fmt.Printf("%v\n", text)
        }
    }

    if err := scanner.Err(); err != nil {
        fmt.Fprintln(os.Stderr, "reading command input:", err)
    }
}

func runWatchCmdLoop(cmd *exec.Cmd, term chan bool) {
    cmd.Wait()
    fmt.Println("Process exit detected")
    term <- true
}

func main() {
    ruby := "puts 'Start'; while str = gets; exit if str.start_with?('quit'); p str.chomp.reverse; end"
    cmd := exec.Command("ruby", "-e", ruby)
    stdout, err := cmd.StdoutPipe()
    stdin, err2 := cmd.StdinPipe()
    if err != nil || err2 != nil {
        panic(err)
    }

    if err := cmd.Start(); err != nil {
        panic(err)
    }

    // Invoke first input
    fmt.Fprint(stdin, "\n")
    scanner := bufio.NewScanner(stdout)
    go runScanLoop(scanner)

    term := make(chan bool, 1)
    go runWatchCmdLoop(cmd, term)

    go func() {
        <-term
        fmt.Println("Exited...")
        os.Exit(0)
    }()

    for {
        p := pipe.Line(
            pipe.Read(os.Stdin),
            pipe.Write(stdin),
        )
        if err := pipe.Run(p); err != nil {
            panic(err)
        }
        if s, err := pipe.Output(p); err != nil {
            fmt.Println(s)
            panic(err)
        }
    }
}

gospike.gif

なお、これがFukuoka.rbでの成果物であることは内緒である。

11
11
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
11
11