How to Change a String in a Plist File?

The solutions given here: Reading and writing plists from Execute Script actions - #17 by JMichaelTX are too complex for me.

Could a kind soul please help me to create a script (AppleScript or JXA) to set the value of the string:

<key>projectTemplates</key>
<string>/Users/hl/Dropbox/CT/User/CT_tpl/pt_abc.xml|</string>

in the PLIST:

/Users/hl/Library/Preferences/com.apple.java.util.prefs.plist

e.g. to pt_def.xml (in the same path)

Thanks!

You don't need a script for this.

Assuming the file contents are:

This one KM Action should do the trick:

image

File Path:
/Users/hl/Library/Preferences/com.apple.java.util.prefs.plist

Search For:
(<key>projectTemplates<\/key>\R\h*<string>\/Users\/hl\/Dropbox\/CT\/User\/CT_tpl\/)pt_abc\.xml(\|<\/string>)

Replace With:
\1pt_def.xml\2

WARNING! This will overwrite your existing file!
Be sure to make a BACKUP copy of your file before running this Action/Macro.

Question: Are you sure this is the text from the plist file, that it contains the Vertical Bar | after the file name ?

Questions?

1 Like

I'm struggling with an encoding problem. After this (or a literal replacement) the encoding changes from UTF-8, UNIX LF to (Western Mac OS Roman), UNIX LF:

How can I prevent this?

That plist is a binary file, not text:

user-168:~ nigel$ less ~/Library/Preferences/com.apple.java.util.prefs.plist 
"/Users/nige_s/Library/Preferences/com.apple.java.util.prefs.plist" may be a binary file.  See it anyway? 

...so you'll want to edit it with defaults or PlistBuddy. You'll need to check the plist structure first to determine the syntax you'll need, so start in Terminal with

defaults read  ~/Library/Preferences/com.apple.java.util.prefs.plist

Hey Nige,

but I can edit in BBEdit with no problem. Isn't that strange?

I can, of course, use TextFactory.

No, because BBEdit can open, edit, and save binary text files. So you could make your changes in BBEdit -- but defaults and PlistBuddy are tools specifically made to do this job so it makes sense to use them.

I see what you mean.

I have found this info:

To run PlistBuddy, open the Terminal and type:

/usr/libexec/PlistBuddy

Explanation how to use:

Which I'll carefully have to study and try on the plist at hand.

Hey Hans,

I wouldn't expect Keyboard Maestro to change a file's encoding...

Can you post a same plist file and a basic macro that flubs the encoding?

I think this should be tested and perhaps reported to Peter.

-Chris

The main problem is working your way through the plist's hierarchy. My plist will be different to yours so I'm not going to guess, but post or DM me the contents of the plist and I'll see if I can help.

It looks like a text S'n'R action acting directly on a binary file, so I think it's more "wrong action, wrong place" than a KM flub.

I don't disagree, but I still think Keyboard Maestro should not be changing a file's encoding.

My guess is that BBEdit infers the encoding based on the file's contents and that doing a text S'n'R on the binary completely banjaxes that. You can do it in BBEdit, but that's because it can translate the binary on opening and closing and maintain consistency.

1 Like

Hey Chris,

At this stage it is just one simple replacement. Please find the plist attached.

Hans

Archive.zip (11.2 KB)

1 Like

That's very generous of you, Nige. Please find the plist attached.

These are the relevant keys at this moment of development:

<key>projectTemplates</key>
<string>/Users/hl/Dropbox/CT/User/CT_tpl/test_pt.xml|</string>

<dict>
<key>Goggomobil.xlf</key>
<string>test_pt.xml</string>
</dict>

The actions that have to be executed are:

  1. Replace /Users/hl/Dropbox/CT/User/CT_tpl/test_pt.xml with /Users/hl/Dropbox/CT/User/CT_tpl/abc_pt.xml.
  2. Add an entry in this dictionary:
<dict>
<key>abc.xlf</key>
<string>abc_pt.xml</string>
<key>Goggomobil.xlf</key>
<string>test_pt.xml</string>
</dict>

Thanks for your help!

BTW: Perhaps this can be escalated later to a generic plist modification macro for the Library?

Archive.zip (11.2 KB)

1 Like

When dealing with dictionaries you need to include the <key> line that precedes <dict> -- that's the thing that names the dictionary and allows you to target it. But there's only one <key>Goggomobil.xlf</key> in the plist so it wasn't needed this time.

So your first change is

/usr/libexec/PlistBuddy -c "Set :/:translator/:projectTemplates /Users/hl/Dropbox/CT/User/CT_tpl/abc_pt.xml|" ~/Library/Preferences/com.apple.java.util.prefs.plist

"Set projectTemplates, whose parent is translator/, whose parent is /, to the value /Users/hl/Dropbox/CT/User/CT_tpl/abc_pt.xml|" (I've included the | at the end because that was in the previous entry, take it out if not needed).

The second is

/usr/libexec/PlistBuddy -c "Add :/:translator/:templatesMap/:abc.xlf string \"abc_pt.xml\"" ~/Library/Preferences/com.apple.java.util.prefs.plist

"Add a key abc.xlf with the string value abc_pt.xml to dictionary templatesMap/, whose parent is translator/, whose parent is /." I've used escaped double-quotes around the string -- they're more obvious than unescaped singles (you'd end up with '") although neither are necessary here because the string contains no spaces or "funky" characters.

1 Like

Thank you @Nige_S!

So, if I want to integrate this in a Keyboard Maestro, I would have to use the Shell script action, I guess? (The variable "endclient" will be inherited from the previous actions of a project creation macro.)

Yes -- and I've found after a bit of playing that you can put multiple commands into one call to PlistBuddy. It's good practice when interpolating variables to use the ${variableName} format so it's obvious to the shell (and to you!) what's the variable name and what's the text you are including it in:

Java Util Plist Edit.kmmacros (2.1 KB)

Image

I've followed your variable format here, but if you don't get/set that variable in other macros then the usual advice to use a Local variable applies.

Do test on a copy of the plist first!

1 Like

Hey @Nige_S,

I have stitched everything together now and it works. However, I have one last (?) question: how can I change the last action from Add to Set? (I've tried a lot, but I don't get there):

Test - Set CafeTran Espresso's PLIST for new project template.kmmacros (10.1 KB)

We need to see a) the dictionary before, and b) the dictionary after to be sure. Note that you can't Set the value of a key that doesn't exist, you must Add it.

But assuming it's already there it's like all the others -- path to key, then the value. So something like:

/usr/libexec/PlistBuddy -c "Set :/:translator/:templatesMap/:\"${KMVAR_endclient}.xlf\" \"${KMVAR_endclient}_pt.xml\"" ~/Library/Preferences/com.apple.java.util.prefs.plist

Even if you keep each PlistBuddy command separate, I'd urge you to include them all in one shell script (unless this is just a test and you are using KM-logic to pick between them) -- each "Execute" action takes resources to spin up a shell, so do it all in one go.

2 Likes

I'm stuck here. I cannot get the red action to work:


So I tried deleting the key first, and adding it then, which works. The problem, however, is that I do not know the value of the key IRL (only at first run the key will be 'tmp'). (All this exercises are because the app requires the newly added key to be the first one in the dictionary, maybe there is a way to achieve that?)
com.apple.java.util.prefs.plist.zip (6.7 KB)
I have tried using '*' and '\*' to delete any existing key, but to no avail.
Here is the key:

<key>templatesMap/</key>
<dict>
	<key>tmp.xlf</key>
	<string>tmp_pt.xml</string>
</dict>

I'll add a copy of the plist too, with everything set to 'tmp'.