What's Up With My Screenshot Saver Keyword-Tagger Workflow?

I think I'm not getting the variable into the shell script correctly. I expect it's wrong to assume the shell script should know what variables KBM has.

very new to this but that's what I suspect I'm doing wrong. Mind you, it's not even saving the screenshot to the desktop so there's something up there too.

here's the workflow as it stands.

Capture, Tag Keyword, Save.kmmacros (5.0 KB)

Have a good read of the Execute a Shell Script Wiki page. The variables section will show you how to get KM variables into your shell script (you've used a sort-of text token but you need something like "$KMVAR_myexiftags").

Also have a look at the Paths section. It's likely that your script is unable to even find exiftool because it won't be in one of your default paths. Easiest way to fix that is to go to Terminal, type which exiftool, hit Return, and use the output of that to explicitly call the utility in your shell script. For example, if you're on an M1 Mac and installed exiftool with brew, the output from which exiftool is probably /opt/homebrew/bin/exiftool. I think you're also missing a file name from the end of your file path.

More problematic is that you aren't actually doing anything with the screenshot! screencapture -ic puts the screenshot onto the clipboard (and you're instantly overwriting that with your "Prompt" action). You're saving the result of the script to thescreencapture, but the result is null because the script completed successfully. Take a look at man screencapture in Terminal to understand the options and see how you save the screenshot with a known name and path that you can then use later with the exiftool command.

Start by getting the screenshot and exif-editing (using a fixed tag) sorted in Terminal, then transfer that to KM, then change things to use variables for the file name and tags. That way you can more easily troubleshoot as you go.

1 Like

Remembered I had a machine with exiftool installed, and after a bit of playing:

Screenshot and Tag.kmmacros (5.6 KB)


I've munged a couple of things together, but it should be clear what's happening.

The first action sets the path and file name for your screenshot. I used a folder called "Screenies" on my Desktop -- change that to match your save location. And I used the same naming convention as the OS's built-in -- the %ICUDateTime% token produces the current date and time in the format specified, making it easy to add a timestamp that suits you.

We then take the screenshot interactively (-i), as you did, but write it to the file path we built in the first action, passing in the KM variable just as the Wiki page told us to.

When you do a multiple selection in "Prompt with List" there's a trailing return character which isn't there for a single selection, so search'n'replace returns for commas (to match your original workflow) then check to see if there's a trailing comma -- if there is, chop it off!

Then we get to the exiftool command. You'll have to change the path if you are on M1 silicon or have installed the utility elsewhere -- again, which exiftool in Terminal will give you the path to use here. Keywords need to be provided as a list else your multiple selection will be just one long, single, keyword -- since you've separated by commas we've used -sep "," to tell exiftool to do the same. We include the tags list as the variable $KMVAR_Local_myexiftags, again following the Wiki page, use -overwrite_original to change exiftool's default behaviour (remove that if you want both tagged and untagged versions of the file) along with -P to preserve timestamps (probably not needed here, but you never know). Finally we include our Local_filePath variable to tell it which file to edit -- we don't need an output path because we've already said to overwrite the original.

That's probably over-explained, but it's well worth checking each action's help page (click it's "Options" cogwheel and the "Help" item will take you to the action's Wiki page) so you can see how/why it works, and how you can change it to suit the way you want to do things.

And if my "explanations" have just added to the confusion -- ask!


Thanks so much for this @Nige_S

It works!

When you say

/usr/sbin/screencapture -i "$KMVAR_Local_filePath"

it's saying first; run the command "screen capture" which is found in the path "/user/sbin/screencapture"
I didn't even know there was a "usr" or a "sbin" let alone the fact that screen capture was sort of a command that had a place in the file system.

Anyway. Then it's saying it's interactive. And then it's saying the place to write it to. The shell script doesn't have to say "writeto:" it just assumes the last part of what you write is the file path.

I haven't checked the link yet but I guess using "$KMVAR_" and then the variable you've set is the way to tell the shell script to get the variable from the workflow.

