{"id":17055,"date":"2017-01-30T12:56:50","date_gmt":"2017-01-30T17:56:50","guid":{"rendered":"http:\/\/mjtsai.com\/blog\/?p=17055"},"modified":"2017-01-30T22:22:03","modified_gmt":"2017-01-31T03:22:03","slug":"processing-the-selected-text-via-script","status":"publish","type":"post","link":"https:\/\/mjtsai.com\/blog\/2017\/01\/30\/processing-the-selected-text-via-script\/","title":{"rendered":"Processing the Selected Text via Script"},"content":{"rendered":"<p>It seems like it should be easy to run the selected text in any app through a shell script, but I&rsquo;ve run into a surprising number of issues doing this. I&rsquo;ve long used <a href=\"http:\/\/mjtsai.com\/blog\/2012\/07\/29\/thisservice-3-0\/\">ThisService<\/a> to create system services out of shell scripts that sort lines, smarten quotes, and apply title case. Then I would assign keyboard shortcuts in System Preferences and have easy access to the scripts from any app.<\/p>\n<p>Over the last few months, keyboard shortcuts for system services (and PDF services) have become unreliable. They still show up in System Preferences, but they usually don&rsquo;t work. Often, System Preferences forgets them. It also forgets which services I&rsquo;ve enabled and disabled, hiding my favorite scripts and bringing back dozens of commands that I never use. This makes the Services contextual menu unwieldy. The Keyboard preferences pane is awkward to use, so that it takes a long time to go through the list checking and unchecking the appropriate boxes.<\/p>\n<p>My first thought was to use LaunchBar, which I knew could get and replace the selected text (either using a <a href=\"https:\/\/www.obdev.at\/resources\/launchbar\/help\/RunningUnixExecutables.html\">script<\/a> or a <a href=\"https:\/\/www.obdev.at\/resources\/launchbar\/help\/index.php?chapter=Services\">service<\/a>). However, this takes multiple steps: long Command-Space to load the text, Tab, type the name of the script\/service, Command-Shift-C to copy the result text and paste it back. This is fine for occasionally used scripts, where keyboard shortcuts would not be practical, but not for ones I use many times per day.<\/p>\n<p>How about using <a href=\"https:\/\/red-sweater.com\/fastscripts\/\">FastScripts<\/a> to assign keyboard shortcuts and run the scripts? Unfortunately, most applications are not scriptable enough to access the selected text. I wrote a script to use GUI scripting to invoke my service from the Services menu:<\/p>\n\n<pre>my runServiceNamed(\"Title Case\")\n\non runServiceNamed(_serviceName)\n    tell application \"System Events\"\n        set _frontApp to first application process whose frontmost is true\n        tell _frontApp\n            set _appMenu to menu 2 of menu bar 1\n            set _servicesMenu to menu 1 of menu item \"Services\" of _appMenu\n            set _menuItem to menu item _serviceName of _servicesMenu\n            click _menuItem\n        end tell\n    end tell\nend runServiceNamed<\/pre>\n\n<p>However, GUI scripting the menu bar doesn&rsquo;t seem to work in MarsEdit, one of the apps where I would use the scripts most frequently.<\/p>\n\n<p>I ended up giving up on services entirely and used GUI scripting and the clipboard to get and set the selected text:<\/p>\n<pre>my process(\"\/Users\/mjt\/Library\/Application Support\/BBEdit\/Text Filters\/titlecase.py\")\n\non process(_scriptPath)\n    set _savedClipboard to the clipboard\n\n    -- Copy selected text\n    tell application \"System Events\" to keystroke \"c\" using {command down}\n    delay 1 -- Without this, the clipboard may have stale data.\n\n    -- Clipboard has Mac line breaks, but script requires Unix.\n    set _script to \"pbpaste | tr '\\\\r' '\\\\n'  | \" &amp; _scriptPath's quoted form &amp; \" | pbcopy\"\n    do shell script _script\n\n    -- Paste to replace selected text\n    tell application \"System Events\" to keystroke \"v\" using {command down}\n    delay 1 -- Without this, may restore clipboard before pasting.\n\n    set the clipboard to _savedClipboard\nend process\n<\/pre>\n<p>This is ugly and has some downsides: there are delays necessitated by the GUI scripting, and certain types of clipboard data are not preserved. But I can assign a single keyboard shortcut, which FastScripts won&rsquo;t forget, and it works reliably.<\/p>\n<p>Note: I originally wrote the script to use <code>the clipboard<\/code> instead of <code>pbcopy<\/code> and <code>pbpaste<\/code>. The latter are better because that way the text doesn&rsquo;t end up in an AppleScript variable. Getting the text from AppleScript into stdin seems to require using either <code>echo<\/code>, which is subject to the shell&rsquo;s command length limit, or writing to a temporary file.<\/p>","protected":false},"excerpt":{"rendered":"<p>It seems like it should be easy to run the selected text in any app through a shell script, but I&rsquo;ve run into a surprising number of issues doing this. I&rsquo;ve long used ThisService to create system services out of shell scripts that sort lines, smarten quotes, and apply title case. Then I would assign [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"apple_news_api_created_at":"","apple_news_api_id":"","apple_news_api_modified_at":"","apple_news_api_revision":"","apple_news_api_share_url":"","apple_news_coverimage":0,"apple_news_coverimage_caption":"","apple_news_is_hidden":false,"apple_news_is_paid":false,"apple_news_is_preview":false,"apple_news_is_sponsored":false,"apple_news_maturity_rating":"","apple_news_metadata":"\"\"","apple_news_pullquote":"","apple_news_pullquote_position":"","apple_news_slug":"","apple_news_sections":"\"\"","apple_news_suppress_video_url":false,"apple_news_use_image_component":false,"footnotes":""},"categories":[2],"tags":[159,650,131,125,467,30,1381,1470],"class_list":["post-17055","post","type-post","status-publish","format-standard","hentry","category-technology","tag-applescript","tag-applescriptobjc","tag-bug","tag-keyboardshortcuts","tag-launchbar","tag-mac","tag-macos-10-12","tag-system-services"],"apple_news_notices":[],"_links":{"self":[{"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/posts\/17055","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/comments?post=17055"}],"version-history":[{"count":1,"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/posts\/17055\/revisions"}],"predecessor-version":[{"id":17056,"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/posts\/17055\/revisions\/17056"}],"wp:attachment":[{"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/media?parent=17055"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/categories?post=17055"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/tags?post=17055"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}