dongying9756
2016-08-31 01:44
浏览 179
已采纳

连续两次进入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.

  • 写回答
  • 好问题 提建议
  • 关注问题
  • 收藏
  • 邀请回答

2条回答 默认 最新

  • douwen7516 2016-08-31 04:01
    已采纳

    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.

    已采纳该答案
    评论
    解决 无用
    打赏 举报
  • doubi6898 2016-08-31 04:35

    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; ...")
    
    评论
    解决 无用
    打赏 举报

相关推荐 更多相似问题