【问题标题】:How to execute system command with unknown arguments?如何使用未知参数执行系统命令?
【发布时间】:2013-12-07 03:50:11
【问题描述】:

我有一堆类似于将新内容附加到文件的系统命令。我写了一个简单的脚本来执行系统命令,如果有 'ls' , 'date' 等单个单词,它会很好。但是如果命令大于那个,程序就会死。

以下是代码

package main

import (
    "fmt"
    "os/exec"
    "sync"
)

func exe_cmd(cmd string, wg *sync.WaitGroup) {
    fmt.Println(cmd)
    c = cmd.Str
    out, err := exec.Command(cmd).Output()
    if err != nil {
        fmt.Println("error occured")
        fmt.Printf("%s", err)
    }
    fmt.Printf("%s", out)
    wg.Done()
}

func main() {
    wg := new(sync.WaitGroup)
    wg.Add(3)

    x := []string{"echo newline >> foo.o", "echo newline >> f1.o", "echo newline >> f2.o"}
    go exe_cmd(x[0], wg)
    go exe_cmd(x[1], wg)
    go exe_cmd(x[2], wg)

    wg.Wait()
}

以下是我看到的错误

exec: "echo newline >> foo.o": executable file not found in $PATHexec: 
"echo newline >> f2.o": executable file not found in $PATHexec: 
"echo newline >> f1.o": executable file not found in $PATH 

我猜,这可能是由于没有单独发送 cmds 和参数 (http://golang.org/pkg/os/exec/#Command)。我想知道如何颠覆这一点,因为我不知道我的命令中有多少需要执行的参数。

【问题讨论】:

    标签: go


    【解决方案1】:

    我找到了一个相对不错的方法来达到同样的效果。

    out, err := exec.Command("sh","-c",cmd).Output()
    

    直到现在都为我工作。仍在寻找更好的方法来实现这一目标。

    编辑1:

    最后一个更简单有效(至少到目前为止)的方法是这样的

    func exeCmd(cmd string, wg *sync.WaitGroup) {
      fmt.Println("command is ",cmd)
      // splitting head => g++ parts => rest of the command
      parts := strings.Fields(cmd)
      head := parts[0]
      parts = parts[1:len(parts)]
    
      out, err := exec.Command(head,parts...).Output()
      if err != nil {
        fmt.Printf("%s", err)
      }
      fmt.Printf("%s", out)
      wg.Done() // Need to signal to waitgroup that this goroutine is done
    }
    

    感谢 go 中的可变参数以及向我指出这一点的人:)

    【讨论】:

    • 它适用于问题中提到的字符串。但是,如果您尝试执行诸如“echo new >> test.txt”之类的内容,它就会中断。 :(
    • 如果您想执行包含多个部分的命令,例如“command1 && command2”,那么第一种(预编辑)方法可以工作,即使第二种方法失败。
    • 我认为它会在date +"%y %m %d"这样的情况下失败
    • 对于 Windows,等效值为 cmd := exec.Command("C:\\Windows\\System32\\cmd.exe", "/c", command)。您可能应该使用exec.LookPath$PATH/%PATH% 获取系统的shell 路径。
    【解决方案2】:

    对于exec.Command(),第一个参数需要是可执行文件的路径。然后剩余的参数将作为参数提供给可执行文件。使用strings.Fields() 帮助将单词拆分为 [] 字符串。

    示例:

    package main
    
    import (
        "fmt"
        "os/exec"
        "sync"
        "strings"
    )
    
    func exe_cmd(cmd string, wg *sync.WaitGroup) {
        fmt.Println(cmd)
                parts := strings.Fields(cmd)
        out, err := exec.Command(parts[0],parts[1]).Output()
        if err != nil {
            fmt.Println("error occured")
            fmt.Printf("%s", err)
        }
        fmt.Printf("%s", out)
        wg.Done()
    }
    
    func main() {
        wg := new(sync.WaitGroup)
        commands := []string{"echo newline >> foo.o", "echo newline >> f1.o", "echo newline >> f2.o"}
        for _, str := range commands {
            wg.Add(1)
            go exe_cmd(str, wg)
        }
        wg.Wait()
    }
    

    这是另一种方法,只需将所有命令写入文件,然后在新创建的输出目录的上下文中执行该文件。

    示例 2

    package main
    
    import (
        "os"
        "os/exec"
        "fmt"
        "strings"
        "path/filepath"
    )
    var (
        output_path = filepath.Join("./output")
        bash_script = filepath.Join( "_script.sh" )
    )
    func checkError( e error){
        if e != nil {
            panic(e)
        }
    }
    func exe_cmd(cmds []string) {
        os.RemoveAll(output_path)
        err := os.MkdirAll( output_path, os.ModePerm|os.ModeDir )
        checkError(err)
        file, err := os.Create( filepath.Join(output_path, bash_script))
        checkError(err)
        defer file.Close()
        file.WriteString("#!/bin/sh\n")
        file.WriteString( strings.Join(cmds, "\n"))
        err = os.Chdir(output_path)
        checkError(err)
        out, err := exec.Command("sh", bash_script).Output()
        checkError(err)
        fmt.Println(string(out))
    }
    
    func main() {
        commands := []string{
        "echo newline >> foo.o",
        "echo newline >> f1.o",
        "echo newline >> f2.o",
        }
       exe_cmd(commands)
    }
    

    【讨论】:

    • 它正在执行它。但不是真正意义上的。该文件应附加换行符。但它没有发生。
    • 我认为如果你把命令写到一个 bash 脚本中,然后用 go 来执行脚本会更好。否则你将不得不添加对标准输入和输出的支持。
    • 嗯。那很好。但是,如果我能以某种方式截断字符串并将 []string 元素作为参数发送给 Command() ,那就太好了。这样就不需要做所有这些了。
    • Rahul,我相信您所指的是可变参数值。例如: exec.Command("ls", args...).Output() en.wikipedia.org/wiki/Variadic_function
    • 小修正:Command的第二个参数应该是parts[1:]...,否则不起作用。
    【解决方案3】:
        out, _ := exec.Command("sh", "-c", "date +\"%Y-%m-%d %H:%M:%S %Z\"").Output()
        exec.Command("sh","-c","ls -al -t | grep go >>test.txt").Output()
        fmt.Printf("%s\n\n",out)
    

    测试了几个案例,一切都很好。如果您在程序中处理快速的 shell 命令,这是一个救命稻草。未针对复杂案例进行测试。

    【讨论】:

    • 这节省了我很多时间。谢谢!
    猜你喜欢
    • 2011-10-07
    • 2013-04-01
    • 1970-01-01
    • 2014-04-16
    • 2010-10-21
    • 2021-09-07
    • 2021-10-26
    • 1970-01-01
    • 2019-05-13
    相关资源
    最近更新 更多