#macos #go #osascript
#macos #Вперед #osascript
Вопрос:
Я запускаю приложение golang на Macos. В нем есть несколько кодов, подобных следующему:
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
, и это всегда работает нормально без каких-либо проблем, даже если экран заблокирован / выключен в Linux.
Отредактировано:
Мое решение — использовать chrome вместо:
cmd := exec.Command(`/Applications/Google Chrome.app/Contents/MacOS/Google Chrome`, "-new-window", "/path/hello.html")
Ответ №1:
Похоже, это связано с тем, как 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)
}
}
}
}
Комментарии:
1. Большое спасибо за ответ. Это работает, но есть проблема. Мне нужно, чтобы диалоговое окно присутствовало вечно, пока я не нажму кнопку «OK» в диалоговом окне, когда экран НЕ выключен. С решением диалоговое окно исчезнет по истечении времени ожидания. Есть ли какое-либо лучшее решение? В противном случае мне приходится указывать очень большое значение тайм-аута, что не очень идеально. Спасибо.
2. О, конечно, есть решение. Дайте мне несколько минут.
3. Большое вам спасибо. Не торопитесь. ^_^
4. Похоже, проблема конкретно в
display dialog
я обновил код вторым примером, который проверяет, заблокирован ли экран, прежде чем пытаться вызвать диалоговое окно отображения.5. Большое вам спасибо за ваш код и время. Раньше я не понимал, что эта проблема возникает только с osascript. Теперь я использую
cmd := exec.Command(`/Applications/Google Chrome.app/Contents/MacOS/Google Chrome`, "-new-window", "/path/hello.html")
и пока все идет хорошо. Я думаю, что пока я буду использовать браузер. 🙂
Ответ №2:
Вы можете просто использовать amp;
в конце вашей команды, которая заставит программу работать в фоновом режиме.
for {
time.Sleep(time.Second * 5)
cmd := exec.Command("/usr/bin/osascript", "-e", `display dialog "hello" with title "hello"`, "amp;")
err := cmd.Run()
}