当屏幕为 locked/off 时,cmd.Run() 在 MacOS 上的 golang 中永远挂起
cmd.Run() hangs forever in golang on Macos when the screen is locked/off
我是 运行 Mac 上的 golang 应用程序。它有一些代码如下:
for {
time.Sleep(time.Second * 5)
cmd := exec.Command("/usr/bin/osascript", "-e", `display dialog "hello" with title "hello"`)
err := cmd.Run()
}
如果我不锁定屏幕(屏幕一直亮着),它就可以正常工作。但是代码 err := cmd.Run()
如果在该行执行时屏幕被锁定和关闭,代码将永远挂起。当我解锁屏幕(打开它)时,for
循环就永远挂在那里,永远不会继续执行。
我不确定这个问题是属于golang还是MacOS如何处理osascript。谁能告诉我如何解决它?非常感谢。
PS:我在 Linux 中使用了相同的代码,并将 /usr/bin/osascript
替换为 /usr/bin/xmessage
,即使屏幕是 locked/off 在 Linux 中。
已编辑:
我的解决方案,改用chrome:
cmd := exec.Command(`/Applications/Google Chrome.app/Contents/MacOS/Google Chrome`, "-new-window", "/path/hello.html")
这似乎与MacOS在屏幕锁定时如何让进程处于空闲状态有关。它使 osasscript
子进程永远不会完成执行并阻止 for 循环。
您可以做的一件事是 运行 带有超时上下文的命令。我已经尝试过并且有效。当屏幕解锁且超时到期时,将继续执行。
示例:
package main
import (
"context"
"fmt"
"os/exec"
"time"
)
func main() {
for {
time.Sleep(time.Second * 5)
// run your command with a timeout
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
cmd := exec.CommandContext(
ctx,
"/usr/bin/osascript",
"-e",
`display dialog "hello" with title "hello"`,
)
err := cmd.Run()
if err != nil {
fmt.Println(err)
}
// don't forget to cancel your context to avoid context leak
cancel()
}
}
或者,如果您不想超时,可以在尝试调用显示对话框之前检查屏幕是否已锁定。
package main
import (
"context"
"fmt"
"os"
"os/exec"
"strings"
"time"
)
func main() {
for {
time.Sleep(time.Second * 5)
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
cmd := exec.CommandContext(
ctx,
"python",
"-c",
"import sys,Quartz; d=Quartz.CGSessionCopyCurrentDictionary(); print d",
)
var err error
var b []byte
if b, err = cmd.CombinedOutput(); err != nil {
cancel()
continue
}
cancel()
// if screen is not locked
if !strings.Contains(string(b), "CGSSessionScreenIsLocked = 1") {
cmd = exec.Command(
"/usr/bin/osascript",
"-e",
"display dialog \"Hello\"",
)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err = cmd.Run()
if err != nil {
fmt.Println("err: ", err)
}
}
}
}
您可以在命令末尾使用 &
,这将使程序在后台 运行。
for {
time.Sleep(time.Second * 5)
cmd := exec.Command("/usr/bin/osascript", "-e", `display dialog "hello" with title "hello"`, "&")
err := cmd.Run()
}
我是 运行 Mac 上的 golang 应用程序。它有一些代码如下:
for {
time.Sleep(time.Second * 5)
cmd := exec.Command("/usr/bin/osascript", "-e", `display dialog "hello" with title "hello"`)
err := cmd.Run()
}
如果我不锁定屏幕(屏幕一直亮着),它就可以正常工作。但是代码 err := cmd.Run()
如果在该行执行时屏幕被锁定和关闭,代码将永远挂起。当我解锁屏幕(打开它)时,for
循环就永远挂在那里,永远不会继续执行。
我不确定这个问题是属于golang还是MacOS如何处理osascript。谁能告诉我如何解决它?非常感谢。
PS:我在 Linux 中使用了相同的代码,并将 /usr/bin/osascript
替换为 /usr/bin/xmessage
,即使屏幕是 locked/off 在 Linux 中。
已编辑:
我的解决方案,改用chrome:
cmd := exec.Command(`/Applications/Google Chrome.app/Contents/MacOS/Google Chrome`, "-new-window", "/path/hello.html")
这似乎与MacOS在屏幕锁定时如何让进程处于空闲状态有关。它使 osasscript
子进程永远不会完成执行并阻止 for 循环。
您可以做的一件事是 运行 带有超时上下文的命令。我已经尝试过并且有效。当屏幕解锁且超时到期时,将继续执行。
示例:
package main
import (
"context"
"fmt"
"os/exec"
"time"
)
func main() {
for {
time.Sleep(time.Second * 5)
// run your command with a timeout
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
cmd := exec.CommandContext(
ctx,
"/usr/bin/osascript",
"-e",
`display dialog "hello" with title "hello"`,
)
err := cmd.Run()
if err != nil {
fmt.Println(err)
}
// don't forget to cancel your context to avoid context leak
cancel()
}
}
或者,如果您不想超时,可以在尝试调用显示对话框之前检查屏幕是否已锁定。
package main
import (
"context"
"fmt"
"os"
"os/exec"
"strings"
"time"
)
func main() {
for {
time.Sleep(time.Second * 5)
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
cmd := exec.CommandContext(
ctx,
"python",
"-c",
"import sys,Quartz; d=Quartz.CGSessionCopyCurrentDictionary(); print d",
)
var err error
var b []byte
if b, err = cmd.CombinedOutput(); err != nil {
cancel()
continue
}
cancel()
// if screen is not locked
if !strings.Contains(string(b), "CGSSessionScreenIsLocked = 1") {
cmd = exec.Command(
"/usr/bin/osascript",
"-e",
"display dialog \"Hello\"",
)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err = cmd.Run()
if err != nil {
fmt.Println("err: ", err)
}
}
}
}
您可以在命令末尾使用 &
,这将使程序在后台 运行。
for {
time.Sleep(time.Second * 5)
cmd := exec.Command("/usr/bin/osascript", "-e", `display dialog "hello" with title "hello"`, "&")
err := cmd.Run()
}