1

my objective is to create a keyboard shortcut to activate/deactivate the "replace with" on Mac.
The idea is to create an automation that can be activated with a shortcut trough services.

What the shortcut does is switching two similar .GlobalPreferences.plist files, one with "replace with" keys one without.

Here is my code: enter image description here

The problem is that for some reason it doesn't switch the files. But on a regular folder it works.

In detail:
running mv ~/Library/Preferences/.GlobalPreferences.plist ~/Library/Preferences/.GlobalPreferencesTMP.plist perfectly works, the proprieties are copied.
On the other hand if I run mv /Library/Preferences/.GlobalPreferences2.plist ~/Library/Preferences/.GlobalPreferences.plist It doesn't keep the proprieties of .GlobalPreferences2.plist but instead I got exactly the same as .GlobalPreferences.plist which I started with.

3
  • 1
    Does it definitely not replace the files, or does it just not acknowledge there is a new version? Most prefs like this are only read once, so changing a pref needs the file to be re-read. I'm not certain how you could force that, so I can't provide an answer.
    – Tetsujin
    May 10 at 14:00
  • 4
    I'd also be more tempted to use defaults write than swap out the files each time.
    – Tetsujin
    May 10 at 14:07
  • Properties are cached, so even if you just change the files, that's not enough to get them re-read. Also: How will you cope with changes to other key/values in the plist, made by the OS?
    – benwiggy
    May 10 at 16:20
1

In order to toggle the state of Text Substitutions by swapping the ~/Library/Preferences/.GlobalPreferences.plist file, you would need to have a way to update the cached copy of it in memory. If you are an Objective-C programmer and have Xcode you could roll-your-own app to update the file and what's cached in memory. Sans that, here is a method using a third-party command line utility, shortcuts, and a shell script that works for me.

The bash shell script I've written uses the shortcuts binary executable to backup, delete and restore the Text Substitutions. The backup and restore are simple and straight forward compared to deleting the Text Substitutions as shortcuts can only delete one shortcut at a time. It's relatively quick. I have 200+ items in my Replace | With list and the script takes ~4 seconds to delete them, and a fraction of a second to restore them.

This was tested under macOS Catalina and macOS Big Sur with Language & Region settings in System Preferences set to English (US) — Primary and worked for me without issue1.

  • 1 Assumes necessary and appropriate setting in System Preferences > Security & Privacy > Privacy have been set/addressed as needed.

Note: First make a manual backup of your Text Substitutions by going to System Preferences > Keyboard > Text, pressing ⌘A to highlight all entries of Replace | With list, then click-hold with the mouse, then drag and drop to the Desktop, or any folder of a Finder window.

This will create a file named Text Substitutions.plist that can be dragged and dropped onto the Replace | With list in System Preferences > Keyboard > Text to manually restore.

The example shell script code in conjunction with the shortcuts command line utility does it own backup and restores from its backup, but always make the manual backup described above just to have if ever needed.


Install shortcuts by one of the methods mentioned in the link.

I downloaded the source code, reviewed and verified there was nothing ominous in the code, then built it using Xcode.

It's also available thru Homebrew, in Terminal brew install rodionovd/taps/shortcuts, or download as a pre-built binary.


Note: The following instructions and example shell script code are for use as an executable shell script in Terminal, and or can be used in a Run Shell Script action in an Automator Service/Quick Action and assigned a keyboard shortcut in: System Preferences > Keyboard > Shortcuts > Services

I'd suggest testing as an executable shell script first to ensure that it works for you, then use the example shell script code in an Automator Service/Quick Action.

In Terminal, run the following compound command:

      Assumes the pwd is your HOME directory.

f=trw; touch "$f"; open -e "$f"; chmod +x "$f"

      Note: trw = Toggle Replace With (Had to name it something.)

In the opened trw document copy and paste the example shell script code into it, then save and close it.

Back in Terminal:

./trw

Will toggle the state of the Text Substitutions.

Note: After testing, move trw to a directory defined in the PATH to be available without typing: ./


Example shell script code:

#!/bin/bash

# set -e
# set -x 

shortcuts="/usr/local/bin/shortcuts"
automator=false # true or false


message() {
    [ "${1}" == "xmissing" ] && msg=" The 'shortcuts' executable is missing!"
    [ "${1}" == "already" ] && msg=" The text substitutions deletion is already in progress!"
    [ "${1}" == "backedup" ] && msg=" The text substitutions have been backed up."
    [ "${1}" == "nobackup" ] && msg=" No text substitutions to backup or restore!"
    [ "${1}" == "restored" ] && msg=" The text substitutions have been restored."
    [ "${1}" == "deleting" ] && msg=" The text substitutions deletion is in progress..."
    [ "${1}" == "finished" ] && msg=" The text substitutions deletion has completed."
    [ $automator == false ] && printf '%s\n' "${msg}"
    [ $automator == true ] && printf '%s\n' "$(date -j) ${msg}" >> message.txt
    [ $automator == true ] && [ -n "${2}" ] && \
    osascript -e "display notification \"${msg}\" with title \"Toggle Replace With\""
}

