How to run a CLI command from Go?
Asked Answered
H

5

23

How do I run a Gulp task from a Go program?

This is the command I run from a typical terminal:

gulp serv.dev

How could I run this simple line of code from golang:

package main

import (
    "net/http"
    "github.com/julienschmidt/httprouter"
    "fmt"
)

func main() {
    // What do I put here to open terminal in background and run `gulp serv.dev`?
}
Humiliate answered 31/7, 2015 at 2:11 Comment(0)
U
37

What you're looking for is exec.Command

You'll pretty much want to spawn off a process that will run your gulp task.

This can be done like so:

package main

import (
    "os/exec"
)

func main() {
    cmd := exec.Command("gulp", "serv.dev")
    if err := cmd.Run(); err != nil {
        log.Fatal(err)
    }
}
Umbrageous answered 31/7, 2015 at 2:15 Comment(0)
L
14

Take a look at exec. For your use case:

package main
import (
    "net/http"
    "github.com/julienschmidt/httprouter"
    "fmt"
    "os/exec"
    "log"
)

func main() {
    out, err := exec.Command("gulp", "serv.dev").Output()
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("The date is %s\n", out)
}
Leeuwenhoek answered 31/7, 2015 at 2:13 Comment(0)
U
1

Most probably you need exec package

cmd := exec.Command("gulp", "serv.dev")
err := cmd.Run()

Take a look at example at exec.Command. They explained how to pass parameters and read the output.

Unconquerable answered 31/7, 2015 at 2:16 Comment(0)
M
1

More generic, better output.


Use the exec.Command, along with buffers to record output and display it only when useful.

You can even make the function work with any command by using variadic arguments, aka arguments of an arbitrary quantity of elements.

Label unhanded errors appropriately, so if a command fails you are told which one and why.

Lastly note that Go, although expressive, is a quite raw language. It holds your hand for nothing. You will have to program plenty by yourself.


Example code:

package main

import (
    "bytes"
    "fmt"
    "os"
    "os/exec"
    "runtime"
    "strings"
)

func main() {
    runCommand(currentFunction(), "ping", "-c1", "google.commm")
}

func commandErrorMessage(stderr bytes.Buffer, program string) string {
    message := string(stderr.Bytes())

    if len(message) == 0 {
        message = "the command doesn't exist: " + program + "\n"
    }

    return message
}

func currentFunction() string {
    counter, _, _, success := runtime.Caller(1)

    if !success {
        println("functionName: runtime.Caller: failed")
        os.Exit(1)
    }

    return runtime.FuncForPC(counter).Name()
}

func printCommandError(stderr bytes.Buffer, callerFunc string, program string, args ...string) {
    printCommandErrorUbication(callerFunc, program, args...)
    fmt.Fprintf(os.Stderr, "%s", commandErrorMessage(stderr, program))
}

func printCommandErrorUbication(callerFunc string, program string, args ...string) {
    format := "error at: %s: %s %s\n"
    argsJoined := strings.Join(args, " ")
    fmt.Fprintf(os.Stderr, format, callerFunc, program, argsJoined)
}

func runCommand(callerFunc string, program string, args ...string) {
    command := exec.Command(program, args...)
    var stderr bytes.Buffer
    command.Stderr = &stderr
    fail := command.Run()

    if fail != nil {
        printCommandError(stderr, callerFunc, program, args...)
        os.Exit(1)
    }
}

Example run:

$ go run test.go
error at: main.main: ping -c1 google.commm
ping: google.commm: Name or service not known
exit status 1

Microscopy answered 12/10, 2021 at 18:14 Comment(0)
O
0

I'll leave a small example for those who are going to run a binary:

package main

import (
    "fmt"
    "log"
    "os"
    "os/exec"
    "path"
    "path/filepath"
    "runtime"

)

func main() {
    ex, err := os.Executable()
    if err != nil {
        panic(err)
    }
    exPath := filepath.Dir(ex)

    var command = path.Join(exPath, "libexec")
    os := runtime.GOOS
    switch os {
    case "windows":
        command += ".exe"
    case "darwin":
        command += ""
    case "linux":
        command = "." + command
    default:
        fmt.Printf("%s.\n", os)
    }
    // it lacks to implement a check if the executable exists or
    // is in one of the path variables
    out, err := exec.Command(command).Output()

    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Out: %s", out)

}
  • os/exec - used to get the current executable directory
  • runtime - used to get arch
  • path - used to mount libexec path
Ockeghem answered 19/5, 2023 at 15:26 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.