Check the last time an application was opened

danshaw
Contributor II

Curious if anyone knows of a way to via script, command line, or JSS to check the last time an application was opened/run. After some digging on the inter-webs, I couldn't find much. Only way I read was through the finder using the "Date Last Opened" category.

We have a few computers out there that have 2 versions of some applications and rather than going to each of the users and asking them, I wanted to see if the older version had been used. If not, I could delete it.

The JSS also has the Application Usage Logs, so maybe the data is there, but not sure how I'd use that.

2 ACCEPTED SOLUTIONS

seansb
New Contributor III

Hi,

In terms of the information the JSS has: it records the name, version, and amount of minutes in the foreground and the number of minutes open for an application for each and every day. I believe this information is available via the API in versions 9.93+. So technically speaking you could parse through that information date by date and look for your specific application you want and grab the date it was last recorded as being open (though how far back you can search depends on your log flushing schedule).

If you're looking for a more surgical approach, I use the mdls command to indicate the last date a specific app was opened. So you could throw that in an extension attribute and pull it that way.

mdls "$mdlsDirectoryOrFileName" | grep kMDItemLastUsedDate | awk '{print $3}'

So $mdlsDirectoryOrFileName could be a specific app or directory. This query will result in a date like: 2016-09-29.

Hope that helps!

View solution in original post

mm2270
Legendary Contributor III

Yes, use mdls for this as mentioned above. Just a few things. First, you don't need to use grep to pull that data. mdls has syntax to pull just that piece of information. Plus, I would pull column 3 & 4 with awk, not just $3, since the time may also be important.

mdls "$mdlsDirectoryOrFileName" -name kMDItemLastUsedDate | awk '{print $3,$4}'

As a bonus, the result will actually be in a perfect format for a 'Date' formatted Extension Attribute. Something like:

2016-09-30 14:48:53

View solution in original post

21 REPLIES 21

seansb
New Contributor III

Hi,

In terms of the information the JSS has: it records the name, version, and amount of minutes in the foreground and the number of minutes open for an application for each and every day. I believe this information is available via the API in versions 9.93+. So technically speaking you could parse through that information date by date and look for your specific application you want and grab the date it was last recorded as being open (though how far back you can search depends on your log flushing schedule).

If you're looking for a more surgical approach, I use the mdls command to indicate the last date a specific app was opened. So you could throw that in an extension attribute and pull it that way.

mdls "$mdlsDirectoryOrFileName" | grep kMDItemLastUsedDate | awk '{print $3}'

So $mdlsDirectoryOrFileName could be a specific app or directory. This query will result in a date like: 2016-09-29.

Hope that helps!

mm2270
Legendary Contributor III

Yes, use mdls for this as mentioned above. Just a few things. First, you don't need to use grep to pull that data. mdls has syntax to pull just that piece of information. Plus, I would pull column 3 & 4 with awk, not just $3, since the time may also be important.

mdls "$mdlsDirectoryOrFileName" -name kMDItemLastUsedDate | awk '{print $3,$4}'

As a bonus, the result will actually be in a perfect format for a 'Date' formatted Extension Attribute. Something like:

2016-09-30 14:48:53

danshaw
Contributor II

Guys this is perfect, thank you. For anyone else that stumbles upon this, you will have to target the binary of the program to get this info otherwise you'll get a "NULL" if targeting the containing .app package itself.

For example:

mdls "/Applications/VLC.app/Contents/MacOS/VLC" -name kMDItemLastUsedDate | awk '{print $3,$4}'

demery
New Contributor

Is there a way to put this script in JAMF and retrieve the output? I have about 500 laptops I need to check for Adobe products.

joshk
New Contributor II

@demery Create an extension attribute with the name "Adobe [PRODUCT] Last Accessed" and then have it populated by a script that runs the mdls command referenced above. IE.

#!/bin/sh
mdls "/Applications/Adobe [PRODUCT]/Contents/MacOS/[binaryname]" -name kMDItemLastUsedDate | awk '{print $3,$4}'

You'll need to put the actual name of the application and path to the application binary but that should get you most of the way there.