So, at this point the file is written. And we do the exif thing.

You say " You'll have to change the path if you are on M1 silicon or have installed the utility elsewhere -- again, which exiftool in Terminal will give you the path to use here."

here's what I get:

(base) james@JVs-MacBook ~ % which exiftool

I think I have it right. Should I have it installed somewhere else? I can't remember whether I used brew or not.

When doing a variable btw do all spaces have to be filled with % signs? And could variable be with a lower case v or must it be V. And why isn't it Var?

Then we delete the final comma which is great. And then we're checking whether they end in a comma? I don't see the reason for that because there's no "else"

When the shell script says "with input from nothing" what sort of input would it take?

Then we invoke exif tool in the shell script again and tell it to separate the keywords by the comma.

And then tell it to add the tags to the subject. i presume exiftool is doing the heavy lifting and knowing that because it's separated everything out with a comma, it's applying the tags as separate tags.

Then the shell script (is it still exiftool doing this) overwrites the original with this.

Oddly the only thing not working with this is the "I did it"
I changed it to display text in a window but still, nothing. Weird.

How can I best use this macro to also work on images I select in the finder?

Should I turn it into a macro that I can invoke from another macro*. I forget what they're called.

I presume the workflow would be different for finder files, however, since they would not need to be written etc so perhaps I can take the middle part of the macro and turn it into a template macro* I can call upon. To keep it as efficient as I can.

*I know there's a word for this I just can't remember it.

Anyway many many thanks. Learned loads.

Not quite -- this is a single command in a (one line) shell script. If you do man screencapture in Terminal you'll see screencapture's man (manual, or "help") page, where it tells you that you use the command, then any arguments you want, and finally the file to write the image to.

Then you can use the same exiftool command as I did. The location of installed utilities will depend on how you install them and, if done with homebrew, whether you are on Intel or Apple silicon. So it's always worth checking with which program_name in Terminal.

