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:
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; ...")