如何执行具有未知参数的系统命令?

huangapple go评论75阅读模式
英文:

How to execute system command with unknown arguments?

问题

我有一堆系统命令,类似于向文件追加新内容。我写了一个简单的脚本来执行系统命令,如果命令只有单个单词(如'ls','date'等),它可以正常工作。但是如果命令比较长,程序就会崩溃。

以下是代码:

package main

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

func exe_cmd(cmd string, wg *sync.WaitGroup) {
	fmt.Println(cmd)
	out, err := exec.Command("bash", "-c", cmd).Output()
	if err != nil {
		fmt.Println("error occurred")
		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": 在 $PATH 中找不到可执行文件
exec: "echo newline >> f2.o": 在 $PATH 中找不到可执行文件
exec: "echo newline >> f1.o": 在 $PATH 中找不到可执行文件

我猜这可能是因为没有将命令和参数分开发送(参考:http://golang.org/pkg/os/exec/#Command)。我想知道如何解决这个问题,因为我不知道要执行的命令中会有多少个参数。

英文:

I have a bunch of systems commands which are somwhat similar to appending new content to a file. I wrote a simple script to execute system commands, which works well if there are single words like 'ls' , 'date' etc. But if the command is greater than that, program dies.

The following is the code

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()
}

The following is the error i see

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 

I guess, this may be due to, not sending cmds and arguments seperately ( http://golang.org/pkg/os/exec/#Command ). I am wondering how to subvert this, since I don't know how many arguments will be there in my command which needs to be executed.

答案1

得分: 106

我找到了一个相对不错的方法来实现相同的功能。

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

到目前为止,这个方法对我来说还可以。我还在寻找更好的方法来实现相同的功能。

编辑1:

最终,一个更简单和高效(至少目前是这样)的方法是这样的:

func exeCmd(cmd string, wg *sync.WaitGroup) {
  fmt.Println("command is ", cmd)
  // 分割命令头 => g++ 部分 => 剩余的命令
  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() // 需要通知 waitgroup 这个 goroutine 已经完成
}

感谢 Go 语言中的可变参数和指出这一点的人们 如何执行具有未知参数的系统命令?

英文:

I found a relatively decent way to achieve the same .

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

Works for me until now. Still finding better ways to achieve the same.

Edit1:

Finally a easier and efficient (atleast so far) way to do would be like this

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
}

Thanks to variadic arguments in go and people that pointed that out to me 如何执行具有未知参数的系统命令?

答案2

得分: 11

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

示例:

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("发生错误")
        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)
}
英文:

For exec.Command() the first argument needs to be the path to the executable. Then the remaining arguments will be supplied as arguments to the executable. Use strings.Fields() to help split the word into a []string.

Example:

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()
}

Here's an alternative approach that just writes all the commands to a file then executes that file within the context of the new created output directory.

Example 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)
}

答案3

得分: 8

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命令,这是一个救命稻草。未测试复杂情况。

英文:
    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)

Tested couple cases and all work good. This is a lifesaver if you are dealing with quick shell commands in your program. Not tested with complex cases.

huangapple
  • 本文由 发表于 2013年12月7日 11:50:11
  • 转载请务必保留本文链接:https://go.coder-hub.com/20437336.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定