Note: If you're collecting this information for an Adobe audit you likely won't need to go through the effort as they will usually provide a binary that will collect all of the adobe products on every machine in your environment.

mm2270
Legendary Contributor III

Hi folks. Since this thread bubbled up to the top, I wanted to post a word of caution. I don't think using the binary inside the "MacOS" folder is wise. It seems to give pretty inaccurate results for me. This is on 10.12.6, so I'm not sure if it's the same for 10.13.x.

For example, here is what Safari looks like on my own Mac when I run it using the full path to the binary and not the app bundle.

$ mdls /Applications/Safari.app/Contents/MacOS/Safari -name kMDItemLastUsedDate | awk '{print $3,$4}'
$ 2017-07-15 05:06:06

Here it is with just the app bundle path:

$ mdls /Applications/Safari.app -name kMDItemLastUsedDate | awk '{print $3,$4}'
$ 2018-02-24 20:45:12

I happen to know for a fact that the latter one is correct. I recently rebooted my Mac (on the 24th) and launched Safari again, which is reflected in the date. The former one shows a date from way back in July of 2017. Way WAY off. I would not go with the full path if I were you folks. Just sayin'

thellum
New Contributor II

Interesting.
I wonder if there is a character limit on paths?
When I run the following on my own machine:

!/bin/sh

mdls "/Applications/Adobe InDesign CC 2018/Adobe InDesign CC 2018.app" -name kMDItemLastUsedDate | awk '{print $3,$4}'
mdls "/Applications/Adobe Illustrator CC 2018/Adobe Illustrator CC 2018.app" -name kMDItemLastUsedDate | awk '{print $3,$4}'
mdls "/Applications/Adobe Photoshop CC 2018/Adobe Photoshop CC 2018.app" -name kMDItemLastUsedDate | awk '{print $3,$4}'

...I get (where the hamburger is me):
~ :hamburger: #!/bin/sh
~ :hamburger: mdls "/Applications/Adobe InDesign CC 2018/Adobe InDesign CC 2018.app" -name kMDItemLastUsedDate | awk '{print $3,$4}'
2018-06-04 18:12:20
~ :hamburger: mdls "/Applications/Adobe Illustrator CC 2018/Adobe Illustrator CC 2018.app" -name kMDItemLastUsedDate | awk '{print $3,$4}'
CC 2018/Adobe
~ :hamburger: mdls "/Applications/Adobe Photoshop CC 2018/Adobe Photoshop CC 2018.app" -name kMDItemLastUsedDate | awk '{print $3,$4}'
2018-05-29 15:01:27

Note what happens to Illustrator. This output remains the same regardless of order in this list of three. If I change the {print} values, I get more or fewer characters for "Illustrator" in the output, but certainly not the date and time stamp anticipated. Is there something weird with Illustrator not allowing this?

msmith80
New Contributor III

Is there a way to format this in another date format?

ryan_ball
Valued Contributor

Edited, maybe not.

mm2270
Legendary Contributor III

@msmith80 What exactly do you mean by another date format? Can you give an example of how you'd like to see the date formatted?

andy_granger
New Contributor III

@thellum I know this is an old thread, and I'd like to think you figured this out a long time ago. However, the reason you're getting the odd result on Illustrator is because the Adobe seems not to have control of its individual application teams in terms of a consistent application naming convention. Observe:

/Applications/Adobe Acrobat DC/Adobe Acrobat.app
/Applications/Adobe Illustrator CC 2019/Adobe Illustrator.app
/Applications/Adobe InDesign CC 2019/Adobe InDesign CC 2019.app
/Applications/Adobe Photoshop CC 2019/Adobe Photoshop CC 2019.app

I know you were asking about the CC 2018 versions of these applications, but this inconsistent naming convention among the applications is internally consistent with itself back through many major versions of these applications. Acrobat and Illustrator seem never to have the "DC" or "CC 20xx" appendages, respectively.

The reason you were getting that odd result when piping the output of the mdls command through awk when run against what you though was the correct path to the Illustrator app is revealed if you do the same thing but don't pipe it through awk:

