Search code examples
pythonmacospython-2.7applescript

How to make AppleScript ignore escape characters in string when making a AppleScript Note


I have a python 2.7.10 script that takes a user's input and inserts it into the name and body parameter of the make new note AppleScript.

The problem is that any escape or special characters get interpreted by AppleScript. I want all strings to be treated as raw and to ignore all escape characters and things like :\//tft// or c:\test\test.txt without giving me a Expected “"” but found unknown token. error or ignoring the t character after the \

The line of python looks like this:

cmd = "osascript -e 'tell application \"Notes\" \n tell account \"iCloud\" \n make new note at folder \"Notes\" with properties {name:\"%s\", body:\"%s\"} \n end tell \n end tell'" % (header, body)

... where header and body are the user supplied strings.

But for manual testing I use the Script Editor to quickly reproduce the error.

tell application "Notes"
  tell account "iCloud"
    make new note at folder "Notes" with properties {name:"myname", body:"c:\test\test.txt"}
  end tell
end tell

This particular note ends up looking like this:

enter image description here

I know that I can use \\ instead of \ but I don't want users to have to sanitize all their input as they could be copy pasting from a large body of text. Think of a log file or paste-bin style body of text.

Is there a way to programmatically sanitize the user's input?

NOTE:

This is NOT a duplicate of this question because I've tried replacing the string variable header and body with eval(header) and eval(body) but that didn't work. I also tried .decode('string_escape') to no avail. Finally, I tried this with no luck either:

d = header.escape('\w[0-9]')
header = d.decode('string_escape')

I think this has to do with AppleScript's ability to accept this string and not just Python's ability to sanitize it using the above functions.

UPDATE

I am capturing user input using a dialog box with code like this:

cmd = "osascript -e \'set theString to text returned of (display dialog \"Please Enter The Note To Add To Your iCloud Notes \" with icon file \"%s\" default answer \"\n\n\n\" buttons {\"OK\",\"Cancel\"} default button 1) \'"  % (appicon_abs_path_apple)
note = run_script(cmd)

Solution

  • In the demo.py gist below there is a custom sanitize function which is utilized to escape any backslash (\) or double quote (") character(s) that the user may enter into the dialog box. It's necessary to escape these characters this way in your .py file prior to subsequently passing them to osascript.

    demo.py

    #!/usr/bin/env python
    
    from subprocess import Popen, PIPE
    
    
    def sanitize(str):
        return str.replace("\\", "\\\\") \
                  .replace("\"", "\\\"")
    
    
    def run_script(script):
        p = Popen(['osascript', '-'], stdin=PIPE, stdout=PIPE, stderr=PIPE)
        stdout, stderr = p.communicate(script)
        return stdout
    
    appicon_abs_path_apple = "Applications:Stickies.app:Contents:Resources:Stickies.icns"
    
    input_dialog = """
    tell application "Finder"
      activate
      set mssg to "Please Enter The Note To Add To Your iCloud Notes"
      set theString to text returned of (display dialog mssg with icon file "%s" \
          default answer "\n\n\n" buttons {"OK", "Cancel"} default button 1)
    end tell
    """ % (appicon_abs_path_apple)
    
    # Notice in the following line we `sanitize` the text characters returned
    # from the input dialog before assigning it to the `note` variable.
    note = sanitize(run_script(input_dialog))
    
    # -------------------------------
    
    # Let's test that any special characters have been preserved
    show_input = """
    tell application "Finder"
      activate
      display dialog "%s"
    end tell
    """ % (note)
    
    run_script(show_input)
    

    Notes:

    The show_input and run_script(show_input) parts at the end of demo.py are for test purposes only. This should be replaced with the AppleScript code necessary to make a new note. For instance:

    # ...
    # ...
    
    note = sanitize(run_script(input_dialog))
    
    # header = sanitize(header)
    
    make_note = """
    tell application "Notes"
      tell account "iCloud"
        make new note at folder "Notes" with properties {name:"%s", body:"%s"}
      end tell
    end tell
    """ % (header, note)
    
    run_script(make_note)
    

    You'll probably also want to consider passing the text string assigned to the header variable through the sanitize function (as per the line currently commented out in snippet above), however I'm unsure from your question where that data comes from. If it's user generated then definitely sanitize it.

    Also note how in the aforementioned gist we wrap the Applescript code in triple double quotes (""") - this helps to make the code more readable and avoids the need for additional escaping and \n character(s), unlike the following:

    make_note = "osascript -e 'tell application \"Notes\" \n tell account \"iCloud\" \n make new note at folder \"Notes\" with properties {name:\"%s\", body:\"%s\"} \n end tell \n end tell'" % (header, body)
    

    Demo:

    To test the demo.py gist above you'll need to:

    1. Ensure the file is executable by running something like the following chmod command and changing the file path as necessary.
    chmod +x /some/path/to/demo.py
    
    1. Also, you'll probably want to change the HFS path path (i.e. a colon separated path) which is assigned to the appicon_abs_path_apple variable - currently it utilizes the icon for the Stickies application.

    Input:

    The following arbitrary text string containing special characters was entered into the dialog:

    :\//tft//
    c:\test\test.txt
    c:\\quux\\foo.md
    '¡"€#¢∞§¶•ª⁄™‹›fifl‡°·
    

    Screenshot showing input text

    Screenshot (above) showing the input text includes many special characters.

    Output:

    Screenshot showing that the input text is preserved after osascript

    Screenshot (above) showing that the special characters in the input text have been preserved in the subsequent dialog.