Shell script to extract colours from macOS Terminal.app themes
DATE: 2022-12-29
AUTHOR: John L. Godlee
On macOS, configuration profiles for Terminal.app are stored as plist files with the extension .terminal. These files contain XML or binary data. The profiles contain, among other settings, the colour scheme for the terminal.
I wrote a shell script to extract the colour scheme information as RGB255 values for each colour scheme value. The function uses a combination of xmlstarlet[1], awk[2], and some programs which come with macOS, like tr, base64, and plutil.
2: https://www.gnu.org/software/gawk/manual/gawk.html (https://www.gnu.org)
First, I define a function to extract colour values:
function dict_search() {
# If colour code found in file
if grep -q "${1}" "${2}"; then
xml sel --net -t -v "//key[.=\"$1\"]/following-sibling::data[1]" "${2}" |
tr -d '[:space:]' |
base64 --decode |
plutil -convert xml1 - -o - |
xml sel --net -t -v "//dict/array/dict/key[.='NSRGB']/following-sibling::data[1]" |
tr -d '[:space:]' |
base64 --decode |
awk -v var="${1}" 'BEGIN{ ORS=" "; print var }
{ for(i=1;i<=3;i++) { printf "%.0f ", $i*255 }; printf "\n" }'
fi
}
Going line by line:
- if grep ... will only run the rest of the function if the chosen colour ID is found in the .terminal file.
- xml sel ... searches for the key (i.e. the chosen colour ID), then pulls the contents of the data node which comes directly after it.
- tr -d ... removes all spaces, including new lines, effectively concatenating the contents of the node.
- base64 ... converts the base64 encoded data into text
- plutil -convert ... converts that into XML
- xml sel ... extracts the RGB values in the XML
- tr -d ... as above, removes all spaces and new lines
- base64 ... as above, converts base64 encoded data into text
- awk ... prints the name of the colour ID, and the three RGB components multiplied by 255 and rounded to convert from a 0-1 decimal to a 0-255 integer.
Then I create an array containing all the colour IDs of interest:
colArray=("ANSIBlackColor" "ANSIBlueColor" "ANSICyanColor" "ANSIGreenColor" "ANSIMagentaColor" "ANSIRedColor" "ANSIWhiteColor" "ANSIYellowColor" "ANSIBrightBlackColor" "ANSIBrightBlueColor" "ANSIBrightCyanColor" "ANSIBrightGreenColor" "ANSIBrightMagentaColor" "ANSIBrightRedColor" "ANSIBrightWhiteColor" "ANSIBrightYellowColor" "BackgroundColor" "CursorColor" "SelectionColor" "TextBoldColor" "TextColor")
Finally, to construct the output of the script, firstly printing the filename, then running the function for each colour ID, and only printing the result to stdout if the function output is not empty:
# Print filepath of theme
printf "%s\n" "${1}"
# For each in array, run function
for i in ${colArray[@]}; do
# Run function
out=$(dict_search "${i}" "${1}")
# If output not empty, print
if [ -n "${out}" ]; then
printf "%s\n" "${out}"
fi
done
This produces, for example for my tweaked version of the Smyck[3] colour scheme, which I use every day:
smyck.terminal ANSIBlackColor 0 0 0 ANSIBlueColor 64 125 153 ANSICyanColor 32 115 131 ANSIGreenColor 125 169 0 ANSIMagentaColor 186 138 204 ANSIRedColor 184 65 49 ANSIWhiteColor 161 161 161 ANSIYellowColor 196 165 7 ANSIBrightBlackColor 75 75 75 ANSIBrightBlueColor 141 207 240 ANSIBrightCyanColor 106 217 207 ANSIBrightGreenColor 196 241 55 ANSIBrightMagentaColor 247 154 255 ANSIBrightRedColor 214 131 124 ANSIBrightWhiteColor 247 247 247 ANSIBrightYellowColor 254 225 77 BackgroundColor 18 21 28 CursorColor 32 115 130 SelectionColor 32 115 131 TextBoldColor 247 247 247 TextColor 247 247 247
Response: 20 (Success), text/gemini
| Original URL | gemini://republic.circumlunar.space/users/johngodlee/post... |
|---|---|
| Status Code | 20 (Success) |
| Content-Type | text/gemini; charset=utf-8 |