script no longer quitting all open apps under 10.15

If an app supports App Nap, a feature introduced in Mojave where an app can be put into a paused state, it will not be regarded as visible while napping.

There’s no guarantee that the app name and its file name are the same. Acrobat is a good example of this.

Here’s an ASObjC version:

use AppleScript version "2.5" -- macOS 10.11 or later
use framework "Foundation"
use framework "AppKit"
use scripting additions

set theApps to current application's NSWorkspace's sharedWorkspace()'s runningApplications()
set thePred to current application's NSPredicate's predicateWithFormat:"activationPolicy == 0 AND NOT (localizedName IN %@)" argumentArray:{{"Finder", "Script Editor"}} -- edit to suit
set theApps to theApps's filteredArrayUsingPredicate:thePred
repeat with anApp in theApps
	anApp's terminate()
end repeat

It gets the list of apps much faster than System Events’s every process whose background only is false, but its main advantage is that app/process names aren’t required (except for the exceptions), so any differences are don’t matter.

You should keep 1 app at least - the current application.

I really liked the solution from Shane Stanley. It is truly more universal and faster than mine. I will just take it to my library without having to edit the name of the editor or applet itself:


use AppleScript version "2.5" -- macOS 10.11 or later
use framework "Foundation"
use framework "AppKit"
use scripting additions
property keepApps : {name of current application, "Finder"}

set theApps to current application's NSWorkspace's sharedWorkspace()'s runningApplications()
set thePred to current application's NSPredicate's predicateWithFormat:"activationPolicy == 0 AND NOT (localizedName IN %@)" argumentArray:{keepApps}
set theApps to theApps's filteredArrayUsingPredicate:thePred
repeat with anApp in theApps
	anApp's terminate()
end repeat

Hello Shane.
Reading your post pushed me to give the content of my applet “tout quitter”.

use AppleScript version "2.4"
use framework "Foundation"
use scripting additions
property forTesting : true
-- true --> execute a subset of the script plus informative instructions
-- false --> execute the normal tasks

property cleanApplicationState : false
-- true --> empty the folder "Saved Application State:"
-- false --> doesn't empty the folder -- my current setting

if forTesting then set {everyApps, filteredApps} to {{}, {}}

set runningApplications to current application's NSWorkspace's sharedWorkspace()'s runningApplications()
set keepApps to {"Finder", "tout quitter"}
set regularActivationPolicy to current application's NSApplicationActivationPolicyRegular
repeat with anApplication in runningApplications
	set appName to anApplication's localizedName() as text
	if forTesting then set end of everyApps to appName
	if anApplication's activationPolicy() is regularActivationPolicy and appName is not in keepApps then
		if forTesting then
			set end of filteredApps to appName
		else
			anApplication's terminate()
		end if
	end if
end repeat
if forTesting then return {everyApps, linefeed, linefeed, filteredApps}

# Now the applications are cleanly closed
if cleanApplicationState then
	# We may delete the Saved Application State datas
	try
		set p2AS to (path to library folder from user domain as text) & "Saved Application State:"
		tell application "System Events" to tell folder p2AS
			delete every folder -- some items are folders
			delete every file -- some items are aliases
		end tell # System Events …
	end try
end if

set p2pane to ((path to system preferences as text) & "StartupDisk.prefPane") as «class furl»
# Get four localized strings
set cancelBtn to localized string "CANCEL" in bundle p2pane
set restartBtn to localized string "RESTART" in bundle p2pane
set shutdownBtn to localized string "SHUTDOWN" in bundle p2pane
set prompt to localized string "RESTART?" in bundle p2pane

tell application "SystemUIServer" to display dialog prompt buttons {cancelBtn, restartBtn, shutdownBtn} default button 3 cancel button 1
set |éteindre| to (button returned of result) is shutdownBtn
# Now we may trigger shutdown or restart cleanly. Open documents were saved before.
tell application "System Events"
	if |éteindre| then
		shut down
	else
		restart
	end if
end tell # System Events

In real life the property forTesting is set to false.
When I set it to true it return :

