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

通过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 metadata提取的PDF元数据,如何转换为一个Excel
  • ¥15 关于arduino编程toCharArray()函数的使用
  • ¥100 vc++混合CEF采用CLR方式编译报错
  • ¥15 coze 的插件输入飞书多维表格 app_token 后一直显示错误,如何解决?
  • ¥15 vite+vue3+plyr播放本地public文件夹下视频无法加载
  • ¥15 c#逐行读取txt文本,但是每一行里面数据之间空格数量不同
  • ¥50 如何openEuler 22.03上安装配置drbd
  • ¥20 ING91680C BLE5.3 芯片怎么实现串口收发数据
  • ¥15 无线连接树莓派,无法执行update,如何解决?(相关搜索:软件下载)
  • ¥15 Windows11, backspace, enter, space键失灵