5

I have the following bash script bash_loop.shthat prints 1 to 10 and sleep 3 s in between.

#!/bin/bash
# Basic while loop
counter=1
while [ $counter -le 10 ]
do
    echo $counter
    ((counter++))
   sleep 3 
done
echo All done

Right now, I have my go code as follow:

burstingScript := "bash_loop.sh"
cmd := exec.Command("/bin/sh", burstingScript)

var out bytes.Buffer
cmd.Stdout = &out
if err := cmd.Run(); err != nil {
    fmt.Println("An error has occurred..")
    log.Fatal(err)
}
fmt.Println(out.String())

However, this only print out everything after the cmd finish running after 30s, instead of print things as they become available.

So my question is that if I can print each number while the bash script is still running instead of printing everything all together after the bash script finishes execution.

P.S.1: In the real use case, I have to process the output of bash script in realtime, instead of simply printing things out to os.Stdout, so I am wondering if there's any command poll() interface or equivalence in go.

P.S.2:In the real use case, I want to detach from the child process as soon as I find interesting message. For example, after I read 3, I want my function return 3 immediately and not wait for the rest of output anymore, though I still want the child process (the bash script) itself to be up and running.

P.S.3: In python, I would do something like this

cmd = "./bash_loop.sh"
p = subprocess.Popen(cmd, shell=True, stderr=subprocess.PIPE, stdout=subprocess.PIPE)

result = {}
while True:
    out = p.stdout.readline()
    if out == '' and p.poll() != None:
        break
    if out != '':
        #process(out)
        sys.stdout.write(out)
        sys.stdout.flush()

P.S.4: Now I have evolved my go snippet to follow. Will the child become a zombie if I return before the command finishes running?

burstingScript := path.Join(rootDir, "bash_loop.sh")
cmd := exec.Command("/bin/sh", burstingScript)

stdout, _ := cmd.StdoutPipe()
cmd.Start()
scanner := bufio.NewScanner(stdout)
for scanner.Scan() {
    m := scanner.Text()
    fmt.Println(m)
    //if m=="3" {
    //  return process(m)
    //}
}
cmd.Wait()
JimB
  • 104,193
  • 13
  • 262
  • 255
cookieisaac
  • 1,357
  • 5
  • 18
  • 35
  • 3
    Related / possible duplicate of [Streaming commands output progress](http://stackoverflow.com/questions/30725751/streaming-commands-output-progress). – icza Aug 26 '16 at 19:37
  • @icza great pointer. I added P.S.2, where instead of waiting for the entire Cmd to finish, I want my function return the interesting output as soon as it's read without killing the running process. – cookieisaac Aug 26 '16 at 19:59
  • re P.S.4, yes, you need to call wait, so don't return before calling cmd.Wait(). – JimB Aug 26 '16 at 20:48

1 Answers1

3

Set the command output to stdout:

cmd.Stdout = os.Stdout
JimB
  • 104,193
  • 13
  • 262
  • 255
  • 1
    I actually want to sniff the output of the bash script and do some processing before print it out to `os.Stdout`. – cookieisaac Aug 26 '16 at 19:35
  • 2
    Next time be sure to mention every thing you want to do in your question to avoid asking these: http://xyproblem.info/ – Anfernee Aug 26 '16 at 19:36
  • @cookieisaac: so why not use [StdOutPipe](https://golang.org/pkg/os/exec/#Cmd.StdoutPipe)? – JimB Aug 26 '16 at 19:59