通过Golang执行shell脚本后得到错误

时间:2016-09-29 08:00:06

标签: go

我有一个简单的shell脚本(名为copy.sh),如下所示: -

#! /bin/sh

cp $1 $2

我做了chmod 777 copy.sh

我有一个执行上述shell代码的golang代码: -

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("\nCopy Successful - %v")
}

以上代码显示如下输出: -

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

但是我从shell脚本收到的错误如下所示: -

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

有人可以告诉我如何获取shell脚本返回的相同错误消息吗?

如果我不这样做,请chmod 777 copy.sh并且该文件具有以下权限: -

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

然后golang代码给出shell脚本给出的输出。有些人也可以让我知道为什么会这样吗?

我在

  • Golang 1.7
  • Mac OS X 10.11.4

1 个答案:

答案 0 :(得分:2)

Alrighty!所以问题的核心在于你有两个不同的地方可以得到一个错误:

  1. 当exec.Command()。输出()根本无法运行你的shell脚本时(因为它在这种情况下没有权限),并且
  2. 当您的shell脚本本身运行时,但返回错误代码(因为脚本中运行的最后一行是错误的。)
  3. 在第一种情况下,你应该在错误中得到一些合理的东西,因为Go在尝试做某事时会自己得到一个明确定义的系统错误(立即失败)。在第二种情况下,Go能够运行你的脚本,但它有输出(你忽略了)和一个返回代码(在这种情况下,你的脚本执行的最后一件事的退出代码)。由于输出可能完全是任何东西(包括兆字节和兆字节的无意义文本),Go采取懒惰的方式,只返回状态代码。鉴于基本上每个人滥用stderr,恕我直言,这是正确的做法。

    另外,您的脚本(如果略微修改)实际上可以返回错误代码而不显示错误消息!

    也就是说,如果要查看脚本中的实际错误信息,请执行以下操作之一:

    // 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\n", err.Error())
        return
    }
    

    或:

    // 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\n", err.Error())
        return
    }
    

    ...或者你可以去发烧友并使用StderrPipe()并阅读最后一行或类似内容。真的,有很多方法来修饰这个特殊的猫(因为cmd.Stderr可以是任何io.Writer,你可以轻松编写几乎所有的处理程序 - 包括一个解析流寻找根本原因等等)。 / p>

    需要考虑的是,使用的Output()和CombinedOutput()仅在脚本完全运行时显示内容。对于长时间运行的脚本来说,这可能是一个严重的UI问题,因为这意味着任何用户监控正在进行的操作都会在处理过程中看到大量的负载。使用StdoutPipe()和StderrPipe()可能更可取,因为您可以在发生时解析/解释(或仅显示)输出。当然,如果你可以保证你的剧本不会运行超过几秒钟,或者它不会被一个关心的人监视?

    如果您想探索如何在脚本运行时显示输出,StdoutPipe()的示例是一个非常好的起点。