【问题标题】:Execute another go program from within a golang program从 golang 程序中执行另一个 go 程序
【发布时间】:2016-09-04 11:30:53
【问题描述】:

我想从另一个 go 程序中执行另一个 go 程序并给它 args,如下所示:

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
}

我不想发送一个 int 作为 arg,而是像 http.ResponseWriter 这样的东西

我想到了一个解决方案,但它不是很好:

  1. 使用 gob 将 http.ResponseWriter 转换为字符串
  2. 从 test.go 中读取一行
  3. 将字符串发送到 test.go

感谢您的任何回答:D

【问题讨论】:

  • 不是重复的,你提到的问题是要导入一个go源文件,在程序运行之前就已经知道了,而我想加载一个go源文件/编译文件,只得到运行时的文件名。

标签: go


【解决方案1】:

为要从另一个文件运行的包创建一个可执行文件。提供要在exec.Command 中运行的文件的相对路径。例如:

文件 A

package a

import "fmt"

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

为上述文件创建一个二进制/可执行文件。该文件将在包内,因此请走到包并运行可执行文件。

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

下面是linux系统中二进制文件的例子。

文件 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)
    }
}

【讨论】:

    【解决方案2】:

    有很多方法可以做到这一点互操作性
    1-如果您有双方的源文件,我建议使用标准的 golang 包 (Lib) 调用,而不是互操作性。

    2- 使用“os/exec”:如果你没有源代码,而你只有二进制文件, 或者您可以通过文件或文本参数传递参数:

    你可以像这样传递参数:

    package main
    
    import (
        "fmt"
        "os"
    )
    
    func main() {
    
        fmt.Println(os.Args[0]) // fileNameAndPath
    }
    

    或使用“标志”标准库:

    // 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")
    */
    

    这将提供 -h 的帮助。
    你可以像这样调用另一个二进制程序或 golang 编译器本身:

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

    3- 另一种方法是使用 stdin /stdout 调用外部程序。
    通过这种方式,您可以通过标准输入/输出发送二进制数据:
    这里文件“a”调用二进制文件“b”并通过stdin/stdout发送和接收:
    这是我的转换:
    http://erlang.org/doc/tutorial/c_port.html
    (您可以使用 os 命名管道)
    文件一:

    // 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
    }
    

    文件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 - 另一种方式是使用“net/rpc”,这是从另一个程序调用另一个函数的最佳方式。
    示例:

    // 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
    */
    

    【讨论】:

    • exec.Command() 真的可以执行带有 http.ResponseWriter 等参数的程序吗?此外,我只在运行时获取文件名,这就是为什么如果我正确理解您的意思,第一个解决方案将不起作用的原因。
    • 如果你为你的 http.ResponseWriter 提供一些示例代码会更好,我认为第三种方式更适合这个,我会尽快添加示例
    • @LucaS。您不能序列化 http.ResponseWriter,因为它是一个接口,包实现包含状态,并且它处理网络连接。
    • 你有什么理由使用两个独立的程序
    • 所以你不需要发送http.ResponseWriter。在一台计算机内的两个程序之间进行通信的最佳方式是使用管道甚至 TCP,这种方式非常简单。但如果从另一个程序调用另一个函数“net/rpc”是合适的方式,我认为。
    【解决方案3】:

    使用os/exec,你可以像这样调用go编译器:

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

    Go 不会通过main 将参数传递给程序,例如func main(x int)。在 Go 中执行此操作的方法是使用 os.Args

    在我看来,您正在尝试在 Go 中使用 PHP 概念,结果不会很好。相反,您应该使用模板和静态文件来实现动态网站。查看包裹text/template

    【讨论】:

    • 好吧,我有点尝试在 Go 中使用 PHP 概念,但文本/模板不符合我的标准,例如我需要根据文件和那些东西进行重定向
    猜你喜欢
    • 1970-01-01
    • 2010-10-10
    • 2013-05-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-02-29
    • 2020-09-27
    • 2014-06-10
    相关资源
    最近更新 更多