1

I have the following code which works, I need to execute commands in chain that need to finish before the other command is executed. I do it with the wait command with ugly ifElse and if I need to chain more command it become uglier...is there a better way to write it in go?

    cmd, buf := exec.CommandContext("npm", dir+"/"+path, "install")

    //Wait 
    if err := cmd.Wait(); err != nil {
        log.Printf("executing npm install returned error: %v", err)
    } else {
        log.Println(buf.String())
        gulpCmd, gulpBuf := exec.CommandContext(“gulp”, pdir+"/"+n.path)
   //Wait 
        if err := gulpCmd.Wait(); err != nil {
            log.Printf("error: %v", err)
        } else {
            log.Println(gulpBuf.String())
            pruneCmd, pruneBuf := exec.CommandContext("npm", pdir+"/"+n.path, "prune", "--production")
     //Wait 
            if err := pruneCmd.Wait(); err != nil {
                log.Printf("error: %v", err)
            } else {
                log.Println(pruneBuf.String())
            }

        }

update:

if I try to run this simple program it works and I get message

added 563 packages in 19.218s*

This is the code

cmd := exec.Command("npm", "install")
cmd.Dir = filepath.Join(pdir, n.path)
cmdOutput := &bytes.Buffer{}
cmd.Stdout = cmdOutput
err := cmd.Run()
if err != nil {
    os.Stderr.WriteString(err.Error())
}
fmt.Print(string(cmdOutput.Bytes()))

But If I try like following, I get error and it not able to execute the first command which is npm install, any idea?

cmdParams := [][]string{
    {"npm", filepath.Join(dir,path), "install"},
    {"gulp", filepath.Join(pdir,n.path)},
    {"npm", filepath.Join(pdir, n.path), "prune", "--production"},
}

for _, cmdParam := range cmdParams {
    out, err := exec.Command(cmdParam[0], cmdParam[1:]...).Output()
    if err != nil {
        log.Printf("error running %s: %v\n", cmdParam[0], err)
        return
    }
    log.Println(string(out))
}

The error I get is error running npm: exit status 1

update 2

The commands are and should be run one after another, when the first finish just then run the gulp etc, and also I need to provide the output from the commands

1. npm install
2. gulp
3. npm prune 

2 Answers2

2

List your commands in a slice, and use a simple loop to execute all sequentially. And use filepath.Join() to build folders.

Also I'm not sure what package you're using to run the commands, using os/exec we can simplify further the execution of the commands in the loop body. For example Command.Output() runs the command and returns its standard output:

cmdParams := [][]string{
    {filepath.Join(dir,path), "npm", "install"},
    {filepath.Join(pdir,n.path), "gulp"},
    {filepath.Join(pdir, n.path), "npm", "prune", "--production"},
}

for _, cp := range cmdParams {
    log.Printf("Starting %s in folder %s...", cp[1:], cp[0])
    cmd := exec.Command(cp[1], cp[2:]...)
    cmd.Dir = cp[0]
    // Wait to finish, get output:
    out, err := cmd.Output()
    if err != nil {
        log.Printf("Error running %s: %v\n", cp[1:], err)
        return
    }
    log.Println("Finished %s, output: %s", cp[1:], out)
}
icza
  • 389,944
  • 63
  • 907
  • 827
  • I try to use the code and I got multiple errors , will try with the last update like on `cmdParams` etc as unused –  Feb 15 '18 at 15:09
  • wnen I use the code I got lots of error like in unused `cmdParams` and also where is the `wait` ? –  Feb 15 '18 at 15:14
  • @RaynD Then you failed to copy my code. `cmdParams` is used in the loop. And wait is not needed since `Command.Outupt()` implicitly waits for the command to complete (or fail). – icza Feb 15 '18 at 15:15
  • I was able to fix the error it should be like this missing trailing `,` ` `cmdParams := [][]string{ {"npm", filepath.Join(pdir, n.path), "install"}, {"gulp", filepath.Join(pdir, n.path)}, {"npm", filepath.Join(pdir, n.path), "prune", "--production"}, } for _, cmdParam := range cmdParams { out, err := exec.Command(cmdParam[0], cmdParam[1:]...).Output() if err != nil { log.Printf("error running %s: %v\n", cmdParam[0], err) return err } log.Println(string(out)) }` –  Feb 15 '18 at 15:22
  • @RaynD You're right, the code I've posted had syntax error. Fixed it. – icza Feb 15 '18 at 15:24
  • Great, ive test it now and I got error `could not find symbol value for Stderr` when running `npm install` any idea what it could be ? –  Feb 15 '18 at 15:25
  • @RaynD I just copied the commands from the question, are you sure they are valid and working? – icza Feb 15 '18 at 15:26
  • the command that I run is wrapper on os/exec like this and it works , not im not sure why the error is coming and in the debug the second paramter show the right path, any idea what it could be ?...`func CommandContext(name string, entryPath string, args ...string) (*exec.Cmd, *bytes.Buffer) { cmd := exec.Command(name, args...) cmd.Dir = entryPath buf := &bytes.Buffer{} cmd.Stdout = buf cmd.Stderr = buf if err := cmd.Start(); err != nil { log.Printf("Failed to start cmd: %v", err) } return cmd, buf }` –  Feb 15 '18 at 15:36
  • Did you check it on your side with npm ? –  Feb 15 '18 at 15:38
  • I try again to use the code you suggested, which is look exactly what I want but it's not working...failing on the first command, please see my update. do I miss something ? becouse running `npm install` with the regular exec works... –  Feb 15 '18 at 21:50
  • the error I got is `error running npm: exit status 1` and using the simple `cmd := exec.Command("npm", "install")` works... –  Feb 15 '18 at 21:54
  • maybe its related to this ? `cmdParam[1:]...` ,? –  Feb 15 '18 at 21:58
  • @RaynD You claim `exec.Command("npm", "install")` works, but the first command you execute is `exec.CommandContext("npm", dir+"/"+path, "install")`, they are not the same. Please edit the question and list the exact commands you would run in your shell, and I'll edit my Go code to match those. – icza Feb 15 '18 at 22:07
  • To make it more clear ignore `commandContext` and assume Im using the code in the update which is more generic and works( the `CommandContect` is just wrapper of the `exec.Command` ...) and the are doing at the end exactly the same... –  Feb 15 '18 at 22:10
  • You can create this simple pacage json and try to run your code...you will see the error which I get https://github.com/heroku/node-js-sample/blob/master/package.json –  Feb 15 '18 at 22:13
  • @RaynD Regardless of any Go code, can you list all the shell commands you want to execute? – icza Feb 15 '18 at 22:24
  • exactly like in the post `{"npm", filepath.Join(dir,path), "install"}, {"gulp", filepath.Join(pdir,n.path)}, {"npm", filepath.Join(pdir, n.path), "prune", "--production"},` –  Feb 15 '18 at 22:26
  • @RaynD Ok, so the folder is the working directory, and it's not a parameter of the commands. See my edited answer. – icza Feb 15 '18 at 22:52
  • I try it and now there is error to compile the code , it doesn't recognise `exec.Output()` , should I get the output differently? –  Feb 16 '18 at 04:59
  • It should be `cmd.Output()` , Thanks! –  Feb 16 '18 at 05:04
  • one last question in this context, assume I need to add to the out message `string(out) ` additional info which related to each process, how would you do it ? for example, `starting npm install` and `done running npm install` , thanks a lot! –  Feb 16 '18 at 05:07
  • @RaynD See edited answer. Added logs to display which command in which folder is being executed. – icza Feb 16 '18 at 08:28
  • please see if you can help here either https://stackoverflow.com/questions/48849625/running-command-and-get-the-output-online , thanks –  Feb 18 '18 at 08:01
0

To avoid ugly if-else you can write code like this:

err := someFunction1()
if err != nil {
  return err
}

err := someFunction2()
if err != nil {
  return err
}

err := someFunction3()
if err != nil {
  return err
}

but you will have ugly (IMHO) multiply return statements

RidgeA
  • 1,506
  • 1
  • 10
  • 18
  • Your opinion is fine but it's worth noting that multiple returns is 100% idiomatic Go. It is the preferred method for handling this type of logic in this language. – Adrian Feb 15 '18 at 15:08
  • @Adrian yeah, but it still seems to me ugly :-) – RidgeA Feb 15 '18 at 15:10