duanpuqi9965 2016-09-29 08:00
浏览 397
已采纳

通过Golang执行Shell脚本后找回错误

I have a simple shell script (named copy.sh) which looks like below:-

#! /bin/sh

cp $1 $2

I did chmod 777 copy.sh.

I have a golang code which executes the above shell code:-

package main

import (
    "fmt"
    "os/exec"
)

func main() {
    _, err := exec.Command("/Users/debraj/copy.sh", "/Users/debraj/temp.txt", "/Users/debraj/gotest/").Output()
    if err != nil {
        fmt.Println("Failed to execute command " + err.Error())
        return
    }
    fmt.Printf("
Copy Successful - %v")
}

The above code is showing be the below output:-

jabongs-MacBook-Pro-4:src debraj$ go run copyerr.go 
Failed to execute command exit status 1
jabongs-MacBook-Pro-4:src debraj$ 

But the error I receive from shell script looks like below:-

jabongs-MacBook-Pro-4:~ debraj$ ./copy.sh /Users/debraj/temp.txt /Users/debraj/gotest/
cp: /Users/debraj/gotest/temp.txt: Permission denied     

Can someone let me know how how can I get the same error message that is returned by the shell script?

If I don;t do chmod 777 copy.sh and the file has permission as below:-

jabongs-MacBook-Pro-4:~ debraj$ ls -la copy.sh 
-rw-r--r--  1 debraj  staff  21 Sep 29 13:28 copy.sh

Then the golang code gives the output as given by the shell script. Can some also let me know why this is behaving like this?

I am on

  • Golang 1.7
  • Mac OS X 10.11.4
  • 写回答

1条回答 默认 最新

  • duanbai5348 2016-09-29 13:28
    关注

    Alrighty! So the problem at its core is that you have two different places that you can get an error:

    1. When exec.Command().Output() can't run your shell script at all (because it doesn't have permissions in this case), and
    2. When your shell script itself runs, but returns an error code (because the last line run in the script is an error).

    In the first case, you should get something reasonable in err because Go itself got a well-defined system error when trying to do something (which failed immediately). In the second case, Go was able to run your script, but it had output (that you're ignoring) and an return code (in this case, the exit code of the last thing executed by your script). Since the output could have literally been anything at all (including megabytes and megabytes of nonsense text), Go takes the lazy way out and just returns the status code. This, IMHO, is the right thing to do, given the rampant abuse of stderr by basically everyone.

    Plus, your script (if modified slightly) could actually return an error code without displaying an error message at all!

    That said, if you want to see the actual error information from your script, do one of the following:

    // Display everything we got if error.
    output, err := exec.Command(...).CombinedOutput()
    if err != nil {
        fmt.Println("Error when running command.  Output:")
        fmt.Println(string(output))
        fmt.Printf("Got command status: %s
    ", err.Error())
        return
    }
    

    or:

    // Display just the stderr if an error occurs
    cmd := exec.Command(...)
    stderr := &bytes.Buffer{}    // make sure to import bytes
    cmd.Stderr = stderr
    err := cmd.Run()
    if err != nil {
        fmt.Println("Error when running command.  Error log:")
        fmt.Println(stderr.String())
        fmt.Printf("Got command status: %s
    ", err.Error())
        return
    }
    

    ...or you can go fancier and use StderrPipe() and just read the last line or similar. Really, there are a bunch of ways to skin this particular cat (since cmd.Stderr can be any io.Writer, you could easily write nearly any handler -- including one that parses the stream looking for root causes, etc etc).

    Something to consider is that Output() and CombinedOutput() as used will display stuff only when the script is completely done running. This can be a serious UI problem with long-running scripts, as it means that any user monitoring what's going on will see a big, fat load of nothing while processing is happening. Using StdoutPipe() and StderrPipe() is likely preferable, as you can parse/interpret (or just display) output as it happens. Of course, if you can guarantee that your script won't run for more than a few seconds or that it won't be monitored by a human, who cares?

    If you want to explore how to display output as the script runs, the example for StdoutPipe() is a pretty good place to start.

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥15 对象代号: , 表单: 不存在!
  • ¥15 WebSocket的问题
  • ¥15 centos上启动kylin后网页报错404
  • ¥20 使用hackrf进行信号收发时接收到的信号幅度太小
  • ¥15 WebSocket的问题
  • ¥15 BDSBAS-B1C和B1C信号有什么不同
  • ¥15 在半圆平面内随机生成点坐标
  • ¥15 系统容量变化的几种多址方式TDMA, CDMA,FDMA,OFDMA 对比,应该给的是一个曲线 图,随着系统容量的增加,几种多址方式性能的对比 图,MATLAB程序仿真折线图
  • ¥15 用visual Studio 写c ++只运行上一个旧代码的运行结果是怎么回事
  • ¥15 系统容量变化的几种多址方式(TDMA,FDMA,OFDMA,CDMA)对比(相关搜索:曲线图)