Having trouble with quotes and shell variables

I have what I think is a fairly simple macro: It prompts for user input, then executes a shell script using some boilerplate text plus the variable set in the user input. (I’m writing this to simplify setting hidden prefs for one of our own apps, which I do a lot when testing.)

The Prompt for Input bit saves the input to a variable named witchPref. The next command executes a shell script, combining the variable with some fixed text:

Execute text script:

defaults write /Users/me/Library/Application\ Support/Witch/Settings $KMVAR_witchPref

So far so good … I’ve confirmed that the output string is the correct format (by temporarily saving it to variable via “Set variable to text,” then using the same line as above). I can take the output string, paste it in Terminal, and it works.

But it will not work in a shell script action, and I know the problem is quotes. I just don’t know how to fix it. Here’s a typical variable I need to pass to the shell:

“Selection Style” -int 0

So the full command is:

defaults write /Users/me/Library/Application\ Support/Witch/Settings “Selection Style” -int 0

If I paste that in Terminal, it works. If I paste the stored string from the variable I create, it works. But in a shell script action, I get this:

Could not parse: Style". Try single-quoting it.

So I do, changing the " to ’ … then I get this:

Unexpected argument -int; leaving defaults unchanged.

So I thought I’d try outsmarting it. I changed the pasted text to…

Selection\ Style\ -int\ 0

And my shell script action to …

defaults write /Users/robg/Library/Application\ Support/Witch/Settings “$KMVAR_witchPref”

But that got me …

Rep argument is not a dictionary. Defaults have not been changed.

Argh … can someone explain what obvious thing I’m overlooking here?

thanks;
-rob.

1 Like

I remember, I had a similar quoting problem in the past, and couldn’t figure out. Something weird.

As a workaround – avoiding the quotes in the variable – you could do it like this (which seems the cleaner solution anyway):

_[test] Set Witch Selection Style.kmmacros (3.3 KB)

BTW, what does the setting exactly change? I have read the description, but somehow I don’t see a difference…

You suggested:

“As a workaround – avoiding the quotes in the variable – you could do it like this (which seems the cleaner solution anyway):”

The problem is that I have about 50 different prefs, and they’re all structured differently. So I need to be able to paste the string and just have it work; I can’t code for a specific pref.

Basically, to test prefs for customers, I need to disable Witch, quit Sys Prefs, run the Terminal command, then relaunch Witch. The macro does all of that for me, except I can’t get this one paste to work.

I wonder if I wrap it in an AppleScript instead … off to test.

(That one changes how rows are highlighted, but depending on your settings, it may not change anything.)

-rob.

Yeah, I thought as much…

I’m sure our problem/error is something really obvious and somebody will enlighten us soon :wink:

PS:

What I’ve tried so far:

Various levels of escaping \", using the hex char code as %22% and \x22, single quote variants and combinations of single and double quotes, quoting the variable itself, “Process text normally”, “Process nothing”, …

I tried AppleScript, still failed. So I took the stupid way out, and split the input into pref name and value. Then I can hardcode the quotes in the shell script step, and that seems to work fine.

I'm still curious why the other method fails, though. Seems it really should work.

-rob.

OK, I figured out a way to do it:

_[test] Set Witch Prefs.kmmacros (3.3 KB)

2 Likes

Ah, eval! I totally forgot about eval…I always forget about eval.

thanks!
-rob.

Yes, who does not forget about eval? :grinning:

2 Likes

Quoting is always a pain in the neck. In order to understand it, you have to understand what happens at each level of the processing.

Thankfully, in a script, 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 it is the bash tool that sees the string

defaults write /Users/me/Library/Application\ Support/Witch/Settings $KMVAR_witchPref

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:

  • defaults
  • write
  • /Users/me/Library/Application Support/Witch/Settings
  • "Selection
  • Style"
  • -int
  • 0

Note that 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 variable substitution, backslash, de-quoting is all happening at the start, after which you are left with quotes from the variable substitution, but they no longer have any meaning, they are just characters.

As for the best way to solve this, well, I would generally have two or even three parts:

defaults write "/Users/me/Library/Application Support/Witch/Settings" "$KMVAR_Setting" -"$KMVAR_Type" "$KMVAR_Value"

Rather than a string representing three pieces of information.

5 Likes

Thanks for the thorough explanation. Really appreciated!

Now my world is coherent again :blush:

Thanks Peter, that’s very enlightening – and as it turns out, your solution is the same one I wound up with; I now prompt for all three fields. It takes a bit more time, but it’s more flexible and best of all, always works.

-rob.

Thank you for sharing

I have a similar issue with this command line:
system_profiler SPStorageDataType | sed -n '/DRIVE:/,/S.M.A.R.T./p'

and I would like to replace [DRIVE] by a KM variable:
system_profiler SPStorageDataType | sed -n '$KMVAR_ShellSed,/S.M.A.R.T. Status:/p'

I've tried many things but I can't figure out where to split as Peter explained.
Thanks for your time

What possible characters with the ShellSed variable contain?

Shell will not expand $variables inside a single quoted string.

It will inside a double quoted string. Sp you can do something like:

sed -n "$KMVAR_ShellSed"',/S.M.A.R.T. Status:/p'

The variable will be an hard drive name as DRIVE: in my exemple. Could be any name like Shuttle 003 or HD Back Up 1.

Thank you Peter for your support
Looking forward to see what you gonna extract from the lagic hat. Any chance to read what you are cooking for version 9 ?

"$KMVAR_ShellSed"',/S.M.A.R.T. Status:/p'

should work fine then.

I do drop hints in various places around the forum.

@Lucien_Keller et al: You might try this imperfect forum search to find Peter's hints about the next version, since 2019-01-01:
@peternlewis "next version" after:2019-01-01

2 Likes