{{“loginwindow”, “universalaccessd”, “tout quitter”, “talagent”, “SystemUIServer”, “Dock”, “Finder”, “Spotlight”, “com.apple.dock.extra”, “Centre de notifications”, “Box Finder Extension”, “imklaunchagent”, “com.apple.PressAndHold”, “CoreLocationAgent”, “Wi-Fi”, “Agent Photos”, “FolderActionsDispatcher”, “AirPlayUIAgent”, “popCalendar”, “Box”, “TISwitcher”, “SMARTReporter”, “FastScripts”, “fake_mojave”, “TextEdit”, “nbagent”, “HP Device Monitor”, “Box Helper”, “ViewBridgeAuxiliary”, “Legacy Color Picker Extensions (TextEdit)”, “uBlock”, “storeuid”, “Box UI”, “LaterAgent”, “Xcode”, “com.apple.CoreSimulator.CoreSimulatorService”, “BBEdit”, “Legacy Color Picker Extensions (BBEdit)”, “Aperçu”, “com.apple.speech.speechsynthesisd”, “Mail”, “Safari”, “Safari Networking”, “uBlock Safari Icon”, “Contenu web Safari (préchauffé)”, “LibreOffice”, “CoreServicesUIAgent”, “Éditeur de script”, “com.apple.security.pboxd”, “Événements système”}, "
", "
", {“fake_mojave”, “TextEdit”, “uBlock”, “Xcode”, “BBEdit”, “Mail”, “LibreOffice”, “Éditeur de script”}}

In real life, after several months of use I disabled the cleaning of Saved Application State.
Given that, when I reStart the machine, “tout quitter.app” is executed and installed in the Dock.
It’s why it’s listed in previous tests.
I assume that it’s because it has no provision to respond to a quit call that the previous tests brought an infinite running requiring to force quit the Script Editor.

I wish to add a comment about your script.
I feel that it requires some enhancement because, as is, it would fail to keep the Script Editor running.

use AppleScript version "2.5" -- macOS 10.11 or later
use framework "Foundation"
use framework "AppKit"
use scripting additions

set theApps to current application's NSWorkspace's sharedWorkspace()'s runningApplications()
set thePred to current application's NSPredicate's predicateWithFormat:"activationPolicy == 0 AND NOT (localizedName IN %@)" argumentArray:{{"Finder", "Script Editor"}} -- edit to suit
set theApps to theApps's filteredArrayUsingPredicate:thePred

repeat with anApp in theApps
	--anApp's terminate()
	log (localizedName() of anApp) as string -- EDITED according to Shane Stanley's comment
end repeat

return:
(tout quitter)
(fake_mojave)
(TextEdit)
(uBlock)
(Xcode)
(BBEdit)
(Mail)
(Safari)
(LibreOffice)
(Éditeur de script)

I had to edit it as :

use AppleScript version "2.5" -- macOS 10.11 or later
use framework "Foundation"
use framework "AppKit"
use scripting additions

property |NSURL| : a reference to current application's NSURL
property NSPredicate : a reference to current application's NSPredicate
property NSWorkspace : a reference to current application's NSWorkspace

set POSIXPath to POSIX path of (path to application "Script Editor")
(*
set theURL to current application's NSURL's fileURLWithPath:POSIXPath
--> error "Erreur dans Script Editor : NSURL ne comprend pas le message « fileURLWithPath_ »." number -1708 from NSURL
*)
set theURL to |NSURL|'s fileURLWithPath:POSIXPath
set {theResult, theValue, theError} to theURL's getResourceValue:(reference) forKey:"NSURLLocalizedNameKey" |error|:(reference)
set scriptEditor_loc to theValue as text
if scriptEditor_loc ends with ".app" then set scriptEditor_loc to text 1 thru -5 of scriptEditor_loc -- must drop the name extension
(*
set theApps to current application's NSWorkspace's sharedWorkspace()'s runningApplications()
--> error "Erreur dans Script Editor : NSWorkspace ne comprend pas le message « sharedWorkspace »." number -1708 from NSWorkspace
*)
set theApps to NSWorkspace's sharedWorkspace()'s runningApplications()
(*
set thePred to current application's NSPredicate's predicateWithFormat:"activationPolicy == 0 AND NOT (localizedName IN %@)" argumentArray:{{"Finder", scriptEditor_loc}} -- edit to suit
--> error "Erreur dans Script Editor : NSPredicate ne comprend pas le message « predicateWithFormat_argumentArray_ »." number -1708 from NSPredicate
*)
set thePred to NSPredicate's predicateWithFormat:"activationPolicy == 0 AND NOT (localizedName IN %@)" argumentArray:{{"Finder", "Script Debugger", scriptEditor_loc}} -- edit to suit
set theApps to theApps's filteredArrayUsingPredicate:thePred

repeat with anApp in theApps
	--anApp's terminate()
	log (localizedName() of anApp) as string -- EDITED according to Shane Stanley's comment
end repeat

I’m really puzzled by the need to define some properties.

Yvan KOENIG running High Sierra 10.13.6 in French (VALLAURIS, France) samedi 25 avril 2020 17:02:08

Added “Script Debugger” which is not localized in the list of ‘protected’ applications.

Are you sure of that ?

When I run this test version from Script Editor,


use AppleScript version "2.5" -- macOS 10.11 or later
use framework "Foundation"
use framework "AppKit"
use scripting additions
property keepApps : {name of current application, "Finder"}

set theApps to current application's NSWorkspace's sharedWorkspace()'s runningApplications()
set thePred to current application's NSPredicate's predicateWithFormat:"activationPolicy == 0 AND NOT (localizedName IN %@)" argumentArray:{keepApps}
set theApps to theApps's filteredArrayUsingPredicate:thePred
repeat with anApp in theApps
	--anApp's terminate()
	log (localizedName() of anApp) as string -- EDITED according to Shane Stanley's comment
end repeat

I get :
(tout quitter)
(fake_mojave)
(TextEdit)
(uBlock)
(Xcode)
(BBEdit)
(Mail)
(Safari)
(LibreOffice)
(Éditeur de script)

Once more you forgot that name of current application is not localized.
Here it returns “Script Editor”
while Shane’s script lists localizedName of running applications.
As a result, Script Editor would be quitted.

I’m afraid that you will often miss this kind of behavior because you run your system in English.

Yvan KOENIG running High Sierra 10.13.6 in French (VALLAURIS, France) samedi 25 avril 2020 17:12:46

So, what to do? I would not like to correct the name of the editor or applet in the code each time. Give a solution for non-English systems, if possible.

Just take time to read what I posted to Shane in message #13 were I wrote about this feature before seeing your post.

Yvan KOENIG running High Sierra 10.13.6 in French (VALLAURIS, France) samedi 25 avril 2020 17:32:28

Well, Yvan, thanks for this. I tried your script in the post #13 and it worked for me fine. I just replaced the path to “Script Editor” with path to current application. May you test like me with path to current application and tell me if it works for your non English system?

I want to not hardcode the name of current application. Because I use multilpy choices for my scripts: Script Editor, Script Debugger, applet, droplet, mail rule, and so on… You understand me.

As I assume that your goal is to protect the editor with which you are running I make some enhancement.

use AppleScript version "2.5" -- macOS 10.11 or later
use framework "Foundation"
use framework "AppKit"
use scripting additions

property NSPredicate : a reference to current application's NSPredicate
property NSWorkspace : a reference to current application's NSWorkspace

set protectedName to name of current application
if protectedName is not "Script Debugger" then
	set theBundle to path to application "Script Editor"
	set protectedName to localized string "CFBundleName" from table "InfoPlist" in bundle theBundle
	-- set protectedName to localized string "CFBundleDisplayName" from table "InfoPlist" in bundle theBundle -- same result
end if
(*
set theApps to current application's NSWorkspace's sharedWorkspace()'s runningApplications()
--> error "Erreur dans Script Editor : NSWorkspace ne comprend pas le message « sharedWorkspace »." number -1708 from NSWorkspace
*)
set theApps to NSWorkspace's sharedWorkspace()'s runningApplications()
(*
set thePred to current application's NSPredicate's predicateWithFormat:"activationPolicy == 0 AND NOT (localizedName IN %@)" argumentArray:{{"Finder", scriptEditor_loc}} -- edit to suit
--> error "Erreur dans Script Editor : NSPredicate ne comprend pas le message « predicateWithFormat_argumentArray_ »." number -1708 from NSPredicate
*)
set thePred to NSPredicate's predicateWithFormat:"activationPolicy == 0 AND NOT (localizedName IN %@)" argumentArray:{{"Finder", protectedName}} -- edit to suit
set theApps to theApps's filteredArrayUsingPredicate:thePred

repeat with anApp in theApps
	--anApp's terminate()
	log (localizedName() of anApp) as string -- EDITED according to Shane Stanley's comment
end repeat

It’s the running editor which will be protected.
At this time, the code in message #13 protect both of them.

As you may see, I use a cleaner code to grab the localized name of the Script Editor.

Yvan KOENIG running High Sierra 10.13.6 in French (VALLAURIS, France) samedi 25 avril 2020 18:11:52

I want to not hardcode the name of current application at all. Because I use multilpy choices for my scripts: Script Editor, Script Debugger, applet, droplet, folder action, mail rule, and so on… You understand me.

I tested this:


use AppleScript version "2.5" -- macOS 10.11 or later
use framework "Foundation"
use framework "AppKit"
use scripting additions
property |NSURL| : a reference to current application's NSURL
property NSPredicate : a reference to current application's NSPredicate
property NSWorkspace : a reference to current application's NSWorkspace

set theURL to |NSURL|'s fileURLWithPath:(POSIX path of (path to current application))
set {theResult, theValue, theError} to theURL's getResourceValue:(reference) forKey:"NSURLLocalizedNameKey" |error|:(reference)
set currentApps_loc to theValue as text
if currentApps_loc ends with ".app" then set scriptEditor_loc to text 1 thru -5 of scriptEditor_loc

set theApps to NSWorkspace's sharedWorkspace()'s runningApplications()
set thePred to NSPredicate's predicateWithFormat:"activationPolicy == 0 AND NOT (localizedName IN %@)" argumentArray:{{currentApps_loc, "Finder"}}
set theApps to theApps's filteredArrayUsingPredicate:thePred

repeat with anApp in theApps
	anApp's terminate()
end repeat

Can you test is too, as is?

I tested with both editors and applet running :

use AppleScript version "2.5" -- macOS 10.11 or later
use framework "Foundation"
use framework "AppKit"
use scripting additions

property |NSURL| : a reference to current application's NSURL

set POSIXPath to POSIX path of (path to current application)
(*
set theURL to current application's NSURL's fileURLWithPath:POSIXPath
--> error "Erreur dans Script Editor : NSURL ne comprend pas le message « fileURLWithPath_ »." number -1708 from NSURL
*)
set theURL to |NSURL|'s fileURLWithPath:POSIXPath
set {theResult, theValue, theError} to theURL's getResourceValue:(reference) forKey:"NSURLLocalizedNameKey" |error|:(reference)
set protectedName to theValue as text
-- stripping aways required here
if protectedName ends with ".app" then set protectedName to text 1 thru -5 of protectedName -- must drop the name extension

display dialog protectedName

It returned the correct values:
Script Debugger (which is never localized)
Éditeur de script on my machine running in French
fakeApplet when running an applet by a double click (this one was not localized)
I didn’t try with a mail rule but (1) I don’t see why it would fail (2) at least in French the name of Mail.app is not localized so I would see nothing if it didn’t work.

I guess that the best thing to do is to set, at least temporarily, your machine to run in Greek and make some tests on your side.

I was glad to have found a cleaner way to grab the localized name for Script Editor but, it doesn’t apply to your 3rd, 4th… cases
I hope that Shane will be able to give a cleaner code.
I searched in wCode help for infos about localizedName but found nothing allowing me to clean the code.
As you saw, I was a bit puzzled by the fact that the syntax used by Shane fails on my machine.
What a surprise, after a simple copy then paste in a blank window, the original instructions worked flawlessly so the current code is :

use AppleScript version "2.5" -- macOS 10.11 or later
use framework "Foundation"
use framework "AppKit"
use scripting additions

set POSIXPath to POSIX path of (path to current application)

set theURL to current application's NSURL's fileURLWithPath:POSIXPath
set {theResult, theValue, theError} to theURL's getResourceValue:(reference) forKey:"NSURLLocalizedNameKey" |error|:(reference)
set protectedName to theValue as text
-- always required here
if protectedName ends with ".app" then set protectedName to text 1 thru -5 of protectedName -- must drop the name extension

set theApps to current application's NSWorkspace's sharedWorkspace()'s runningApplications()
set thePred to current application's NSPredicate's predicateWithFormat:"activationPolicy == 0 AND NOT (localizedName IN %@)" argumentArray:{{"Finder", protectedName}} -- edit to suit
set theApps to theApps's filteredArrayUsingPredicate:thePred

repeat with anApp in theApps
	--anApp's terminate()
	log (localizedName() of anApp) as string -- EDITED according to Shane Stanley's comment
end repeat

Yvan KOENIG running High Sierra 10.13.6 in French (VALLAURIS, France) samedi 25 avril 2020 18:59:43

Thank you, Yvan, for testing and your amendment. I see, it works as expected.

Thank’s.

At last, I found the clean way to get the localizedName of the current application.

use AppleScript version "2.5" -- macOS 10.11 or later
use framework "Foundation"
use framework "AppKit"
use scripting additions

set curName to current application's NSRunningApplication's currentApplication()'s localizedName() as string

set theApps to current application's NSWorkspace's sharedWorkspace()'s runningApplications()
set thePred to current application's NSPredicate's predicateWithFormat:"activationPolicy == 0 AND NOT (localizedName IN %@)" argumentArray:{{"Finder", curName}} -- edit to suit
set theApps to theApps's filteredArrayUsingPredicate:thePred

repeat with anApp in theApps
	--anApp's terminate()
	log (localizedName() of anApp) as string -- EDITED according to Shane Stanley's comment
end repeat

Yvan KOENIG running High Sierra 10.13.6 in French (VALLAURIS, France) samedi 25 avril 2020 20:47:09

It certainly should keep the current application running – but that may not be Script Editor. And I suppose the name of Finder could be localized in some locales. So:

use AppleScript version "2.5" -- macOS 10.11 or later
use framework "Foundation"
use framework "AppKit"
use scripting additions

set theFinder to (current application's NSRunningApplication's runningApplicationsWithBundleIdentifier:"com.apple.finder")'s firstObject()
set currApp to current application's NSRunningApplication's currentApplication()
set theApps to current application's NSWorkspace's sharedWorkspace()'s runningApplications()
set thePred to current application's NSPredicate's predicateWithFormat:"activationPolicy == 0 AND NOT (SELF IN %@)" argumentArray:{{theFinder, currApp}}
set theApps to theApps's filteredArrayUsingPredicate:thePred
repeat with anApp in theApps
	anApp's terminate()
end repeat

It’s not a need – more a convenience, in terms of avoiding repeated “current application’s”.

It’s generally safer to treat ASObjC properties as methods, so better to use localizedName(), with the parentheses.It doesn’t matter in the code you posted, but it can be a trap, so it’s a good habit.

Curiously, there are still some processes where System Events returns something different. AdobeAcrobat vs Acrobat Pro DC is one example. It looks like System Events uses the name of the executable in the app’s bundle.

It’s KniazidisR which asked for a code protecting the current application.
So I posted a code responding to its requirements.

For the Finder, you are right.
I missed that here is one language for which Finder is not spelled “Finder” but “访达”

----------------------------------------------------------------
use AppleScript version "2.5"
use framework "Foundation"
use scripting additions

-- Build an array of the localized names of the application Finder
-- Some languages are defined twice because they aren't named the same according to the running OS
-- Yvan KOENIG (VALLAURIS, France) dimanche 29 mars 2020  17:42:48
----------------------------------------------------------------

property |⌘| : a reference to current application

set theLanguages to paragraphs of "ar
ca
cs
da
de
el
en_AU
en_GB
en
es_419
es
fi
fr_CA
fr
he
hi
hr
hu
id
it
ja
ko
ms
nl
no
pl
pt_PT
pt
ro
ru
sk
sv
th
tr
uk
vi
zh_CN
zh_HK
zh_TW
Dutch
English
French
German
Italian
Japanese
Spanish"

set root to (path to application "Finder" as text) & "Contents:Resources:"
--set root to "resources YK:resources trois:Catalina:System:Library:CoreServices:Finder.app:" & "Contents:Resources:"
set fullTable to "InfoPlist.strings"

set allLocales to {"language" & tab & "CFBundleDisplayName" & tab & "CFBundleName"}
-- Now extract the values for every language available
repeat with usedLanguage in theLanguages
	set anURL to (|⌘|'s NSURL's fileURLWithPath:(POSIX path of (root & usedLanguage & ".lproj:" & fullTable)))
	set theDict to (|⌘|'s NSDictionary's dictionaryWithContentsOfURL:anURL)
	if theDict is not missing value then
		set val1 to (theDict's valueForKey:"CFBundleDisplayName")
		set val2 to (theDict's valueForKey:"CFBundleName")
	else
		set {val1, val2} to {"??", "??"}
	end if
	set end of allLocales to (usedLanguage as string) & tab & val1 & tab & val2
end repeat

set theData to my concatlist:allLocales usingString:linefeed
-- save data to new file
set reportName to "Finder local name.txt"
set hfsPath to (path to desktop as text) & reportName
set targetFile to hfsPath as «class furl»
(theData's writeToURL:targetFile atomically:true encoding:(|⌘|'s NSUTF8StringEncoding) |error|:(missing value))

tell application "TextEdit"
	activate
	if exists window reportName then close window reportName
	open targetFile
end tell

#=====

on concatlist:theList usingString:d1
	set anArray to current application's NSArray's arrayWithArray:theList
	return (anArray's componentsJoinedByString:d1) -- as text
end concatlist:usingString:

#=====

Here without my special path I get:
language CFBundleDisplayName CFBundleName
ar Finder Finder
ca Finder Finder
cs Finder Finder
da Finder Finder
de ?? ??
el Finder Finder
en_AU ?? ??
en_GB ?? ??
en ?? ??
es_419 Finder Finder
es ?? ??
fi Finder Finder
fr_CA ?? ??
fr ?? ??
he Finder Finder
hi Finder Finder
hr Finder Finder
hu Finder Finder
id Finder Finder
it ?? ??
ja ?? ??
ko Finder Finder
ms Finder Finder
nl ?? ??
no Finder Finder
pl Finder Finder
pt_PT Finder Finder
pt Finder Finder
ro Finder Finder
ru Finder Finder
sk Finder Finder
sv Finder Finder
th Finder Finder
tr Finder Finder
uk Finder Finder
vi Finder Finder
zh_CN 访达 访达
zh_HK ?? ??
zh_TW Finder Finder
Dutch Finder Finder
English Finder Finder
French Finder Finder
German Finder Finder
Italian Finder Finder
Japanese Finder Finder
Spanish Finder Finder

because several lprojs are missing (or named differently) in High Sierra.
In Mojave or Catalina, the double question marks would be replaced by “Finder” and the late seven descriptors would be ?? ??.

No, in message #13, you may see that I was forced to use the properties and this is why I was puzzled.
I don’t understand why it behaved this way because, as I wrote in an other message, after a copy/paste process it was no longer required.

Yvan KOENIG running High Sierra 10.13.6 in French (VALLAURIS, France) dimanche 26 avril 2020 12:07:18

Running under an older version of the OS, with Satimage scripting addition installed?

I run 10.13.6 but Satimage isn’t available.
What is really puzzling is that after a simple copy/paste in a blank script I got a script were the properties weren’t required.
At least, now I know what to do if the oddity strikes again but I would be more satisfied if I had a valid explanation for this behavior.

Yvan KOENIG running High Sierra 10.13.6 in French (VALLAURIS, France) dimanche 26 avril 2020 14:44:47