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

Go os/exec Command.Start() twice in a row

我正在尝试使用 Go 的 os/exec Command() 模拟按键 ,有时我我会想在 快速连续 中多次使用此按键。我正在使用 exec.Command 调用 "xte"、"key XF86AudioPlay",这会暂停 Linux OS 上的音乐。虽然 Command 可以 Start() Run() 没有问题,但如果我尝试再次执行,我会得到一个错误:

exec: already started

我试过在执行后立即使用 Process.Kill() 来释放它,但这首先会使执行无法进行。我从这里得到这个想法:Terminating a Process Started with os/exec in Golang

我的代码使用 switch 并相应地调用此暂停函数,但我将简单地分享我编写的代码的基础,以 case 作为示例函数:

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

所以,回顾一下,我成功调用了一次,但在成功调用 Pause() 后,我从 cmd.Start()/Run() 中收到错误,内容如下: exec: already started.

我也尝试过降低,即使用syscall,但我运行遇到了一些麻烦。我试过了:

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

这里我得到一个 permission denied 错误,甚至 运行 作为超级用户 (sudo)。

我应该如何继续调用此 Command() 然后释放它以立即 召回 ?还是我在 syscall 的正确轨道上?

编辑 所以 Amd 和 Son Bui 状态的解决方案是 create 每次我打算调用它时 Command,基本上把赋值 cmd := exec.Command()里面我的Pause()方法。

正如 type Cmd struct { 文档所说:

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


1- 像这样使用两个单独的 exec.Command
此外,您可能需要 运行s 之间的一些延迟(模拟两个单独的击键):

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- 您可以 运行 一次:

你可以使用这样的东西(未测试):

xte 'key XF86AudioPlay' 'key XF86AudioPlay'

并考虑向 xte 命令添加一个短暂的延迟(模拟两个单独的击键):

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

像这样:

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

参见:

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


希望对您有所帮助。

从源代码看:

cmd 结构:(https://golang.org/src/os/exec/exec.go 第 99 行)

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

并且在Start函数中(https://golang.org/src/os/exec/exec.go第327行) :

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

所以你只能使用cmd.Start一次。如果你想多次使用,你可以一次创建新的 Cmd 或 运行 多个命令,例如:

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