$ mdls "/Applications/Adobe Illustrator CC 2019/Adobe Illustrator CC 2019.app" -name kMDItemLastUsedDate
/Applications/Adobe Illustrator CC 2019/Adobe Illustrator CC 2019.app: could not find /Applications/Adobe Illustrator CC 2019/Adobe Illustrator CC 2019.app.

Since the awk command is being asked to print only the third and fourth elements of the data given to it, and by default awk assumes elements are separated by spaces, the third and fourth elements of that error message (as delimited by the spaces in that string) are:

CC 2019/Adobe

"CC" is the third element, and "2019/Adobe" is the fourth element.

Naturally if you provide the correct path to Illustrator, it works as expected:

$ mdls "/Applications/Adobe Illustrator CC 2019/Adobe Illustrator.app" -name kMDItemLastUsedDate | awk '{print $3,$4}'
2019-05-10 19:53:22

This is all simply meant as a cautionary tale never to trust Adobe—or really any large software developer—to behave the way you think they should: Verify reality before committing to a plan.

I'm sure you got this figured out long ago, so this was really more of a self-serving exercise for me to solidify it in my own mind. But maybe this will help someone.

Tigerhaven
Contributor

0e3ce13aad2f464d974a62d2951c62cd
hi team I am running the script :
mdls "/Applications/Microsoft Teams.app/Contents/MacOS/Teams" -name kMDItemLastUsedDate | awk '{print $3,$4}'
could not
and result is coming to Could Not , any thoughts will be helpful.

Kunal V

mm2270
Legendary Contributor III

@Tigerhaven You have both double quotes around the path and backslashes within it. Pick one. Don't use both. Meaning, if you use double quotes, drop the backslashes in front of the spaces. If you use backslashes, drop the double quotes.

You're getting "could not" because the full message being returned is:

/Applications/Microsoft Teams.app/Contents/MacOS/Teams: could not find /Applications/Microsoft Teams.app/Contents/MacOS/Teams.

Since the path being entered is wrong.

Tigerhaven
Contributor

@mm2270 Thanks much appreciated. it worked.

Kunal V

thellum
New Contributor II

@andy-granger Thank you very much indeed for pointing out what should certainly have been obvious. I can now run last-run-checks on Adobe to determine usage and possible redistribution based on the results.
Many thanks, indeed.

shaquir
Contributor III

Thanks everyone for the info. I was able to create a EA using the info in this thread to monitor Sketch usage in our environment.

#!/bin/bash
#EA to check when application was last opened
#Sets the EA's Data Type to Date
#Shaquir Tannis 1-10-20
#6-9-20 added quotes around "$Application" to account for Applications with spaces in filepath

Application="/Applications/Sketch.app"
if [ -e "$Application" ]; then
lastOpen=`mdls "$Application" -name kMDItemLastUsedDate | awk '{print $3,$4}'`
echo "<result>$lastOpen</result>"
fi

Note, it will be different on an app to app basis, but I tested:
/Applications/Sketch.app/Contents/MacOS/Sketch -> resulted in the app creation date
/Applications/Sketch.app -> resulted in the actual time the app was opened.

Edited to add quotes around variable

ateazzie
New Contributor III

@shaquir I like your solution a lot, I tried it with Photoshop CC2018 but it doesn't show up on ht device page.
Any idea why?

shaquir
Contributor III

Hi @ateazzie ,
Looks like I forgot the number 1 scripting rule "When in doubt quote it out". I missed a quote around the "$Application" variable.
This should work for you:

#!/bin/bash
#EA to check when application was last opened
#Sets the EA's Data Type to Date
#Shaquir Tannis 1-10-20
#6-9-20 added quotes around "$Application" to account for Applications with spaces in filepath

Application="/Applications/Adobe Photoshop CC 2018/Adobe Photoshop CC 2018.app"
if [ -e "$Application" ]; then
lastOpen=`mdls "$Application" -name kMDItemLastUsedDate | awk '{print $3,$4}'`
echo "<result>$lastOpen</result>"
fi

tlarkin
Honored Contributor

