How to apply regex search / replace to every line on clipboard?

I am trying to do a regex search/replace on all hits in my clipboard. I saw in the docs that I need to do a forEach action, which I've tried, but it's still not working right.

I'm using KM 9.0.6.

Here's a simple example: I want to remove the '- ' from the front of all lines of text. i.e., I want to transform this:
image

Into this:
image

But what's happening is that only the first match is getting fixed, like so:
image

Here's my KM action—what am I doing wrong?
image

Your example of results ADDS an extra line after line 1. Is that intentional?

Assuming all you want to do is replace the leading "- " on each line with nothing, then that is quite easy.
You do NOT need a For Each Action. Just one Search and Replace action:

Search (using Regular Expression) For:
(?m)^-\h*

Replace With:
(leave it blank)

For Regex Details, see: regex101: build, test, and debug regex

Here's the KM Action:

image

ACTION: Search and Replace Using RegEx Action (v9.1d1)

Download

Search and Replace.kmactions (551 B)
NOTE: Before you import this ACTION, open the Macro and select the Action after which you want this action.
Double-clicking or Importing an Action will insert the Action just below the selected Action in the current Macro being edited.

Example Results

image

In terms of getting the For Each approach to work:

The point to remember is that at each stage, the forEach variable just contains a copy of that one line.

It's not a reference to the original line (just a separate copy), and no changes you make to the content of that variable will be copied back to the source line.

All you need to do is:

  • start the process with an additional accumulator variable which is initially empty
  • with each forEach line, append the modified copy, followed by a linefeed, to the accumulator.
  • at the end, when the bullet-stripped accumulator is built, update the clipboard from it.

Clipboard lines all stripped of any dash-space bullet prefix.kmmacros (21.6 KB)

FWIW it's also, of course, the kind of thing that you could do with a script pasted into a Keyboard Maestro Execute JavaScript for Automation action:

(() => {
    'use strict';

    // main :: IO ()
    const main = () => {
        const
            sa = standardSEAdditions(),

            bulletStripped = unlines(
                lines(sa.theClipboard())
                .map(
                    x => x.startsWith('- ') ? (
                        x.slice(2)
                    ) : x
                )
            );
        return (
            sa.setTheClipboardTo(bulletStripped),
            bulletStripped
        );
    };

    // ----------------------- JXA -----------------------

    // standardSEAdditions :: () -> Application
    const standardSEAdditions = () =>
        Object.assign(Application('System Events'), {
            includeStandardAdditions: true
        });

    // --------------------- GENERIC ---------------------
    // https://github.com/RobTrew/prelude-jxa

    // lines :: String -> [String]
    const lines = s =>
        // A list of strings derived from a single
        // string delimited by newline and or CR.
        0 < s.length ? (
            s.split(/[\r\n]/)
        ) : [];

    // unlines :: [String] -> String
    const unlines = xs =>
        // A single string formed by the intercalation
        // of a list of strings with the newline character.
        xs.join('\n');

    // MAIN ---
    return main();
})();

Thank you so much, @ComplexPoint and @JMichaelTX! Solved. This is very helpful. I went with the simple regex using \m but seeing how a more complex forEach works was also useful.

1 Like