backupdir="$HOME/Library/Application Support/com.$USER.shortcuts"
[ ! -d "${backupdir}" ] && mkdir -p "${backupdir}"
cd "${backupdir}" || exit
backup="${backupdir}/Text_Substitutions.plist"
lockdir="/private/tmp/com.$USER.shortcuts"
[ ! -e "${lockdir}" ] && mkdir -p "${lockdir}"
cd "${lockdir}" || exit

if [ ! -x "${shortcuts}" ]; then
    message "xmissing"
    [ $automator == true ] && message "xmissing" "notification"
    exit    
fi

if [ -e ".lock" ]; then
    message "already"
    [ $automator == true ] && message "already" "notification"
    exit
fi

contents="$(defaults read -g NSUserDictionaryReplacementItems)"
empty='(
)'

if [ ! -e "${backup}" ] && [ "${contents}" != "${empty}" ]; then
    "${shortcuts}" read --as-plist | tee "${backup%.*}.$(date '+%s').plist" > "${backup}"
    message "backedup"
elif [ -e "${backup}" ] && [ "${contents}" != "${empty}" ]; then
    "${shortcuts}" read --as-plist | tee "${backup%.*}.$(date '+%s').plist" > "${backup}"
    message "backedup"
elif [ ! -e "${backup}" ] && [ "${contents}" == "${empty}" ]; then
    message "nobackup"
    [ $automator == true ] && message "nobackup" "notification"
    exit
fi

if [ "${contents}" == "${empty}" ]; then
    "${shortcuts}" import "${backup}"
    message "restored"
    [ $automator == true ] && message "restored" "notification"
else
    touch ".lock"
    message "deleting"
    [ $automator == true ] && message "deleting" "notification"
    while IFS= read -r line; do
        "${shortcuts}" delete "${line}"
    done <<< "$("$shortcuts" read | awk 'sub(/^"/, "", $2) sub(/"$/, "", $2) $1 ~ /^[0-9]+:/ { print $2 }')"
    sleep 1
    rm ".lock"
    message "finished"
    [ $automator == true ] && message "finished" "notification"
fi


Notes:

As coded, the example shell script code assume the shortcuts executable is located at /usr/local/bin/shortcuts, change as applicable. It is coded like this as /usr/local/bin/ is not in the PATH passed to a Run Shell Script action in an Automator Service/Quick Action.

The first time the shell script is run it creates a backup of the Text Substitutions to restore from, so it is imperative that there actually be some text substitutions to backup! A current backup is made each time prior to deleting them from the ~/Library/Preferences/.GlobalPreferences.plist file and is restored from the last backup.

The reason a third-party application is being used is in this use case it does something that the macOS default utilities cannot do, which is dynamically update the changes to the system. In other words, the defaults command as well as the /usr/libexec/PlistBuddy command in this use case does not update the system to the changes and has the same issue as swapping out the ~/Library/Preferences/.GlobalPreferences.plist file.

When using the example shell script code in a Run Shell Script action in an Automator Service/Quick Action, you need to set automator=true in the line after, e.g., shortcuts="/usr/local/bin/shortcuts"

Backups are saved to:

$HOME/Library/Application Support/com.$USER.shortcuts/Text_Substitutions.plist

The example shell script code when run as an executable shell script outputs messages to stdout and you see them as you go, however, when used in a Run Shell Script action in an Automator Service/Quick Action messages are displayed in Notification Center and also log to:

/private/tmp/com.$USER.shortcuts/message.txt

I will try you add comments to the code and or add additional notes here in the next couple of days.


enter image description here



Disclaimer Note: The example shell script code is just that and sans any included error handling does not contain any additional error handling as may be appropriate. The onus is upon the user to add any additional error handling as may be appropriate, needed or wanted.

1
  • 1
    This is exactly what I looked for. Many many thanks for such an exhaustive answer! I would have started looking for defaults but, as you explained it would have been useless. This works also for me with German (Switzerland) and MacOS 10.15.7.
    – Pa Dalton
    May 12 at 14:22
1

TextView windows that use macOS's Text Replacement usually provide a menu item to toggle Text Substitution off and on.

Edit > Substitutions > Text Replacement

enter image description here

You can set a keyboard shortcut for this menu item in System Preferences > Keyboard > Shortcuts.

enter image description here

(Here I've done it just for TextEdit, but you can do it for All Applications if you want.)

Toggling the key command gives me the following, when I type omw:

enter image description here

This achieves your goal of switching the replacement status without the complicated effort of maintaining and switching two complete preference files.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .