Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Golang equivalent of creating a subprocess in Python

Tags:

python

go

I am attempting to convert a Python script to Golang, just really to see performance differences and to help me learn Golang more.

In Python, I have 2 scripts. One is a script that runs an infinite loop and sleeps for a minute before running again. The code checks an endpoint on my server and reads the output, then determines if it needs to do anything. If it does, it handles the output and starts a new subprocess. The subprocess is another Python script that does a lot of calculations and creates hundreds of threads. There can be multiple subprocesses running at any given time and they're all different tasks for different users.

I've got my Golang code reading from the API, and it's ready to start a new subprocess. But I'm not really sure how I go about it.

I know when I've created the new subprocess (or whatever it is that is the Go equivalent) I can create a bunch of Go routines, but really I'm just stuck on the "subprocess" bit.

I have tried using Go routines as a substitute for the subprocess but I don't think this is the way to go?

As requested for visualisation purposes, I have added an example of the code.

api.py:

while True:
    someparameter = 'randomIDfromdatabase'
    subprocess.Popen(["python3", "mycode.py", someparameter])
    time.sleep(60)

mycode.py

parameter = sys.argv[1]
for i in range(0, 100):
    thread.append(MyClass(parameter))
    thread.start()

I basically need the Golang equivalent of "subprocess.Popen".

like image 257
fatkat Avatar asked Jan 19 '19 16:01

fatkat


People also ask

How do you create a subprocess in Python?

To start a new process, or in other words, a new subprocess in Python, you need to use the Popen function call. It is possible to pass two parameters in the function call. The first parameter is the program you want to start, and the second is the file argument.

Is subprocess built in Python?

The subprocess module present in Python(both 2. x and 3. x) is used to run new applications or programs through Python code by creating new processes.

What is Popen in Python?

Python method popen() opens a pipe to or from command. The return value is an open file object connected to the pipe, which can be read or written depending on whether mode is 'r' (default) or 'w'. The bufsize argument has the same meaning as in open() function.

Are Subprocesses parallel in Python?

All sub processes are run in parallel. (To avoid this one has to wait explicitly for their completion.) They even can write into the log file at the same time, thus garbling the output. To avoid this you should let each process write into a different logfile and collect all outputs when all processes are finished.


1 Answers

You can use the Go os/exec package for subprocess-like behavior. For example, here's a trivial program that runs the date program in a subprocess and reports its stdout:

package main

import (
    "fmt"
    "log"
    "os/exec"
)

func main() {
    out, err := exec.Command("date").Output()
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("The date is %s\n", out)
}

A more interesting example from gobyexample that shows how to interact with the stdio/stdout of launched processes:

package main

import "fmt"
import "io/ioutil"
import "os/exec"

func main() {

    // We'll start with a simple command that takes no
    // arguments or input and just prints something to
    // stdout. The `exec.Command` helper creates an object
    // to represent this external process.
    dateCmd := exec.Command("date")

    // `.Output` is another helper that handles the common
    // case of running a command, waiting for it to finish,
    // and collecting its output. If there were no errors,
    // `dateOut` will hold bytes with the date info.
    dateOut, err := dateCmd.Output()
    if err != nil {
        panic(err)
    }
    fmt.Println("> date")
    fmt.Println(string(dateOut))

    // Next we'll look at a slightly more involved case
    // where we pipe data to the external process on its
    // `stdin` and collect the results from its `stdout`.
    grepCmd := exec.Command("grep", "hello")

    // Here we explicitly grab input/output pipes, start
    // the process, write some input to it, read the
    // resulting output, and finally wait for the process
    // to exit.
    grepIn, _ := grepCmd.StdinPipe()
    grepOut, _ := grepCmd.StdoutPipe()
    grepCmd.Start()
    grepIn.Write([]byte("hello grep\ngoodbye grep"))
    grepIn.Close()
    grepBytes, _ := ioutil.ReadAll(grepOut)
    grepCmd.Wait()

    // We ommited error checks in the above example, but
    // you could use the usual `if err != nil` pattern for
    // all of them. We also only collect the `StdoutPipe`
    // results, but you could collect the `StderrPipe` in
    // exactly the same way.
    fmt.Println("> grep hello")
    fmt.Println(string(grepBytes))

    // Note that when spawning commands we need to
    // provide an explicitly delineated command and
    // argument array, vs. being able to just pass in one
    // command-line string. If you want to spawn a full
    // command with a string, you can use `bash`'s `-c`
    // option:
    lsCmd := exec.Command("bash", "-c", "ls -a -l -h")
    lsOut, err := lsCmd.Output()
    if err != nil {
        panic(err)
    }
    fmt.Println("> ls -a -l -h")
    fmt.Println(string(lsOut))
}

Note that goroutines have little to do with subprocesses. Goroutines are a way to do multiple things concurrently in a single Go process. That said, when interacting with subprocesses, goroutines often come in handy because they help waiting for subprocesses to finish while also doing other things in the launching (main) program. But the details of this are very specific to your application.

like image 187
Eli Bendersky Avatar answered Oct 18 '22 11:10

Eli Bendersky