连续两次进入os / exec Command.Start()

I'm trying to use Go's os/exec Command() to simulate a keypress, and sometimes I'll want to use this keypress multiple times in quick succession. I'm using exec.Command to call "xte", "key XF86AudioPlay", which pauses music on a Linux OS. While the Command can Start() or Run() no problem, if I try to execute again, I get an error:

exec: already started

I've tried using Process.Kill() immediately after the execution, in order to free it up, but this will make the execution not work in the first place. I got this idea from here: Terminating a Process Started with os/exec in Golang

My code uses a switch and calls this pause function accordingly, but I'll simply share the basis of the code I wrote, with the case as an example function:

cmd := exec.Command("xte", "key XF86AudioPlay")
//...
func Pause() {
        err := cmd.Start() // or cmd.Run()
        if err != nil {
            fmt.Println(err)
        }
        err = cmd.Process.Kill()
        if err != nil {
            fmt.Printf("Failed to kill: %s", err)
        }
}

So, to recap, I'm successful at calling it one time, but upon success calls to Pause(), I get the error from cmd.Start()/Run() which read: exec: already started.

I also tried going lower, that is, using syscall, but I ran into some trouble. I tried:

args[0] = "xte"
args[1] = "key"
args[2] = "XF86AudioPlay"
//... Pause():
            err := syscall.Exec("/bin", args, os.Environ())
        if err != nil {
            fmt.Println(err)
        }

And here I got a permission denied error, even running as super user (sudo).

How should I proceed call this Command() and then free it up for immediate recall? Or am I on the right track with syscall?

Edit So the solution as both Amd and Son Bui state, was to create the Command everytime I intend to call it, basically putting the assignment cmd := exec.Command()inside my Pause() method.

As the type Cmd struct { Docs said:

A Cmd cannot be reused after calling its Run, Output or CombinedOutput methods.


1- Use two separate exec.Command like this,
Also you may need some delay between runs (simulating two separate keystrokes):

package main

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

func main() {
    Pause()
    fmt.Println("Once")
    time.Sleep(100 * time.Millisecond)
    Pause()
    fmt.Println("Twice")
}
func Pause() {
    cmd := exec.Command("xte", "key XF86AudioPlay")
    cmd.Stdout = os.Stdout
    cmd.Stderr = os.Stderr
    err := cmd.Run()
    if err != nil {
        fmt.Println(err)
    }
}

2- You may run it once:

You may use something like this (not tested):

xte 'key XF86AudioPlay' 'key XF86AudioPlay'

and consider adding a short delay to the xte command (simulating two separate keystrokes):

xte 'key XF86AudioPlay' 'usleep 100000' 'key XF86AudioPlay'

Like this:

package main

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

func main() {
    Pause()
    fmt.Println("Once")
}
func Pause() {
    cmd := exec.Command("xte", `key XF86AudioPlay`, `usleep 100000`, `key XF86AudioPlay`)
    cmd.Stdout = os.Stdout
    cmd.Stderr = os.Stderr
    err := cmd.Run()
    if err != nil {
        fmt.Println(err)
    }
}

See:

https://askubuntu.com/questions/499926/why-do-these-xte-commands-work-in-terminal-but-not-when-bound-with-xbindkeys

http://manpages.ubuntu.com/manpages/wily/man1/xte.1.html

http://wiki.robotz.com/index.php/Linux_Tools_to_Remap_Keys_and_Mouse_Buttons


I hope this helps.

See from source code:

cmd struct: (https://golang.org/src/os/exec/exec.go line 99)

// Process is the underlying process, once started.
Process *os.Process

And in Start function (https://golang.org/src/os/exec/exec.go line 327) :

if c.Process != nil {
    return errors.New("exec: already started")
}

So you can only use cmd.Start once time. If you want to use multiple time, you can create new Cmd OR run multiple command in once, ex:

cmd := exec.Command("/bin/sh", "-c", "command1; command2; command3; ...")