love the Spotlight stuff here, just a couple of quick tips on this exact subject

mdls has a switch called -raw (insert ODB reference here) that just gives you the raw meta data, so there is no need to pipe to awk or grep, etc.

% mdls /Applications/Safari.app -name kMDItemLastUsedDate -raw
2020-06-02 21:41:38 +0000

You can also bridge ObjC with Python or use Swift to access the APIs natively, as mdfind and mdls do have some restraints since they are binary developed by someone on how they think it should be used.

There is also a binary called lsappinfo which displays some useful and interesting data, see man page for the full details. Note, the app must be running for lsappinfo to get data, as it only actually gets info from running apps via launch services.

 lsappinfo info "Safari"
"Safari" ASN:0x0-0x59a59a: 
    bundleID="com.apple.Safari"
    bundle path="/Applications/Safari.app"
    executable path="/Applications/Safari.app/Contents/MacOS/Safari"
    pid = 77051 type="Foreground" flavor=3 Version="15609.2.9.1.2" fileType="APPL" creator="sfri" Arch=x86_64 sandboxed 
    parentASN="Spotlight" ASN:0x0-0x3b03b: 
    launch time =  7 seconds ago, 2020/06/09 21:26:51 ( 6.71718 seconds ago )

I literally had to launch Safari to get output because it wasn't running. This should tell if you the app is foreground or background and how long it has been running for.

warning - I did play around with lsappinfo a couple years ago to get it to launch an app and it crashed my Finder so use at own risk

-T

ateazzie
New Contributor III

thnx Shaquir

bizzaredm
Contributor

I know this is older but I wanted to share my attempt at complete EA for this.

Also, some system apps in modern (Monterey at least) macOS are not really in /Applications.  For example "/System/Applications/TextEdit.app" you can check by dragging an app from finder into a terminal window to see its true path. 



 

#! /bin/sh
# Template Version 3
# Description: This template is for finding "Last Launch" of an applications using the mdls comand.
# It will show the date that the app was last launched. Or never opened, or not installed.
# Read the comments below to modify for your usage


## Modify these Variables ## 

### App: Aqua Data Studio
### Date Added: October 29th, 2021

# Set path to "/Applications/" or to folder, do not use wild card.
APPLICATION_PATH="/Applications/"
# Make sure to include .app in the name
APPLICATION_NAME="Aqua Data Studio.app"


###########################################################################################
############################## DO NOT MODIFY BELOW THIS LINE ##############################
###########################################################################################

#Check to see if we have the app path and name set above

if [ -z "$APPLICATION_PATH" ]; then
  echo "APPLICATION_PATH not supplied."
  exit
fi

if [ -z "$APPLICATION_NAME" ]; then
  echo "APPLICATION_NAME not supplied."
  exit
fi



#This joins the App and the path, we keep them apart above for ease of use

APPLICATION_FULLPATH="$APPLICATION_PATH$APPLICATION_NAME"

#debug echo "$APPLICATION_FULLPATH"

#Check to see if the application exists, CD do the path, if fail "Path Missing"
if [ -e "$APPLICATION_FULLPATH" ]; then
		cd "$APPLICATION_PATH" || PATHCHECK="Path Missing"
		APPLASTUSED=$(mdls -name kMDItemLastUsedDate "$APPLICATION_NAME" | awk '{print $3}');
    else
	APPLASTUSED="Not Installed"
fi


#Check result of PATHCHECK, if missing, we assume its not installed...  however if /Applications is missing.. Bigger issues
if [ "$PATHCHECK" = "Path Missing" ]; then
   APPLASTUSED="Path Missing"
   echo '<result>'$APPLASTUSED'</result>'
   exit
fi

#echo "$APPLASTUSED"
 
case "$APPLASTUSED" in

  could | not | "Not Installed")
  
    	APPLASTUSED="Not Installed"
    ;;


  "(null)")
	APPLASTUSED="Never Opened"
    ;;
    
    "Path Missing")
	APPLASTUSED="Path Missing"
    ;;

  *)

    ;;
esac




#return result
echo '<result>'$APPLASTUSED'</result>'