9

I want to execute another go program from inside another go program and give it args, something like this:

package main
func main() {
  //here I want to call a specific go file, e.g. test.go with args
}

test.go

package main
func main(x int) {
  //do stuff with x
}

I don't want to sent an int as arg, but something like http.ResponseWriter

A solution I thought off, but it wouldn't be really good:

  1. Use gob to convert the http.ResponseWriter into a string
  2. Read in a line from test.go
  3. Sent the string to test.go

Thanks for any answers :D

Luca S.
  • 141
  • 1
  • 1
  • 6
  • Possible duplicate of [How to use custom packages in golang?](http://stackoverflow.com/questions/15049903/how-to-use-custom-packages-in-golang) – 0x0 May 09 '16 at 19:21
  • 1
    It's not a duplicate, the question you mention wants to import a go source file, which is already known before the program is run, while I want to load a go source file/compiled file and only get the filename during runtime. – Luca S. May 09 '16 at 19:24

3 Answers3

17

There are many ways to do this Interoperability :
1- I recommend to use standard golang package (Lib) calling, instead of Interoperability, if you have source files of both sides.

2- using "os/exec": if you don't have source, and you have only binary, or you may pass args through files or text args:

you may pass args like this:

package main

import (
    "fmt"
    "os"
)

func main() {

    fmt.Println(os.Args[0]) // fileNameAndPath
}

or using "flag" std lib:

// flags.exe -h
package main

import (
    "flag"
    "fmt"
)

func main() {
    namePtr := flag.String("name", "AR", "name")
    agePtr := flag.Int("age", 3700, "age")
    flag.Parse()
    fmt.Println(*namePtr, *agePtr) //AR 3700
}

/*
Usage of flags.exe:
  -age int
        age (default 3700)
  -name string
        name (default "AR")
*/

which will provides -h for help.
and you can call another binary program or golang compiler itself like this:

package main

import (
    "log"
    "os/exec"
)

func main() {
    cmnd := exec.Command("main.exe", "arg")
    //cmnd.Run() // and wait
    cmnd.Start()
    log.Println("log")
}

3- another way is calling an external program by using stdin /stdout.
in this way you can send binary data over stdin/out:
Here file "a" calls binary file "b" and sends and receives through stdin/stdout:
this is my conversion from :
http://erlang.org/doc/tutorial/c_port.html
(you may use os named pipe)
file a:

// a
package main

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

var cout chan []byte = make(chan []byte)
var cin chan []byte = make(chan []byte)
var exit chan bool = make(chan bool)

func Foo(x byte) byte { return call_port([]byte{1, x}) }
func Bar(y byte) byte { return call_port([]byte{2, y}) }
func Exit() byte      { return call_port([]byte{0, 0}) }
func call_port(s []byte) byte {
    cout <- s
    s = <-cin
    return s[1]
}

func start() {
    fmt.Println("start")
    cmd := exec.Command("../b/b")
    stdin, err := cmd.StdinPipe()
    if err != nil {
        log.Fatal(err)
    }
    stdout, err2 := cmd.StdoutPipe()
    if err2 != nil {
        log.Fatal(err2)
    }
    if err := cmd.Start(); err != nil {
        log.Fatal(err)
    }
    defer stdin.Close()
    defer stdout.Close()
    for {
        select {
        case s := <-cout:
            stdin.Write(s)
            buf := make([]byte, 2)
            runtime.Gosched()
            time.Sleep(100 * time.Millisecond)
            stdout.Read(buf)
            cin <- buf
        case b := <-exit:
            if b {
                fmt.Printf("Exit")
                return //os.Exit(0)
            }
        }
    }
}
func main() {
    go start()
    runtime.Gosched()
    fmt.Println("30+1=", Foo(30)) //30+1= 31
    fmt.Println("2*40=", Bar(40)) //2*40= 80
    Exit()
    exit <- true
}

file b:

// b
package main

import (
    "log"
    "os"
)

func foo(x byte) byte { return x + 1 }
func bar(y byte) byte { return y * 2 }

func ReadByte() byte {
    b1 := make([]byte, 1)
    for {
        n, _ := os.Stdin.Read(b1)
        if n == 1 {
            return b1[0]
        }
    }
}
func WriteByte(b byte) {
    b1 := []byte{b}
    for {
        n, _ := os.Stdout.Write(b1)
        if n == 1 {
            return
        }
    }
}
func main() {
    var res byte
    for {
        fn := ReadByte()
        log.Println("fn=", fn)
        arg := ReadByte()
        log.Println("arg=", arg)
        if fn == 1 {
            res = foo(arg)
        } else if fn == 2 {
            res = bar(arg)
        } else if fn == 0 {
            return //exit
        } else {
            res = fn //echo
        }
        WriteByte(1)
        WriteByte(res)
    }
}

4 - another way is using "net/rpc", this is best way for calling another function from another program.
sample:

// rpc
package main

import (
    "fmt"
    "net"
    "net/rpc"
    "runtime"
    "sync"
)

var wg sync.WaitGroup

type Server struct{}

func (this *Server) Add(u [2]int64, reply *int64) error {
    *reply = u[0] + u[1]
    return nil
}

func server() {
    fmt.Println("server: Hi")
    rpc.Register(new(Server))
    ln, err := net.Listen("tcp", "127.0.0.1:12345")
    if err != nil {
        fmt.Println(err)
        return
    }
    for {
        c, err := ln.Accept()
        if err != nil {
            continue
        }
        go rpc.ServeConn(c)
    }
}

func client() {
    wg.Add(1)
    c, err := rpc.Dial("tcp", "127.0.0.1:12345")
    if err != nil {
        fmt.Println(err)
        return
    }
    fmt.Println("Connected...")
    var result int64
    err = c.Call("Server.Add", [2]int64{10, 20}, &result)
    if err != nil {
        fmt.Println(err)
    } else {
        fmt.Println("Server.Add(10,20) =", result)
    }
    wg.Done()
}

func main() {
    go server()
    runtime.Gosched()
    go client()
    runtime.Gosched()
    wg.Wait()
    fmt.Println("Bye")
}

/*output:
server: Hi
Connected...
Server.Add(10,20) = 30
Bye
*/
  • Can exec.Command() really execute a program with arguments like an http.ResponseWriter? Also I only get the file name on runtime, which is why the first solution wouldn't work if I understood correctly what you mean. – Luca S. May 09 '16 at 18:39
  • It will be better if you provide some sample code for your http.ResponseWriter , I think the 3rd way is better suits for this, I will add sample ASAP –  May 09 '16 at 18:46
  • 1
    @LucaS. You can't serialize an http.ResponseWriter, since it's an interface, the package implementation contains state, and it handles a network connection. – JimB May 09 '16 at 18:47
  • Edit: I'm gonna publish the code I use, I made a little server and I want to use it to server dynamic content programmed in go – Luca S. May 09 '16 at 18:59
  • do you have source of bothe sides? –  May 09 '16 at 19:19
  • I haven't programmed the dynamic page yet, because I don't know how to get that ResponseWriter and Request to the page and without these the page would be worthless. – Luca S. May 09 '16 at 19:30
  • 1
    is there any reason you are using two separate programs –  May 09 '16 at 19:48
  • For some pages that might work, but when I get many dynamic pages using separate programs will be clearer. – Luca S. May 09 '16 at 19:58
  • 1
    So you don’t need to send http.ResponseWriter. The best way for communicating between two programs inside one computer is using pipes or even TCP it is very simple this way. But in case of calling another function from another program “net/rpc” is the suitable way, I think. –  May 09 '16 at 20:04
  • In case 3, program `b` is calling `os.Stdout.Write()` instead of `fmt.Print()`. Would it still be possible for `a` to read if `b` were using `fmt`? In my case, I don't have the source code for `b`. – Carcamano Oct 05 '22 at 14:59
5

Using os/exec, you can call the go compiler like so:

output, err := exec.Command("go", "run", "/file/path/to/open").Output() 
if err == nil {
    w.Write(output) // write the output with ResponseWriter
}

Go doesn't pass parameters to programs through main, eg func main(x int). The way to do it in Go is with os.Args.

It seems to me that you're trying to use PHP concepts in Go, which will not end well. Instead you should be using templates and static files to achieve a dynamic website. Check out the package text/template.

  • Well yeah I kinda try to use PHP concepts in Go, but text/template doesn't fit my criteria, e.g. I need to redirect based on files and that stuff – Luca S. May 09 '16 at 19:36
1

Create an executabl file for the package you want to run from another file. Provide relative path for the file you want to run inside exec.Command. For example:

File A

package a

import "fmt"

func main(){
    for i:=0;i<10;i++{
         fmt.Println(fmt.Sprintf("Process: %d", i))
    }
}

Create a binary/executable for above file. The file will be inside the package so walk to the package and run the executable file.

go build github.com/user/a/a.go // or whatever be your GOPATH for executables to create a binary file

Below is the case of binary file inside linux systems.

File B

package b

import (
    "fmt"
    "os/exec"
)    

func main(){
    // come out of package b and then go inside package a to run the executable file as
    cmd := exec.Command("../a/a.go")
    if err := cmd.Run(); err != nil{
       fmt.Println(err)
    }
}
Himanshu
  • 12,071
  • 7
  • 46
  • 61