如何保持Mac程序运行,即使它已经冻结(停滞)或崩溃



想象一下,您有一个程序想要继续运行,即使在无人值守的情况下也是如此。但它有时会冻结或崩溃,所以你需要一种自动的方法来检测这些情况并采取相应的行动。

我知道这部分可以通过使用launchd来处理,但这种机制不是为GUI应用程序(AFAIK(设计的,它也不会检测程序是否无限期冻结。

(这与我在这里的问题有关:当OS X应用程序崩溃时,它如何重新启动自己(

我已经用下面的AppleScript解决了这个问题。解释包含在源代码中。

要使用此代码,您需要将其保存为具有选项"的应用程序;保持开放";在脚本编辑器或脚本调试器中。

(如果你有语法上的改进,请直接编辑代码。对于功能上的更改,请发表评论或自己的文章。(

-- This script makes sure that a particular program keeps on running,
-- even if it has frozen, in which case it'll be force-quit and relaunched.
--
-- Written 31 May 2021 by Thomas Tempelmann (apps.tempel.org).
-- I claim no copyright. Use it as you like.
-----------
-- Setup
-----------
-- This is the time after which an unresponsive app is considered
-- frozen. I suggest using 30 (for 30 seconds).
property timeoutInSeconds : 30
-- Set the app's bundle ID directly if you know it,
-- such as "com.yourdomain.appname"
property bundleID : ""
-- Otherwise, specify the path to the app:
property theAppPath : "/Applications/The App Name.app"
-----------
-- The following code should need no alterations. It'll force-quit the app if it
-- is frozen, and launch the app if it was not runnnig or if it was force-quit.
-----------
global theAppFile
on run
if bundleID is "" then
set theAppFile to POSIX file theAppPath
set bundleID to bundle identifier of (info for (theAppFile))
else
set theAppFile to null
end if
end run
on idle
-- This code gets invoked once a minute
-- It will then check on the app and restart it if necessary:
my checkTheApp()

return 60 -- idleInterval in seconds
end idle
on checkTheApp()
-- Locate all running programs that use the given 'bundleID'
tell application id "com.apple.systemevents" -- System Events.app
set matchingProcesses to (every process whose bundle identifier is bundleID)
set isRunning to matchingProcesses is not {}
if isRunning then
tell first item of matchingProcesses
set theID to its unix id
set theAppFile to (POSIX path of its application file) as POSIX file
end tell
end if
end tell

set isFrozen to false
if isRunning then
-- The program is running. Now let's check whether it's frozen.
with timeout of timeoutInSeconds seconds
try
tell application id bundleID
get documents
end tell
on error errMsg number errNum
if errNum is -1712 then
-- This error means that the request timed out (i.e. no response).
set isFrozen to true
else
-- Some other error, usually -1728 for apps that do not process
-- this particular AppleEvent request.
-- As it's not a timeout, we assume that the app is not frozen.
end if
end try
end timeout
end if

-- If the app is frozen, we force-quit it.
-- Unsaved changes will be lost!
if isFrozen then
try
do shell script "/bin/kill -9 " & theID
set isRunning to false
delay 1
end try
end if

-- Restart the app if it's not runnning
if not isRunning then
if (theAppFile is not null) and (theAppFile exists) then
-- We prefer to restart exactly the very same app that
-- we had force-quit above
set itsAlias to theAppFile as alias
activate application (itsAlias as string)
else
-- If don't know the app's path we launch it by its bundleID
activate application id bundleID
end if
end if
end checkTheApp

Robert Kniazidis在MacScripter上提出了一个更简短的脚本。

它还使用轮询循环来处理等待终止过程更好地成功的问题。

我唯一的问题是,如果你有多个版本的应用程序,并且想运行特定版本的应用,这个脚本不会给你这个选项。除此之外,它看起来比我的版本要好得多。

-- this script makes sure that a particular program keeps on running,
-- even if it has frozen, in which case it'll be force-quit and relaunched.
property timeoutInSeconds : 30
property appName : "Handbrake"
property bundleID : id of application appName
on idle
if running of application id bundleID then
tell application id "com.apple.systemevents" to set theID to ¬
unix id of (first process whose bundle identifier is bundleID)
with timeout of timeoutInSeconds seconds
try
tell application id bundleID to get documents
on error errMsg number errNum
if errNum is -1712 then -- is frozen
do shell script "/bin/kill -9 " & theID
repeat while its running of application id bundleID
delay 0.2
end repeat
activate application id bundleID
end if
end try
end timeout
else
activate application id bundleID
end if
return 60
end idle

最新更新