{"id":1364,"date":"2023-02-24T04:11:00","date_gmt":"2023-02-24T09:11:00","guid":{"rendered":"https:\/\/www.brunerd.com\/blog\/?p=1364"},"modified":"2023-02-27T22:31:58","modified_gmt":"2023-02-28T03:31:58","slug":"avoid-unicode-mangling-in-jamf-with-shef","status":"publish","type":"post","link":"https:\/\/www.brunerd.com\/blog\/2023\/02\/24\/avoid-unicode-mangling-in-jamf-with-shef\/","title":{"rendered":"Avoid Unicode Mangling in Jamf with shef"},"content":{"rendered":"\n<p>Computers are &#8220;the equivalent of a bicycle for our minds&#8221; as <a href=\"https:\/\/youtu.be\/ob_GX50Za6c?t=71\" target=\"_blank\" rel=\"noreferrer noopener\">Steve Job once said<\/a>. Sure enough, like a bicycle, they also require maintenance! Sometimes that requires soliciting the user to take action. Asking at the <em>right time<\/em> is very important (see my post <a rel=\"noreferrer noopener\" href=\"https:\/\/www.brunerd.com\/blog\/2022\/03\/07\/respecting-focus-and-meeting-status-in-your-mac-scripts-aka-dont-be-a-jerk\/\" target=\"_blank\">Don&#8217;t Be a Jerk<\/a>) and so is brevity. I&#8217;ve found a sprinkling of emoji and other symbols can help your message cut through the noise. A bright red stop sign \ud83d\uded1 can convey its meaning with only a glance, especially for non-native speakers of your language. Your tooling however, may trip you up and mangle your text.<\/p>\n\n\n\n<p>While <a rel=\"noreferrer noopener\" href=\"https:\/\/developer.apple.com\/library\/archive\/documentation\/LanguagesUtilities\/Conceptual\/MacAutomationScriptingGuide\/DisplayDialogsandAlerts.html#\/\/apple_ref\/doc\/uid\/TP40016239-CH15-SW1\" target=\"_blank\">AppleScript<\/a>, <a rel=\"noreferrer noopener\" href=\"https:\/\/github.com\/bartreardon\/swiftDialog\" target=\"_blank\">Swift Dialog<\/a> and <a rel=\"noreferrer noopener\" href=\"https:\/\/hcsonline.com\/images\/How_to_use_Jamf_Helper.pdf\" target=\"_blank\">JamfHelper<\/a> can all handle Unicode, Jamf databases tables by default <em>do not<\/em> support 4-byte Unicode! I touched on this in my post: <a href=\"https:\/\/www.brunerd.com\/blog\/2022\/02\/01\/jpt-1-0-text-encoding-fun\/\">jpt 1.0 text encoding fun<\/a>. While the database engine might support it, chances are your tables are <code>Latin1<\/code> which top out at 3-byte UTF-8 encoded characters. 4-byte characters get mangled. Let&#8217;s take a look at this in action:<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full is-resized\"><a href=\"https:\/\/www.brunerd.com\/blog\/wp-content\/uploads\/SUExample-policy-1edit-detail.png\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/www.brunerd.com\/blog\/wp-content\/uploads\/SUExample-policy-1edit-detail.png\" alt=\"\" class=\"wp-image-1372\" width=\"270\" height=\"182\" srcset=\"https:\/\/www.brunerd.com\/blog\/wp-content\/uploads\/SUExample-policy-1edit-detail.png 638w, https:\/\/www.brunerd.com\/blog\/wp-content\/uploads\/SUExample-policy-1edit-detail-300x202.png 300w\" sizes=\"auto, (max-width: 270px) 100vw, 270px\" \/><\/a><figcaption class=\"wp-element-caption\">Looks good while editing&#8230;<\/figcaption><\/figure>\n<\/div>\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full is-resized\"><a href=\"https:\/\/www.brunerd.com\/blog\/wp-content\/uploads\/SUExample-policy-2saved-detail-1.png\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/www.brunerd.com\/blog\/wp-content\/uploads\/SUExample-policy-2saved-detail-1.png\" alt=\"\" class=\"wp-image-1374\" width=\"271\" height=\"186\" srcset=\"https:\/\/www.brunerd.com\/blog\/wp-content\/uploads\/SUExample-policy-2saved-detail-1.png 603w, https:\/\/www.brunerd.com\/blog\/wp-content\/uploads\/SUExample-policy-2saved-detail-1-300x206.png 300w\" sizes=\"auto, (max-width: 271px) 100vw, 271px\" \/><\/a><figcaption class=\"wp-element-caption\">Once saved, the mangling is clear<\/figcaption><\/figure>\n<\/div>\n\n\n<p>Only the gear makes it through because it&#8217;s actually two 3-byte characters (U+2699 \u2699 gear plus variation selector U+FE0F) but who has time to check which one is which? How about an encoding tool written in shell, that allows you to escape and format Unicode for shell scripts and\/or Jamf script parameters that can make it through unscathed and un-mangled? Sound good? Great! I present <a rel=\"noreferrer noopener\" href=\"https:\/\/github.com\/brunerd\/shef\" target=\"_blank\">shef<\/a>, the <strong>Sh<\/strong>ell <strong>E<\/strong>ncoder and <strong>F<\/strong>ormatter!<\/p>\n\n\n\n<p>Give it some text, specify an encoding scheme and\/or quoting style and out comes your string ready for use! You can put the resulting string in your script as a variable or as a script parameter in a Jamf policy depending on quoting options. I&#8217;ve done the hard work of finding the various ways to escape special characters for shell and made this handy script for you!<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">shef Examples<\/h2>\n\n\n\n<p>First, let&#8217;s make a simple script for Jamf that processes the output of <a rel=\"noreferrer noopener\" href=\"https:\/\/github.com\/brunerd\/shef\" target=\"_blank\">shef<\/a>. I&#8217;ve chosen AppleScript so you can play along at home even if you don&#8217;t have Jamf installed, this can also be applied to &#8220;wrapper scripts&#8221; that leverage other tools like <a rel=\"noreferrer noopener\" href=\"https:\/\/github.com\/bartreardon\/swiftDialog\" target=\"_blank\">Swift Dialog<\/a> or <a rel=\"noreferrer noopener\" href=\"https:\/\/hcsonline.com\/images\/How_to_use_Jamf_Helper.pdf\" target=\"_blank\">JamfHelper<\/a>. At it&#8217;s heart is the simple technique of encoding the input with <a rel=\"noreferrer noopener\" href=\"https:\/\/github.com\/brunerd\/shef\" target=\"_blank\">shef<\/a> then decoding it in the script before presentation. If you use <code>bash<\/code> use <code>echo -e<\/code> if you use <code>sh<\/code> or <code>zsh<\/code> then just <code>echo<\/code> will work, it&#8217;s that simple. Our example script <a rel=\"noreferrer noopener\" href=\"https:\/\/github.com\/brunerd\/macAdminTools\/blob\/main\/Jamf\/scripts\/simpleAlert-AS.sh\" target=\"_blank\">simpleAlert-AS.sh<\/a>, keeps things small and as minimal as possible but keep in mind my fuller-featured tool <a rel=\"noreferrer noopener\" href=\"https:\/\/github.com\/brunerd\/shui\" target=\"_blank\">shui<\/a> for more robust scripts where you may need text or password entry, file picking, etc. without external dependencies but <em>don&#8217;t<\/em> want to bother learning AppleScript.<\/p>\n\n\n\n<pre class=\"wp-block-code language-bash\"><code>#!\/bin\/bash\n#simpleAlert-AS - Copyright (c) 2023 Joel Bruner (https:\/\/github.com\/brunerd)\n#Licensed under the MIT License\n\n#Simple Applescript alert dialog for Jamf - just a title, a message and an OK button\n#Accepts hex (\\xnn) and octal (\\0nnn) escaped UTF-8 encoded characters (since the default Jamf db character set mangles 4 byte Unicode)\n#Use shef to encode your strings\/files for use in this script: https:\/\/github.com\/brunerd\/shef\n\n#function to interpret the escapes and fixup characters that can screw up Applescript if unescaped \\ and \"\nfunction interpretEscapesFixBackslashesAndQuotes()(echo -e \"${@}\" | sed -e 's\/\\\\\/\\\\\\\\\/g' -e 's\/\"\/\\\\\"\/g')\n\nfunction jamflog(){\n\tlocal logFile=\"\/var\/log\/jamf.log\"\n\t#if we cannot write to the log or it does not exist, unset and tee simply echoes\n\t&#91; ! -w \"${logFile}\" ] &amp;&amp; unset logFile\n\t#this will tee to jamf.log in the jamf log format: &lt;Day&gt; &lt;Month&gt; DD HH:MM:SS &lt;Computer Name&gt; ProcessName&#91;PID]: &lt;Message&gt;\n\techo \"$(date +'%a %b %d %H:%M:%S') ${myComputerName:=\"$(scutil --get ComputerName)\"} ${myName:=\"$(basename \"${0}\" | sed 's\/\\..*$\/\/')\"}&#91;${myPID:=$$}]: ${1}\" | tee -a \"${logFile}\" 2&gt;\/dev\/null\n}\n\n\n#process our input then escape for AppleScript\nmessage=$(interpretEscapesFixBackslashesAndQuotes \"${4}\")\ntitle=$(interpretEscapesFixBackslashesAndQuotes \"${5}\")\n#could be a path or a built-in icon (stop, caution, note)\nicon=\"${6}\"\n#invoke the system open command with this argument (URL, preference pane, etc...)\nopen_item=\"${7}\"\n\n#these are the plain icons (Applescript otherwise badges them with the calling app)\ncase \"${icon}\" in\n\t\"stop\") icon=\"\/System\/Library\/CoreServices\/CoreTypes.bundle\/Contents\/Resources\/AlertStopIcon.icns\";;\n\t\"caution\") icon=\"\/System\/Library\/CoreServices\/CoreTypes.bundle\/Contents\/Resources\/AlertCautionIcon.icns\"\n\t\t#previous icon went away in later macOS RIP\n\t\t&#91; ! -f \"${icon}\" ] &amp;&amp; icon=\"\/System\/Library\/CoreServices\/Problem Reporter.app\/Contents\/Resources\/ProblemReporter.icns\";;\n\t\"note\") icon=\"\/System\/Library\/CoreServices\/CoreTypes.bundle\/Contents\/Resources\/AlertNoteIcon.icns\";;\nesac\n\n#make string only if path is valid (otherwise dialog fails)\nif &#91; -f \"${icon}\" ]; then\n\twithIcon_AS=\"with icon file (POSIX file \\\"${icon}\\\")\"\nfi\n\njamflog \"Prompting user: $(stat -f %Su \/dev\/console)\"\n\n#prompt the user, giving up and moving on after 1 day (86400 seconds)\n\/usr\/bin\/osascript &lt;&lt;-EOF\nactivate\nwith timeout of 86400 seconds\n\tdisplay dialog \"${message}\" with title \"${title}\" ${withIcon_AS} buttons {\"OK\"} default button \"OK\" giving up after \"86400\"\nend timeout\nEOF\n\nif &#91; -n \"${open_item}\" ]; then\n\tjamflog \"Opening: ${open_item}\"\n\topen \"${open_item}\"\nfi\n\nexit 0<\/code><\/pre>\n\n\n\n<p>Upload the above script to your Jamf (if you have one), label parameters 4, 5, 6, and 7 as:  <code>Message<\/code>, <code>Title<\/code>, <code>Icon Path<\/code>, an <code>Open After OK<\/code>,  respectively. If running local keep this in mind and put <code>1, 2, 3<\/code> as place holder arguments for the first three parameters.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full is-resized\"><a href=\"https:\/\/www.brunerd.com\/blog\/wp-content\/uploads\/simpleAlert-scriptparams-edit.png\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/www.brunerd.com\/blog\/wp-content\/uploads\/simpleAlert-scriptparams-edit.png\" alt=\"\" class=\"wp-image-1371\" width=\"273\" height=\"328\" srcset=\"https:\/\/www.brunerd.com\/blog\/wp-content\/uploads\/simpleAlert-scriptparams-edit.png 612w, https:\/\/www.brunerd.com\/blog\/wp-content\/uploads\/simpleAlert-scriptparams-edit-250x300.png 250w\" sizes=\"auto, (max-width: 273px) 100vw, 273px\" \/><\/a><figcaption class=\"wp-element-caption\">Jamf Script parameters names for <\/figcaption><\/figure>\n<\/div>\n\n\n<p>Let&#8217;s come up with an example message for our users. How about the common refrain coming from MacAdmins around the world:<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p>\ud83d\uded1 Stop.<br>\u2699\ufe0f Run your updates.<br>\ud83d\ude4f Thanks!<\/p>\n<\/blockquote>\n\n\n\n<p>Now if I tried to pass this text to a script within a Jamf policy, all those emoji would get mangled by Jamf as we saw above. Let&#8217;s use <code>shef<\/code> to encode the string for Jamf:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>% shef &lt;&lt;'EOF'\nheredoc&gt; \ud83d\uded1 Stop.\nheredoc&gt; \u2699\ufe0f Run your updates.\nheredoc&gt; \ud83d\ude4f Thanks!\nheredoc&gt; EOF\n\\xF0\\x9F\\x9B\\x91 Stop.\\n\\xE2\\x9A\\x99\\xEF\\xB8\\x8F Run your updates.\\n\\xF0\\x9F\\x99\\x8F Thanks!<\/code><\/pre>\n\n\n\n<p>For the example above I am using a &#8220;here-doc&#8221;; in practice you can <strong>simply supply <code>shef<\/code> a file path<\/strong>. The output encodes all the newlines in the ANSI-C style of <code>\\n<\/code> and the emoji have all been replaced by their UTF-8 encodings using the hexadecimal escaping of <code>\\x<\/code>. We can take this output and use it in our Jamf policy. The message will get through both textually and symbolically. As an additional feature bonus I added a simple <code>open<\/code> action in the script. Supply the file path to the Software Update panel and after the user clicks OK, it will be opened. Scope a policy like this to anyone with pending updates with Daily frequency:<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-large is-resized\"><a href=\"https:\/\/www.brunerd.com\/blog\/wp-content\/uploads\/SUExample-policy-3retry-dertail.png\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/www.brunerd.com\/blog\/wp-content\/uploads\/SUExample-policy-3retry-dertail-1024x671.png\" alt=\"\" class=\"wp-image-1375\" width=\"554\" height=\"363\" srcset=\"https:\/\/www.brunerd.com\/blog\/wp-content\/uploads\/SUExample-policy-3retry-dertail-1024x671.png 1024w, https:\/\/www.brunerd.com\/blog\/wp-content\/uploads\/SUExample-policy-3retry-dertail-300x197.png 300w, https:\/\/www.brunerd.com\/blog\/wp-content\/uploads\/SUExample-policy-3retry-dertail-768x504.png 768w, https:\/\/www.brunerd.com\/blog\/wp-content\/uploads\/SUExample-policy-3retry-dertail.png 1301w\" sizes=\"auto, (max-width: 554px) 100vw, 554px\" \/><\/a><figcaption class=\"wp-element-caption\">Escape the mangling in Jamf!<\/figcaption><\/figure>\n<\/div>\n\n\n<p>If you are just running the script locally and calling from the Terminal you&#8217;ll want to specify a quoting option. Exclamations are tricky and shells usually love to interpret trailing exclamations as a history expansion command, <code>shef<\/code> does its best to avoid this:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>bash-3.2$ shef -Qd &lt;&lt;'EOF'\n&gt; \ud83d\uded1 Stop.\n&gt; \u2699\ufe0f Run your updates.\n&gt; \ud83d\ude4f Thanks!\n&gt; EOF\n\"\\xF0\\x9F\\x9B\\x91 Stop.\\n\\xE2\\x9A\\x99\\xEF\\xB8\\x8F Run your updates.\\n\\xF0\\x9F\\x99\\x8F Thanks\"\\!\"\"\n\nbash-3.2$ .\/simpleAlert-AS.sh 1 2 3 \"\\xF0\\x9F\\x9B\\x91 Stop.\\n\\xE2\\x9A\\x99\\xEF\\xB8\\x8F Run your updates.\\n\\xF0\\x9F\\x99\\x8F Thanks\"\\!\"\" \"Software Updates Pending\" stop \"\/System\/Library\/PreferencePanes\/SoftwareUpdate.prefPane\"<\/code><\/pre>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full is-resized\"><a href=\"https:\/\/www.brunerd.com\/blog\/wp-content\/uploads\/SimpleSUAlert.png\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/www.brunerd.com\/blog\/wp-content\/uploads\/SimpleSUAlert.png\" alt=\"\" class=\"wp-image-1369\" width=\"467\" height=\"173\" srcset=\"https:\/\/www.brunerd.com\/blog\/wp-content\/uploads\/SimpleSUAlert.png 840w, https:\/\/www.brunerd.com\/blog\/wp-content\/uploads\/SimpleSUAlert-300x111.png 300w, https:\/\/www.brunerd.com\/blog\/wp-content\/uploads\/SimpleSUAlert-768x285.png 768w\" sizes=\"auto, (max-width: 467px) 100vw, 467px\" \/><\/a><figcaption class=\"wp-element-caption\">Our un-mangled output<\/figcaption><\/figure>\n<\/div>\n\n\n<h2 class=\"wp-block-heading\">Stop the Mangling Madness!<\/h2>\n\n\n\n<p>Wrapping things up: use <code><a rel=\"noreferrer noopener\" href=\"https:\/\/github.com\/brunerd\/shef\" target=\"_blank\">shef<\/a><\/code> to encode strings with Unicode so they survive storage in Jamf&#8217;s Latin1 encoded db tables. <code><a rel=\"noreferrer noopener\" href=\"https:\/\/github.com\/brunerd\/shui\" target=\"_blank\">shui<\/a><\/code> my fuller featured AppleScript dialog tool is ready to accept <code><code><a rel=\"noreferrer noopener\" href=\"https:\/\/github.com\/brunerd\/shef\" target=\"_blank\">shef<\/a><\/code><\/code> encoded strings. You can also use <code><code><a rel=\"noreferrer noopener\" href=\"https:\/\/github.com\/brunerd\/shef\" target=\"_blank\">shef<\/a><\/code><\/code> minify your text for your shell script. In fact, I used it to encode the help text file down to a single quoted line for use within itself.  That&#8217;s 1 happy customer and counting, hope you find it useful too!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Computers are &#8220;the equivalent of a bicycle for our minds&#8221; as Steve Job once said. Sure enough, like a bicycle, they also require maintenance! Sometimes that requires soliciting the user to take action. Asking at the right time is very important (see my post Don&#8217;t Be a Jerk) and so is brevity. I&#8217;ve found a [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":1369,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[19,46,12,56,39],"tags":[20,47,22,24,23],"class_list":["post-1364","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-bash","category-jamf","category-scripting","category-shef","category-zsh","tag-bash","tag-jamf","tag-scripting","tag-shell","tag-zsh"],"_links":{"self":[{"href":"https:\/\/www.brunerd.com\/blog\/wp-json\/wp\/v2\/posts\/1364","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.brunerd.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.brunerd.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.brunerd.com\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/www.brunerd.com\/blog\/wp-json\/wp\/v2\/comments?post=1364"}],"version-history":[{"count":17,"href":"https:\/\/www.brunerd.com\/blog\/wp-json\/wp\/v2\/posts\/1364\/revisions"}],"predecessor-version":[{"id":1392,"href":"https:\/\/www.brunerd.com\/blog\/wp-json\/wp\/v2\/posts\/1364\/revisions\/1392"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.brunerd.com\/blog\/wp-json\/wp\/v2\/media\/1369"}],"wp:attachment":[{"href":"https:\/\/www.brunerd.com\/blog\/wp-json\/wp\/v2\/media?parent=1364"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.brunerd.com\/blog\/wp-json\/wp\/v2\/categories?post=1364"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.brunerd.com\/blog\/wp-json\/wp\/v2\/tags?post=1364"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}