The % symbols denote a Keyboard Maestro token -- nothing to do with spaces. To use a variable in text you use the %Variable%VariableName% token (while can use the shorter %VariableName% form that can cause problems in certain situations, so it's good practice to use the explicit log form).


...can be read as "get the contents of the variable Local_myexiftags, and if that ends with , do something. In this case, if it ends with a comma then delete the last character -- the comma we don't want. There is no "otherwise" -- if there's no trailing comma we can leave the text as it is.

Neither the "execute..." nor the "otherwise..." parts of an "If" action a re required to have anything in them. Obviously it's a pretty pointless action if neither of them do :wink: But whether one, the other, or both do will depend on the logic of your macro. To take a real world example from right now: if I am trying to decide what to do next it might be

if it is raining
then -- which is "execute the following actions" in KM
   stay indoors
else -- which is "otherwise execute..." in KM
   go outside
end if

But if I have to go outside I only need to decide about putting on a coat:

if it is raining
   put on coat
end if
go outside

In this case there is no input, so it is literally "from nothing". But some utilities/scripts take an input (often from "standard input", which is the keyboard) and produce output (often to "standard output", like your Terminal window). The usual example is the "transpose" utility tr -- so to convert all the "a"s to "A"s in "banana":

echo "banana" | tr "a" "A"

...and you can do similar in KM with
...so tr takes input from the text field and outputs the result to a window.

Again, this is a single command shell script -- everything here is being done by exiftool, these are just the commands and arguments to make it happen how we want.

Using a sub-macro/routine in multiple places is more about consistency and "editing efficiency" (if you need to make a change you only have to do it once, in one place) and it's generally less efficient at execution time. It might be worth doing here, so that you keep all your tags in one easily-editable list -- although I'd probably just do one macro with two different triggers (one for screenshot and one for Finder-selected) and branch depending on the trigger used (see the %TriggerValue% token).

The "selected files" version will be easy to write by stealing from this version (hint: the "For Each" action can use the "Finder's Selection" collection). Have a go at writing it yourself, then think about how you could combine the two versions into one.

And remember the Forum guideline -- if you've spent 5 minutes looking something up and are still stuck, shout for help!

1 Like

But in that case why the if statement at all why not simply say

**put on** coat

I'm working on the two different triggers with the same macro. Thanks SO much for all your help Nige

@Nige_S I'm back ....

Well I worked out the trigger token thing. I'm now having a problem with the finder files though.
Here's what I've got;

Screenshot and Tag New.kmmacros (10.8 KB)

I think KBM is picking up the finder files I've selected though I haven't checked that come to think of it.
I think the problem I'm having is when it comes to overwriting them or saving them to their original file path. I can't work out how to tell KBM where the file path is.

There must be a way with KBM but I tried using the exif tool and it told me

%%d	- original file directory (including trailing "/" if necessary)
%%f	- original file name (without the extension)
%%e	- original file extension (not including the ".")
%%c	- copy number (output files only)

So I tried that but no dice. I'm stuck!

Because if it isn't raining I don't need a coat, and will get too hot if I put one on.

Sometimes your logic is "do this or do that, depending on this test". But sometimes it is "maybe do this, depending on this test".

Perhaps a more obvious example -- making a cup of tea. You always boil the kettle, but sometimes there's enough water and sometimes you need to fill the kettle up. You could write that as:

if kettle is empty
   fill kettle
   boil kettle
   boil kettle
end if

...or as:

if kettle is empty
   fill kettle
end if
boil kettle

Because boil kettle is something are going to do regardless you can move it outside the if statement, leaving an empty otherwise.

Good practice is to put a "Do nothing" or "Deliberately empty" comment in the otherwise empty otherwise, to make it clear that it is deliberate. I always forget :frowning:

You already have the file path -- it's in the SelectedFinderFiles variable of the "For Each" action. That's what this variation of "For Each" does -- think of it as "for each file path in the collection 'selected files in the Finder', do these things". So use SelectedFinderFiles in "Execute Shell Script" action in the same way as Local_filepath in the other macro.

Ignore those exiftool flags -- they're for setting an output file different to the input, but you want to write tags to the original file.

1 Like

Done. And it works ... sort of.

I'm now using a named clipboard with my keywords because I couldn't work out how to just use the one. But I think this works.

  • For the finder selection it doesn't loop back and do every selected file; just the one.

  • For the finder selection I want to tell it to do one of the following

  • save the file to ~/Pictures/Images/Tagged and delete the original

  • move the file to ~/Pictures/Images/Tagged and delete the original

I tried adding in a move file command but it didn't work. Not sure why.

I did add colours to make it easier for me to understand though.

Screenshot and Tag 3.0.kmmacros (11.6 KB)

Scratch that I think I messed the whole thing up.

You've put your Shell Script actions inside your "If... tags end with a comma" block -- it probably works fine for multiple tags but fails with a single?

When macros grow as you add more features, it's good to take the occasional step back and re-evaluate. We've gone from:

screenshot to a file
prompt for tags
tag the file


   screenshot to a file
   prompt for tags
   tag the file
   prompt for tags
   tag the selected files
   move them
end either_or

Hmm... We're doing the "prompt and tag" bit twice, making our macro more difficult to read and more difficult to consistently edit. Perhaps if we re-arrange things a bit...

   screenshot to a file
   put file path in a list
   put paths of selected files in a list
end either_or
prompt for tags
tag the files in the list
move them

..which doesn't look much better -- but you'll see we're only writing the complicated bits once, whichever route we take. So:

Screenshot and Tag Refactored.kmmacros (16.8 KB)


This is not fully tested -- no exiftool on this machine...

You'll see we've pretty much followed the pseudocode above. The only tricky bit is the "Move" action, because you are now saving your screenshots into the correct place and they can't be moved to where they already are -- by default, KM will display an error notification and abort the macro.

One easy way round that is to save your screenshot to the Desktop as before, and tag/move them like everything else -- boring! Or you could handle the error with "If... trigger value contains 2 then ignore and continue" so any errors on screenshot files are passed over -- but what if one of Finder selection files was already in your "Tagged" folder?

So we've made use of the %ActionResult% token. When the "Move" action is successful, %ActionResult% is "OK" -- if it is not "OK" then something went wrong. We're expecting errors for screenshots and don't want to react to those, so we also check %TriggerValue%. So "if the Move action failed and we aren't doing a screenshot, add the failed file's path to a variable so we log it". To make this work you do have to go into the "Move" action's "Options" cogwheel and turn off "Failure aborts macro", I also turned off "Notify on failure" to stop pointless Notifications.

And when the macro completes we give a "Done!" notification or, if there were errors, show the failed paths in a dialog. Simples!

I love that you added a system beep!

Thank you once again.

I have an issue now where it works perfectly in with screecapture but when I use it on selected finder files, it tags the pictures but doesn't moving the them. Nor is it giving me an alert, beep or error.

Here's what I managed to get from the log.

023-03-15 19:04:55 Execute macro “Screenshot and Tag Refactored” from trigger Duplicate Macro Palette
2023-03-15 19:04:57 Action 13437473 failed: Execute a Shell Script failed with script error: Error: File not found - ^
Error: File not found - ,
2023-03-15 19:04:57 Execute a Shell Script failed with script error: Error: File not found - ^
Error: File not found - ,. Macro “Screenshot and Tag Refactored” cancelled (while executing Execute Shell Script).
2023-03-15 19:05:17 Execute macro “Screenshot and Tag Refactored” from trigger Duplicate Macro Palette
2023-03-15 19:05:19 Action 13437473 failed: Execute a Shell Script failed with script error: Error: File not found - ^
Error: File not found - ,
2023-03-15 19:05:19 Execute a Shell Script failed with script error: Error: File not found - ^
Error: File not found - ,. Macro “Screenshot and Tag Refactored” cancelled (while executing Execute Shell Script).
2023-03-15 19:06:30 Execute macro “Screenshot and Tag Refactored” from trigger The Hot Key ⇧⌘2 is pressed
2023-03-15 19:06:35 Action 13437473 failed: Execute a Shell Script failed with script error: Error: File not found - ^
Error: File not found - ,
2023-03-15 19:06:35 Execute a Shell Script failed with script error: Error: File not found - ^
Error: File not found - ,. Macro “Screenshot and Tag Refactored” cancelled (while executing Execute Shell Script).

Could it to do with the fact that the Cmd-Shift-2 invokes a pallet for me?

Oh. and it's still only tagging the first photo. Maddening!

I'm surprised it's even tagging them -- those logs show an error in the shell script. Not having exiftool on my work machines I used your action, without testing it. Yours starts

/usr/local/bin/exiftool ^ "," 

...and I'm wondering -- what does the ^ do?

This one goes back to my original

/usr/local/bin/exiftool -sep "," 

version, and tests OK now I'm back home.

Screenshot and Tag Refactored.kmmacros (16.9 KB)


1 Like


If you have any extra time I have some tweaks/ feature creep that I'm having trouble figuring out. Three, in fact.

The first is the ability to add a Keyword/Tag to the named clipboard FROM the "Prompt with List"

It has a + button on the left hand side but it doesn't do anything that I can see.
The action, itself, has options but none of them seem to offer the option of adding Keywords if they don't appear. Which means I must go back into KBM to update the list every time which is laborious.

The second thing is to get the prompt to pop up where my cursor is at the time. There's an option for this with a pallet but not with the list prompt that I can see.

I may be overthinking this; i.e there may be a very simple way to do it.
I've realised that on many occasions I want to screencapture but the image is not one I want to keep for very long. I want it on the desktop for a bit but i don't want to keyword and move it.

I figure I'd like to use the same capture area combo I've been using for this too. But instead of typing anything into the prompt, I'd like to simply Esc out of it.

I've tried adding an "If" statement after the macro has prompted with the list as I think that would be the moment to decide whether I want to tag it or not. I figure I could say:

  • if I press Esc then
    • save screencapture to desktop
    • end
  • else
    -continue with the rest of the macro

The problem is that there's only a keyboard button up or down key condition. Which would mean I'd have to be pressing it at the time.

Perhaps I could (I say I but it's you who's doing the heavy lifting and me who's learning so let's go with we); Perhaps we could add .... now I've forgotten what I was going to suggest.

The problem with "Esc" being down is that the else has to be up and I'm not certain what happens when after pressing the Esc key it comes back up.

I have a feeling the answer isn't an If then Else statement but something i don't know of. Perhaps setting a variable for "Esc key has been pressed" ?

Or perhaps there's an action that means Esc automatically exits the sub-routine and does something else; although I guess that's an If, Else statement!

No -- it only lets you pick from a list.

Because you are using a named clipboard, write another macro that lets you add new lines to that. Look at the "Set Clipboard to Text" action and the %NamedClipboard% token.

The "Prompt with List" dialog is a KM engine window. Look at the "Set Next Engine Window" action and the MOUSEX() and MOUSEY() functions. Think about using Calculations to offset the position a bit so your pointer is over the first list item rather than the top-left corner of the dialog :wink:

Note that once you reposition the prompt like that, the next engine window will also be in that position. I don't know if a way to get the last engine window position so you can reset -- gurus?

Simplest way? Use the built-in macOS screenshot command instead of your macro when you want "standard" behaviour!

Otherwise -- hitting Esc to cancel the dialog cancels the action and, like most KM action's, the default on-error behaviour of "Prompt with List" is "abort and notify". So you've got two routes:

  1. Change the options on the action to not abort or notify, then test Local_myexiftags and branch according to whether it has or hasn't been set
  2. Leave the options as they are and put the "Prompt" in a "Try or Catch" action, which suppresses standard error behaviour so you can deal with it. You'd then put your tag-and-move in the "Try" block, and any actions you wanted to take on a "no tag" screenshot in the "Catch" block.

As always -- give some or all of the above a go, see how you get on, shout out if you get stuck. And duplicate your currently-working version first, so you've a good copy to go back to if needed!

Brilliant. Got the prompt window working. I put a "Set Next Engine Window" before the macro begins and then a second one under the else action so that can do different things with both of them.

I do have a question about e "Set Next Engine Window" though. Is there a simple way of avoiding the boundaries of the screen?

For instance, I can ask the window to appear where my mouse is but if it's at the side of the screen it can cause the window to be half off screen. I can say -200 for x and y but then I'm left with the same problem with the right hand and top of the screen. Does KBM have a way to handle that?

I suppose I could do it with a sub macro with pixel-counts and rules but I can imagine spending hours trying to get that right. And on a different display it'd be for nought. Unless I could use %.

Had trouble with; Try or Catch and aborting so I did a bit of a dumb hack and added a keyword that captures it. Using EXIFtool still even though it's not doing anything because I didn't know how else to do it .

You'll also lose any consistency -- the prompt will be somewhere near your pointer, but you'll always have to go hunting. Given that, how about approaching it from the other direction? Put %CurrentMouse% into a variable, spawn the prompt in the middle of the screen, move the pointer to the correct position. At the end of the macro, put the pointer back to where it was using the values in the variable.

Have a look in the manual at the bit about variable scoping. Tags is a "global" variable, which will persist across runs of your macro -- if you set it to ~d this time it will still be ~d next time, unless you remember to unset it. Whereas "local" variables are created afresh with each new instance of your macro.

You don't need it anyway. If you cancel the prompt then Local_myexiftags will be empty -- test for that instead. That "Execute Shell Script" is doing nothing except (maybe) pointlessly re-writing your image file over itself. Just delete the action, leaving the "Move" action there to move the file from your "default" directory on to the Desktop.