Useful Shell Scripts [Example] Macro (v9.2)

Follow-up to where I could find a repertory of very short shell scripts used for the many possible commonly used transformations of selected text.

MACRO:   Useful Shell Scripts [Example]

This macro is just a collection of Shell Scripts, and should not be run as a Macro.

-~~~ VER: 1.0    2021-02-02 ~~~
Requires: KM 8.2.4+   macOS 10.11 (El Capitan)+
(Macro was written & tested using KM 9.0+ on macOS 10.14.5 (Mojave))

DOWNLOAD Macro File:

Useful Shell Scripts [Example].kmmacros
Note: This Macro was uploaded in a DISABLED state. You must enable before it can be triggered.


Author: JMichaelTX (based on scripts provided by others)

Purpose: Shell Scripts I have Found Useful

Below are the Shell Scripts that I have found useful enough to put into my collection of Actions in the [KMFAM] Favorite Actions and Macros (by @DanThomas) system in my KM setup.

I have used all of these, and I think they are safe and reliable. But if you don't understand what any of them do, the PLEASE ASK, BEFORE you use them, on the KM Forum for help, and I'm sure one or more of the Shell Script gurus will jump in and explain.

1 Like

Hi @JMichaelTX, thanks for sharing.

By any chance, is there a simple script to get the Line number of a given string in a given text?

For instance,
Given "456", get the line number of it in

The result is "2", the 2nd line.

I expect there is, but I don't know it off the top of my head.
I'm sure one of the Shell Script gurus will jump in with the solution.

I would probably use JXA (JavaScript) to solve this because I know it better.

great ! thanks very much

1 Like

Hey @martin,

That one's easy – insofar as bash scripting is "easy".  :sunglasses:


Get Line Number of Found Pattern (Awk) v1.00.kmmacros (5.6 KB)

1 Like

Thanks, Chris.
But when I try to make the search string a variable, it won't work.
I have found another answer that works.



The answer depends somewhat on where the input is coming from.

Assuming that it is on the clipboard/pasteboard, one way to do it would be:

pbpaste | cat -n | egrep '\t456$' | awk '{print $1}'

Here's a breakdown of what that line is doing

pbpaste says use the information on the pasteboard

cat -n = "adds line numbers"

egrep '\t456$' = "look for a 'tab' (that's the \t) followed by '456' and then the end of the line (that's the $)

awk '{print $1}' = print the first item on the line, which is the line number that was added by cat -n

My Assumptions

  1. I am assuming that you want to find the line which just has 456. For example, if your input was


the egrep command that I suggested would still give you the 2nd line because all of the other lines have more than just 456.

If that is not an accurate assumption, please let me know. If you just wanted to find the first line that has 456 anywhere in it, you would want to use this:

pbpaste | cat -n | fgrep '456' | awk '{print $1}'

  1. I am also assuming that there is only one line that has exactly what you are looking for. However, if your input looked like this:


Then you are going to get


as a result because it is found on both lines 2 and 4. If you only want the first line that matches, you would use:

pbpaste | cat -n | egrep '\t456$' | awk '{print $1}' | head -1

where head -1 means "take the first line of any result". Likewise

pbpaste | cat -n | egrep '\t456$' | awk '{print $1}' | tail -1

would give you the last matching line.

Searching for variable content

Now let's assume that you might want to have a shell script that will search for any item which is saved as a Keyboard Maestro variable named Something.

In shell scripts, Keyboard Maestro Variables must be prefixed with $KMVAR_ so the variable Something in Keyboard Maestro becomes $KMVAR_Something.

To do the same thing as above with a variable, it would look like this:

pbpaste | cat -n | egrep "\t${KMVAR_Something}\$" | awk '{print $1}'

I hope this helps. If not, please let me know.

Here's where I explain why you have to use \$ instead of just $

This gets a little technical so if it's confusing or boring, feel free to skip this part.

Since we are using a variable we need to use " instead of ' because shell scripts only 'expand' variables when they are in " and not in '.

If the variable Something was equal to 456 then "${KMVAR_Something}" would be replaced by 456 but '${KMVAR_Something}' would not be replaced, so you would end up searching for the literal string ${KMVAR_Something} which is not what you want.

Then there is one last little detail to be aware of. Previously I said that egrep will interpret $ to mean "the end of the line" but if you put $ inside " instead of ' then the shell will think that the $ is trying to refer to some variable. In order to use a literal $ for egrep so it can still understand it as "the end of the line" you need to use \$.

Note that for our purposes ${KMVAR_Something} and $KMVAR_Something are the same.

I tend to suggest using the {brackets} because I think it makes it easier to see where the variable name starts and ends.


Hi @tjluoma,

Thanks very much!!! Your reply is a textbook of an answer given to beginners. Not only does it explain what each step does, but it also points out all the pitfalls that one might step into when dealing with the script.

It explained why I could not make Chris's script work. I did not change the single quote in '/456/' to double-quote when I replaced 456 with a KM variable: "/${KMVAR_LocalString}/". So I was searching for a literal '/${KMVAR_LocalString}/'. I should have caught that~

1 Like

I’m glad to hear that it helped.

Well, don't be too hard on yourself.

Single-vs-double quotes are something that has tripped up nearly everyone who has dealt with shell scripts at some point.

There is a logic to it, but if you aren’t familiar with it, it is easy to use the wrong ones -- especially because in many cases you can get away with using either one and it will not matter, it’s only when dealing with certain characters, especially $ that it becomes crucial to use the right one.


Hey @martin,

You just have to understand how to manage the quoting in the shell (macro appended). I see TJ schooled you on this above.

You can also compose your entire bash command and then execute with eval.

It's also possible to pass a bash variable to awk as the pattern, but that gets more complicated than I want to mess with.


Get Line Number of Found Pattern (Awk) v1.01.kmmacros (5.8 KB)

1 Like

Hey Folks,

I should have mentioned that my first awk macros return ONLY the line number for the FIRST pattern matched and then exit.

By removing the exit statement in the script I'm allowing this version to print line numbers for ALL lines that match the given pattern.


Get Line Number of Found Pattern (Awk) v1.02.kmmacros (6.6 KB)

1 Like

Thanks, Chris.
I was bitten by the quotes in Shell Script more than once.

Quoting specifications in various languages are the bane of everyone's existence – especially if you don't use them all the time...



1 Like