The Execute a Shell Script action executes a specified shell script using /bin/sh
, either from a file or from the script embedded in the action.
Important:
#!
at the front of the script.sh
shell, so if you want to use another language, or use any special kinds of shell-specific syntax, then you should specify the language.To execute the script from an external file, choose the “Execute Script File” in the choices for script location.
You can choose what input (stdin) to send to the script (v8+) as well as where the output should go.
If the script fails, the action will fail (v9.0+), potentially aborting the macro.
In your script, use this format: $KMVAR_[variable_name]
where [variable_name] is the name of your Keyboard Maestro Variable, but with spaces in the name replaced with underscores (‗
).
For example: File Name
would be formatted as $KMVAR‗File‗Name
.
If your variable already has underscores, you can use it as is.
Keep in mind that $KMVAR_[variable_name]
is a shell environment variable.
bash, sh, zsh, etc.
my $var = $ENV{KMVAR_VarName};
Accessing Keyboard Maestro Variables in Python is somewhat different. Here is an example:
REQUIRES: The following two Keyboard Maestro variables to exist and contain the requisite text:
local_DataStr local_CharToCount
#!/usr/bin/env python # Python Version: 2.7.x import os print os.environ['KMVAR_local_DataStr'].count(os.environ['KMVAR_local_CharToCount'])
See the Forum section below for more Python examples.
Note that you can only read these environment variables. You cannot write to them (or more accurately, you can write to them but that will not change the Keyboard Maestro variables that they were created from).
Using shell variables in awk is also different than in most other shell languages. You need to pass the shell variable as parameter to the -v flag.
Some examples:
# EXAMPLE 1 – Using an awk variable in an awk script: awk -v myVar=4 'BEGIN {print "myVar is " myVar}' # --> My var is 4 # --------------------------------------------------------------------------------------- # EXAMPLE 2 – Printing a shell variable with awk: myShellVariable='What can I do for you today?' awk -v awkVariable="$myShellVariable" 'BEGIN {print "The value of VAR is: " awkVariable}' # --> The value of VAR is: What can I do for you today? # --------------------------------------------------------------------------------------- # EXAMPLE 3 – Using KM Variables with awk (You must have a value in the Keyboard Maestro variable My_KM_Variable.) awk -v awkVariable="$KMVAR_My_KM_Variable" 'BEGIN {print "The value of My_KM_Variable is: \""awkVariable"\""}' # --> The value of My_KM_Variable is: "Contents of KM Variable: My_KM_Variable"
For a KM Macro that shows this, see:
MACRO: Using KM Variables with Bash awk [Example]
If you are passing a file/folder path in a Keyboard Maestro Variable, then it is best to put the Variable reference in double quotes so that spaces in the path will work, like this:
cat "$KMVAR_File_Path"
Consider These Guidelines
'
) in order for the variable to be expanded to / replaced by its value. So, for example, this will not work:# 🚫 Does NOT Work /usr/local/bin/emacsclient -e '(w3m-browse-url "$KMVAR_SafariURL")'
because although the Variable is between double-quotes, it is in a string that is between single-quotes.
# ✅ This WORKS /usr/local/bin/emacsclient -e '(w3m-browse-url "'"$KMVAR_SafariURL"'")'
One key Bash feature that makes this work is that when two quoted strings are adjacent, they will be concatenated. So after the Variable is expanded, the command string might look like this:
/usr/local/bin/emacsclient -e '(w3m-browse-url "https://www.apple.com")'
For more info, see these discussions in the Keyboard Maestro Forum:
If you are dealing with non-ASCII characters, you probably want to set the LC_ALL
environment variable to UTF8, which you can do by setting the Keyboard Maestro variable ENV_LC_ALL
to “en_US.UTF-8”.
By default (v9.0+), if you have not set these environment variables they will be set to UTF-8 for you.
The action (gear) ⚙ menu includes the following options:
The results of a shell script can be:
If you want to return multiple values from a script, then you can either use AppleScript (via osascript
) to set Keyboard Maestro variables.
For example, you could have a shell script date display briefly in the Notification Center every hour, or use a hot key to type the results directly into your text editor.
You can also use the clipboard by piping from pbpaste and to pbcopy.
Proper quoting of strings in a Shell Script is often a challenge. In order to understand it, you have to understand what happens at each level of the processing.
Thankfully, in an Execute Script action, Keyboard Maestro itself does not do any processing, so that is one less place for confusion.
With shell commands, the important thing to understand is that, with only a few exceptional programs, it is not the command that does the processing, it is the shell (bash usually, although there are many shells).
So for example with Keyboard Maestro variable set to "Selection Style" -int 0
and en Execute Shell Script command of:
defaults write /Users/me/Library/Application\ Support/Witch/Settings $KMVAR_witchPref
It is the bash tool that sees the string and it processes it in to an array of strings which it passes to the defaults tool. This is important - the defaults command receives not a single string, but an array of strings, in this case the array will be:
defaults
write
/Users/me/Library/Application Support/Witch/Settings
"Selection
Style"
-int
0
It is bash that has processed the variable substitution for $KMVAR_witchPref, split the line in to seven parts, processed the backslash in “Application\ Support” and then executed the defaults tool with the seven parts as arguments (the command itself is the 0th argument, normally mostly ignored by tools).
So the problem is that the word-breaking, variable substitution, de-quoting and de-backslash is all happening at as the line is processed by bash, after which you are left with quotes from within variable substitution, but they no longer have any meaning, they are just characters.
So variables that are meant to be a single parameter should be surrounded by double quotes in the line, but not contain quotes in the variable, and generally you do not want to pass multiple parameters within a single parameter. So instead, perhaps set one variable to the setting name, and one variable to the type and one variable to the value, and then use a command like this:
defaults write "/Users/me/Library/Application Support/Witch/Settings" "$KMVAR_Setting" -"$KMVAR_Type" "$KMVAR_Value"
Now a further complication happens when you want to sent a string containing double quotes and variables to a parameter. For example, perhaps you want a command like this:
emacsclient -e '(w3m-browse-url "$KMVAR_SafariURL")'
This will not work because bash only expands variables within double-quoted strings. Within single quoted strings, they remain as the text $KMVAR_SafariURL
(unchanged). And so emactsclient will receive the array:
emacsclient
-e
(w3m-browse-url "$KMVAR_SafariURL")
But it will not expand the variable (that is the shell’s job).
To work around this you need to use either all double quotes (and backslash any double quotes within them) such as:
emacsclient -e "(w3m-browse-url \"$KMVAR_SafariURL\")"
or use a combination of strings, some double quoted and some single quoted:
emacsclient -e '(w3m-browse-url "'"$KMVAR_SafariURL"'")'
Note that that parameter is now made up of three stings combined into one parameter:
(w3m-browse-url "
- single quoted string")
- single quoted stringFor the reverse problem, where you have a variable that contains multiple parameters, note that you cannot use quotes within a variable - by the time the shell is expanding variables, it has already processed quotes and will not do so again so quotes will just be regular characters passed to the target command and almost certainly result in errors. If possible, use seperate variables for seperate parameters, but if you cannot do that (for example you have a list of paths), store them as seperate lines in a variable (without any quoting or backslashes) and use a command like:
echo "$KMVAR_files" | tr '\r\n' '\0' | xargs -0 ls -l
The tr command will replace \r
or \n
line endings with a nul character, and the xargs -0
command will read that, split the arguments at the nul character, and pass them to the specified command (in this case ls -l
. Note that xargs has a limit to the number of arguments it will pass, so for large numbers of arguments it may run the command multiple times with subsets of the arguments - read the xargs
man page for more details.
In essence, the default path in a Keyboard Maestro Execute Shell Script is the base path for the system:
/usr/bin:/bin:/usr/sbin:/sbin
that is the script will search for tools in the /usr/bin
directory, then in the /bin
directory, then /usr/sbin
and finally /sbin
. Only tools installed by the system will be in these directories - any tools you have installed will almost certainly be elsewhere and so not found by default because:
$PATH
environment variable you may have set in Terminal is not used.How To Set a Path
$PATH
environment variable within each Execute Shell Script action.ENV_PATH
(the Keyboard Maestro Path Variable) prior to the Execute Shell Script Action.$PATH
works in the Terminal app.ENV_PATH
Variable is set, it will remain in your Keyboard Maestro Variable set (until/unless you delete it), available to every Execute Shell Script Action that you might use in the future. So you don't need to set it in every Macro.ENV_
at the front.ENV_PERL5LIB
that will set the the PERL5LIB environment variable for each Execute Shell Script.Setting the ENV_PATH Keyboard Maestro Variable
A shell script path that will work for many users is:
/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin
Of course, if you have tools or files you installed elsewhere, then you would need to include the path to those in this path statement, IF you plan to use any of those tools/files in a Execute Shell Script Action.
You can create the ENV_PATH
Variable one time using the Variable Preferences Pane of the Keyboard Maestro Preferences.
ENV_PATH
and set its value.
For a good discussion about this see:
When you execute a shell script from other apps, like Terminal.app, the shell does NOT have access to the Keyboard Maestro Engine environment, and thus it does NOT know anything about Keyboard Maestro Variables.
In order to access Keyboard Maestro Variables in these scripts, you must use a tool like osascript.
⚠️ Note that you do NOT need to replace spaces with underscores in your Variable name in this case, since you are ultimately using AppleScript in the Shell Script.
Here is a simple example that works with Keyboard Maestro Ver 6+ to get a variable:
osascript -e 'tell application "Keyboard Maestro Engine" to get value of variable "My KM Var"'
If you are using Keyboard Maestro 7.1+ you can use the new simpler command to get a variable:
osascript -e 'tell application "Keyboard Maestro Engine" to getvariable "My KM Var"'
To set a variable (and create if needed) with Ver 7.1+ you can use this:
osascript -e 'tell application "Keyboard Maestro Engine" to setvariable "My KM Var" to "Some new value"'
Here is a more comprehensive example, providing error handling:
read -r -d '' theAppleScript <<'EOF' tell application "Keyboard Maestro Engine" set kmVarName to "My KM Var" tell variable kmVarName if it exists then return its value else return "Error → Keyboard Maestro variable '" & kmVarName & "' does not exist!" end if end tell end tell EOF osascript -e "$theAppleScript";
Keywords: Quote String, Bash, Shell Scripting