I am only now starting to learn how to use Regex to my advantage when creating macros, and have now spent the better part of the evening trying to figuring out a very specific problem: How do I make Regex return only the last match in a long list of numerous matches?
I am searching a log file wanting to capture the time code of the last time this app was launched. (Example of how this log file can be formatet at the bottom of this post.)
I've read this Log file into a variable and I am trying this 'Search using Regular Expression’ to give me the time code of the last time this app was launchd:
The problem is of course that the Regex string (\d+)-(\d+)-(\d+) (\d+):(\d+):(\d+) App starting only returns the timecode in front of the first match in this list, whereas I am interested in the time code in front of the bottom most occurrence of this match. Most of my regex google searches have pointed med towards Negative Lookahead, but I cannot make make sense of this, neither in practice (where to place the ?!) or through my logic understanding from the description of what Negative Lookahead is supposed to do, it does not seem right.
This is without a doubt only a result of my poor understanding of Regex, but I hope that maybe someone here can help me by pointing me in the right direction.
Example log file:
2023-07-07 20:55:31 App Quitting 2023-07-07 20:55:43 App Starting
2023-07-07 20:55:43 Random Data Noise
2023-07-07 20:55:43 Random Data Noise
2023-07-07 20:55:43 Random Data Noise
2023-07-07 20:55:59 App Quitting 2023-07-10 13:26:59 App Starting
2023-07-10 13:27:02 Random Data Noise
2023-07-10 13:27:02 Random Data Noise
2023-07-10 13:27:09 Random Data Noise
2023-07-10 13:27:09 Random Data Noise
2023-08-01 20:03:34 Random Data Noise
2023-08-01 20:03:36 Random Data Noise
2023-08-01 20:03:36 Random Data Noise
2023-08-01 20:03:36 App Quitting
Thank you for your reply! Your proposition makes perfect sense and I will try it out tomorrow
Only I kind of can’t believe that this is not a basic task to ask of Regex to handle. As I’d like to learn Regex better I’d also love any further information about if there is any way to get Regex to return only the last match of a list, or not.
Another thing I just realised is that the log file I want to extract the time code from is 77000 lines long(!) So I might also have to do some tests to check out different ways of doing this task, to see which method is taxing my hardware the least. Now I’m thinking I should find a way to read only the last five pages of the log file into the variable or something like that. But it’d be interesting hearing about other ideas on how to approach this problem as well!
You can use the shell command tail to extract the last chunk of a file into a variable. Still away from my desk, so search the forum for “execute shell script using tail” or similar?
Thanks once again! Been reading about the tail command and it seems like it should do allot of what I need.
I am now trying a Execute Shell Script action, 'with save result to variable' to read the last 100 lines into my variable in reverse order using this command:
tail -r -n 100 "/Users/$_user/Library/Logs/Appname/Log_example.log"
(Where Appname and Log_exampe.log is of course swapped out with actual app and file name.)
But with this directory formating I guess I reveal also my incompetence in command line, as I get the KM error message "No such file or directory" even though the directory/file I am pointing it towards very much exists (proven by the fact that I am able to read the entire file into a variable using the same directory).
Where in my attempt of pointing towards the right directory am I going wrong?
Would I first have to point towards the directory in a separat line in the Shell Script?
Well my guess is what doesn’t exist is a user called $_user
If you have a shell environment variable of that name that contains the actual username, then it won’t work since the KM shell is a vanilla shell with a barebones environment. This issue comes up at least weekly so have a look at the Execute a Shell Script action in the wiki action:Execute a Shell Script [Keyboard Maestro Wiki] and/or search the forum for “shell script problem” - there’s plenty of examples!
Also, have a look in the KM engine log file to see what errors are being reported - it might point you to the cause.
Regular expressions are extremely powerful and cool but they are often best used in combination with other tools (like with tail here). You could probably engineer a regex that would do what you need, using some of the more advanced features of regex syntax, but it would almost certainly be going the long way around. (If you’re curious, search for “negative lookahead” in a regex manual.)
By the way, one good way to learn regex on a Mac is to install the free version of the text editor app BBEdit. The manual’s chapter on grep search is a very well-written regex tutorial, and the app has lots of helpful features for designing and testing your regular expressions.
If you wanted to experiment with native KM actions ( bypassing both regular expressions and shell scripts ) you could, for example, try things like this:
I am definitely interested in this experiment! As I don’t really know coding it would almost always be my intuitive goal to solve any problem within the native KM actions!
But if I was to use the timecode for a time difference calculation I would still need to load the elements of the TC into separate variables (Y1, M1, D1, H1, Min1, Sec1 (to use a Time()-calculation)), right? For something Regex captures seems like the maybe best tool? (The original full task I set out to solve was to calculate the time difference between the last time the app was quit before being stated up again.)
Thank you for this explanation! Very useful to know that Regex is often best used in combination with other tools, and that it is not necessarily the do-it-all-tool for these kinds of tasks as I thought it would be.
Thank you also for pointing me in the direction of how I could best learn the tool better!
One of the good things about using a Keyboard Maestro native action to do this is that it is very clear to see what is going on, especially if the Macro doesn't work as expected or you want to edit at a future date. To get the value for the hour in this case is simple as the positions of those digits will be the same so you can use the same technique @ComplexPoint has shown. Also, you can rename a Keyboard Maestro Action, in effect adding in a note of what that Action is doing.
This solution makes allot of sense!
Only thing is that I have this (mis)conception that more separate KM-actions equates to a more processor taxing more time consuming macro. But I guess there’s no real difference between dividing it up into separate steps like this or not
(Run-time differences will be very small, whereas all the "I have spent the better part of the evening trying to figure out" time is far from insignificant)