web-dev-qa-db-fra.com

Comment exécuter une commande système dans Golang avec des arguments inconnus

J'ai un tas de commandes systèmes qui ressemblent un peu à l'ajout de nouveau contenu à un fichier. J'ai écrit un script simple pour exécuter des commandes système, qui fonctionne bien s'il y a des mots simples tels que 'ls', 'date' etc. Mais si la commande est supérieure à cette valeur, le programme meurt. 

Ce qui suit est le 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()
}

Ce qui suit est l'erreur que je vois

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 

Je suppose que cela peut être dû au fait que nous n’avons pas envoyé les commandes et les arguments séparément ( http://golang.org/pkg/os/exec/#Command ). Je me demande comment subvertir cela, puisque je ne sais pas combien d'arguments seront présents dans ma commande et doivent être exécutés.

37
Rahul

J'ai trouvé un moyen relativement décent d'atteindre le même objectif. 

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

Travaille pour moi jusqu'à maintenant. Nous trouvons toujours de meilleurs moyens d’atteindre le même objectif.

Edit1:

Enfin, un moyen plus facile et efficace (du moins jusqu'à présent) serait comme ceci

func exe_cmd(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
}

Merci aux arguments variadiques et aux gens qui me l'ont signalé :)

90
Rahul

Pour exec.Command(), le premier argument doit être le chemin d'accès à l'exécutable. Ensuite, les arguments restants seront fournis en tant qu’arguments à l’exécutable. Utilisez strings.Fields() pour diviser le mot en une chaîne [].

Exemple:

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

Voici une approche alternative qui écrit simplement toutes les commandes dans un fichier puis exécute ce fichier dans le contexte du nouveau répertoire de sortie créé.

Exemple 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)
}
8
Larry Battle
    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)

Cas de couple testés et tout fonctionne bien. Cela vous sauvera la vie si vous utilisez des commandes Shell rapides dans votre programme. Non testé avec des cas complexes.

5
rjni

echo n'est pas une commande système. C'est un shell intégré, et donc pas directement accessible depuis exec

4
Xena

out, _: = exec.Command ("sh", "-c", "echo test1232 | grep 12"). Sortie ()

fmt.Printf ("% s", out)

^^ ça marche

0
Chandan Kumar