54

I am trying to figure out a way to execute a script (.sh) file from Golang. I have found a couple of easy ways to execute commands (e.g. os/exec), but what I am looking to do is to execute an entire sh file (the file sets variables etc.).

Using the standard os/exec method for this does not seem to be straightforward: both trying to input "./script.sh" and loading the content of the script into a string do not work as arguments for the exec function.

for example, this is an sh file that I want to execute from Go:

OIFS=$IFS;
IFS=",";

# fill in your details here
dbname=testDB
host=localhost:27017
collection=testCollection
exportTo=../csv/

# get comma separated list of keys. do this by peeking into the first document in the collection and get his set of keys
keys=`mongo "$host/$dbname" --eval "rs.slaveOk();var keys = []; for(var key in db.$collection.find().sort({_id: -1}).limit(1)[0]) { keys.push(key); }; keys;" --quiet`;
# now use mongoexport with the set of keys to export the collection to csv
mongoexport --host $host -d $dbname -c $collection --fields "$keys" --csv --out $exportTo$dbname.$collection.csv;

IFS=$OIFS;

from the Go program:

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

where mongoToCsvSH can be either the path to the sh or the actual content - both do not work.

Any ideas how to achieve this?

orcaman
  • 6,263
  • 8
  • 54
  • 69

4 Answers4

60

For your shell script to be directly runnable you have to:

  1. Start it with #!/bin/sh (or #!/bin/bash, etc).

  2. You have to make it executable, aka chmod +x script.

If you don't want to do that, then you will have to execute /bin/sh with the path to the script.

cmd := exec.Command("/bin/sh", mongoToCsvSH)
OneOfOne
  • 95,033
  • 20
  • 184
  • 185
  • 2
    thanks. this seems to be working. however now I have a different problem - although the script takes about 1 min to run, the program does not wait for it to finish running. and so the execution continues without the export actually being written yet.. any ideas? – orcaman Sep 14 '14 at 14:35
  • @orcaman how are you running it? `exec.Command("/bin/sh", mongoToCsvSH).Output()`? – OneOfOne Sep 14 '14 at 14:36
  • yes. thing is, I have to wait for this sh to export the file before I continue to the next lines. – orcaman Sep 14 '14 at 14:38
  • @orcaman It shouldn't do that, I'm not sure why it does. Also take a look into [mgo](https://labix.org/mgo) – OneOfOne Sep 14 '14 at 14:40
  • 2
    can we pass arguments to mongoToCsvSH in the above example? – KD157 Dec 15 '15 at 05:31
  • @KD157 use, `exec.Command("/bin/sh", mongoToCsvSH, "arg 1", "arg 2")`. – OneOfOne Dec 16 '15 at 05:45
  • Might be overkill, but if needing more control over the bash environment may want to check out [go-basher](https://github.com/progrium/go-basher). – Brian Jordan Feb 25 '17 at 06:07
  • Is `mongoToCsvSH` path to sh file? – Benyamin Jafari Jan 02 '19 at 11:17
  • 1
    @BenyaminJafari that looks to be the case per Richard Bright's answer. (adding this mostly for future readers) – M Stefan Walker Dec 03 '20 at 18:00
12

This worked for me

func Native() string {
    cmd, err := exec.Command("/bin/sh", "/path/to/file.sh").Output()
    if err != nil {
    fmt.Printf("error %s", err)
    }
    output := string(cmd)
    return output
}
Richard Bright
  • 121
  • 1
  • 2
6

You need to execute /bin/sh and pass the script itself as an argument.

Evan
  • 6,369
  • 1
  • 29
  • 30
1

This allows you to pass the arguments as well as get the output of the script in the Stdout or Stderr.

import (
    "github.com/sirupsen/logrus"
    "os"
    "os/exec"
)

func Execute(script string, command []string) (bool, error) {

    cmd := &exec.Cmd{
        Path:   script,
        Args:   command,
        Stdout: os.Stdout,
        Stderr: os.Stderr,
    }

    c.logger.Info("Executing command ", cmd)

    err := cmd.Start()
    if err != nil {
        return false, err
    }

    err = cmd.Wait()
    if err != nil {
        return false, err
    }

    return true, nil
}

Calling example:

command := []string{
    "/<path>/yourscript.sh",
    "arg1=val1",
    "arg2=val2",
}

Execute("/<path>/yourscript.sh", command)
Vishrant
  • 15,456
  • 11
  • 71
  • 120