Execute Shell Script Issue: Terminal Yes, Macro No

I seem to be missing something obvious. I have a text file that I want to replace a line in. The line is "LastContacted: " or "Last Contacted: YYYY-MM-DD"

Here's the script, run as "With input from Nothing" and "Ignore results"

sed -i '' 's/.*LastContacted.*/LastContacted: %Variable%LocalReadableDate%/' %Variable%LocalPersonFilePath%/"%Variable%LocalSelectedPerson%.md"

When I run the macro, the line is not updated. When I display the command in a text window, copy and run it in a terminal, the line is updated in the text file .

Any ideas?

Sure. I have an idea. You can't use %Variable%% syntax in an Execute Shell Script action. Read this long page:

https://wiki.keyboardmaestro.com/action/Execute_a_Shell_Script

That may not be the only mistake. For one thing, your quotes aren't balanced. I don't know how this could work in a Terminal window at all without balanced quotes. Moreover, this line would never work in a Terminal because Terminal doesn't know how to deal with tokens.

Also, you haven't shown your code. Even when you fix this command, you also have to have code in your macro that replaces the string. You aren't showing the code. I'm not sure if you have code to address that or not.

Yes -- don't shell out, do this with "Search and Replace File" action instead, where a) it'll be easier to manage tokens and paths, and b) you won't incur the overhead of shell instantiation.

If you do want to use sed then, as @Airy says, you'll need to pass in your variables properly -- text tokens aren't processed within a "script" text field. So it would be something like:

sed -i '' 's/.*LastContacted.*/LastContacted: ${KMVAR_LocalReadableDate}/' ${KMVAR_LocalPersonFilePath}/"${KMVAR_LocalSelectedPerson}.md"

(I think your quotes are OK -- it's hard to see when not code-blocked, but that first " is actually two 's.)

Thanks guys. I started with search and replace and couldn't get it to work :frowning:

I knew sed would work in the terminal, well, if I pass in the variables properly and was being a bit lazy.

On the search and replace front, here's what I tried.

Search file: /my_path/%Variable%LocalSelectedPerson%.md

for regex ignoring case
^.*\bLastContacted\b.*$

and replce with
LastContacted: %Variable%LocalReadableDate%

to source

The file is "fname lname.md" with a space inbetween. I tried adding quotes around it, no difference. I also ran a get file attribute action on "/my_path/%Variable%LocalSelectedPerson%.md" just to make sure it was finidng the file and it did.

The idea is to replace the entire line, as sometimes there's a date and sometimes it's blank. In regex101.com it matches fine, but the files doesn't get updated.

I appreciate the help on this one.

I ended up writing the command out to a file and just ran that with the Execute Shell Script action.

Passing in via KMVAR almost worked, but the field was updated with the text $KMVAR_LocalReadableDate instead of replacing it with the variable value. I don't think it liked being inside the single quotes. The other two worked fine (path & file) and it updated the correct file.

Oops. I'm usually good at reading for typos, but that was my mistake.

Because the entire file is "the string", so your anchors are the beginning and end of the file, but . doesn't match a new line -- so you'll only get a match when the first line contains "LastContacted". It works in sed because that treats each line in turn as the string to test.

Easy enough fix -- use the "multi-line" switch (?m) so that each line is considered individually:

(?m)^.*\bLastContacted\b.*$

You may also want to change "All Matches" to "First Match".

You could also get round it with the (?s) switch instead, so that . includes EOL characters, but you'll have to capture and re-use the not-to-be-replaced parts or you'll nuke most of the file!

Search: (?s)^(.*\b)LastContacted\b(.*)$
Replace: \1LastContacted: %Variable%Local_theDate%\2

...which could take some memory on larger files (and you'll have to be careful if the string appears more than once).

Thanks- that's a rather important detail :slight_smile: I'll play around with this over the weekend. The files are really small and will remain so, no worries on performance.

The first, (?m), method is probably the "correct" method in this case -- particularly in KM as it allows you to take advantage of the "All/First/Last" options in the action's settings.

The second, (?s), method will (I believe) only match the last occurrence of the pattern in your string -- though you could make it the first match by using a non-greedy match on the first pattern (.*?\b) (untested, so verify for yourself).