Applescript osacompile fails if user input contains apostrophe / single quote

applescriptscript

I have created a script which takes some user input from me (prompts with basic dialogs), then generates a standalone script that sends me a text message and an email, which I can then schedule as a calendar alert to trigger at a desired date/time.

Everything works fine, until there is an apostrophe/single quote in the user input. For example if my email body is:

Here's my email

the script will fail to run, with:

sh: -c: line 0: unexpected EOF while looking for matching `"'
sh: -c: line 1: syntax error: unexpected end of file

Here's the relevant code sction:

set savePath to (path to desktop as text) & _appName & ".app"

set code to "osacompile -e '
tell application \"Messages\" 
    set imessageservice to 1st service whose service type = iMessage 
    set iMessageBuddy to buddy \"" & _smsNumber & "\" of imessageservice 
    send \"" & _smsBody & "\" to iMessageBuddy
end tell
tell application \"Mail\"
    set theMessage to make new outgoing message with properties {visible:true, subject:\"" & _emailSubject & "\", content:\"" & quoted form of _emailBody & "\"} 
    tell theMessage 
        make new to recipient at end of to recipients with properties {address:\"" & _emailAddress & "\"} 
    end tell 
    send theMessage
end tell' -o " & quoted form of POSIX path of savePath

do shell script code

Again it works great as long as I don't pass in an apostrophe. In my searching, it appears that escaping apostrophes in script is really, really tricky to get right. I've tried numerous approaches (heredoc, multiple -e lines, quoted form of, etc.) but so far I haven't been able to get any to work. If anyone knows how to get around this I'd love to hear it.

Best Answer

If I was doing something such as this for myself, I would simply write the contents of "code" to a temporary text file then use osacompile to create the AppleScript application bundle and then delete the temporary text file. Doing it in this manner you should not have do deal with the single-quotes passed in a variable.

The following example AppleScript code is just a proof of concept. In Script Editor, just set an appropriate value for the smsNumber variable, for you, and then run the code.

It will create Foobar.app on your Desktop for you to then double-click and see that it sends you the message with the single-quotes in it.

set appName to "Foobar"
set smsNumber to ""
set smsBody to "Here's my email"

set tempAppleScriptCode to ¬
    "/tmp/" & appName & ".applescript"

set saveToPathFileName to POSIX path of ¬
    (path to desktop as text) & appName & ".app"

set myAppleScriptCode to "tell application \"Messages\" 
    set imessageservice to 1st service whose service type = iMessage 
    set iMessageBuddy to buddy \"" & smsNumber & "\" of imessageservice 
    send \"" & smsBody & "\" to iMessageBuddy
end tell"

set tempFileWasWritten to ¬
    (my writeTextToFile(myAppleScriptCode, tempAppleScriptCode, true))

tell application "System Events" to ¬
    set saveToPathFileNameExists to ¬
        (exists item saveToPathFileName)

set saveToPathFileName to saveToPathFileName's quoted form
set tempAppleScriptCode to tempAppleScriptCode's quoted form

if tempFileWasWritten then
    if not saveToPathFileNameExists then
        do shell script "osacompile -o" & space & saveToPathFileName & ¬
            space & tempAppleScriptCode & ";rm" & space & tempAppleScriptCode
    else
        display dialog "The file '" & saveToPathFileName & ¬
            "' already exists!" buttons {"OK"} default button 1 ¬
            with title "The target file already exists!"
    end if
else
    display dialog "An error occured writing to: '" & tempAppleScriptCode & ¬
        "'" buttons {"OK"} default button 1 with title "Error writing to file!"
end if


on writeTextToFile(theText, theFile, overwriteExistingContent)
    try
        set theOpenedFile to open for access POSIX file theFile with write permission
        if overwriteExistingContent is true then set eof of theOpenedFile to 0
        write theText to theOpenedFile starting at eof
        close access theOpenedFile
        return true
    on error
        try
            close access file theFile
        end try
        return false
    end try
end writeTextToFile