Alternative to System Events to get process names and IDs ?

I am running a Do Shell Script in background. When invoked shell script returns its process Unix ID. Using Activity Monitor I’ve found that the process name is usually Python. But, when using System Events to get the process name I get the “Can’t get process” error. When I used System Events to list all processes, I found Python was missing [it was still running]. It seems that System Events doesn’t provide access to all processes.

So, I guess I need another way to query processes. I have found the PS and TOP commands. Are there any other/better ways to query currently running processes with AppleScript ?

On a somewhat related matter, I can get the Unix ID of a process with System Events but, I can’t figure out how to get the process name if I have the Unix ID without looping through all processes to find the right one. For example, given that Safari is running:

tell application "System Events"
	set DL_process_id to unix id of process "Safari"
	get name of first process whose unix id is DL_process_id -- This works but seems excessive
	set DL_process_name to name of process id DL_process_id -- Error - Is there a way to make this work for Unix ID ?
end tell

Cheers.

Correct. System events will only return processes that are connected to the window server. Which are processes that behave like applications and agents (in general).

I’m not sure if it’s better but I wrote an OSAX named AppleScript-Toolbox (see my footer) that can list all BSD processes. Every process of every user will be listed in there including it’s path and parent process id by default but can return more properties of each process. The command is:

AST list processes

The correct syntax would be:

tell application "System Events"
	set DL_process_id to unix id of process "Safari"
	set DL_process_name to name of item 1 of (every process whose unix id = DL_process_id)
end tell

Seems to me the original syntax is fine – at least, it works here:

tell application "System Events"
	set DL_process_id to unix id of process "Safari"
	get name of first process whose unix id is DL_process_id
end tell

I’m puzzled as to why the OP regards it as “excessive”.

Me too. I was correcting his error line, not the “excessive” line.

Many thanks Folks,

I’ll have a look at the AST.

My “excessive” comment was due to a lack of AS education. I thought that “get name of first…” was going into a loop through all processes just to find one. It just seemed more complex than getting the name when given process id instead of Unix id, for example:

	set DL_process_id to id of process "Safari"
	set DL_process_name to name of process id DL_process_id

Cheers.

It does, but it does it always no matter how you get an object. The AppleEvent Object Model (AEOM) used for whose clauses, getting ranges, getting objects by index, etc… is designed in a time the multifinder was in development too. Osaxen were loaded by the OS and the current application context was the OS too. 60 events a minute was normal and it was a challenge to write a script in less as possible events (in most situations using AEOM was considered efficient).

With the transition to Mac OS X many things has changed, including AppleEvents. Things get better and things got worse. AppleEvents was copied to Mac OS X but it was implemented in a completely different context which causes all sort of exploits and even today Apple is still barricading but it made AppleScript much quicker and less buggy.

Yeah, it’s excessive but there is no other alternative and proposed alternatives aren’t that much better. The problem is that as an developer you don’t want to write a get and set command for all properties of objects. The AEOM does that for you. Writing a set and get command for each property value would be cumbersome in term of development and would discourage to make applications scriptable.

I think it’s fair to say that an one-for-all solution is rarely the most efficient or fastest one. They’re not designed to be more efficient or faster. It’s like with higher programming languages, they’re only designed for faster development, nothing else. That they are easier is a misconception as with AEOM.

I’d imagine that “get name of first process whose unix id is DL_process_id” does stop as soon as a matching item is discovered. On the other hand, the other version mentioned above — “set DL_process_name to name of item 1 of (every process whose unix id = DL_process_id)” — specifically mentions “every process” and so would continue to look for more, even though there are none.

AppleScript supports “id references”, so if you have the ‘id’ for a process, you can use an id reference to it, as in your example:

   set DL_process_id to id of process "Safari"
   set DL_process_name to name of process id DL_process_id

But there’s no equivalent reference in the language for a ‘unix id’, which is a property unique to System Events, so you have to use a ‘whose’ filter.

As long as it’s not done at the AppleScript end, it’s unlikely to matter: testing for a name match is likely to take only a fraction of time compared with assembling the list of processes to check in the first place.

First a list of all processes is created (AEList), then the AEOM will apply the whose clause with the given range on the AEList. The whose clause is executed first and then a filter of all processes is applied and the first process is returned from the AEList. AppleScript itself has no influence over this to make this more efficient because it has to make an AppleEvent object conform AEOM to be resolved correctly by the application. The drawback of AEOM, thus object specifiers, is that you can only have one “instruction” per object.

To write it all down the command is actually a serie of nested individual object specifiers who will be resolved one by one. First there are two object specifiers, first one is every process [1] and second the unix id value. These two specifiers are bundled within an compare specifier, which is the whose in AppleScript (nothing more than another object specifier). At last the compare specifier is inside in a range specifier (first process). Because the way objects are resolved, each object specifier is resolved individually and the whose clause and range specifier cannot work together.

[1] While get name of first process whose unix id is DL_process_id seems very efficient, AppleScript simply allows you to get rid of “redundant” code. The following two commands will send exactly the same object specifier to System Events and are therefore identical, even if the first line looks much more efficient.

tell application "System Events"
	-- code implies it is very efficient allowing to remove some code
	first process whose name is "finder"
	-- the above specifier is actually send like this
	process 1 of (every process whose name is "finder")
end tell

Hi DJ.

Thanks for the elucidation. It sounds crazy at first, but possibly makes sense at that level.

Many thanks everybody. Can’t say I understood everything but, well worth a read.

Cheers.