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

Question:

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:testtest.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:testtest.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 "nnn" buttons {"OK","Cancel"} default button 1) '"  % (appicon_abs_path_apple)
note = run_script(cmd)
Asked By: frakman1

||

Answers:

It seems to me this is a python question more than an AppleScript question. I mean, if you were asking for the body text through AppleScript, using, say:

display dialog "Enter Note" default answer ""

AppleScript would automatically escape any problem characters (i.e., if they entered c:testtest.txt, the result of that display dialog command would be c:\test\test.txt). It seems to me you are getting the text from some other source and using python to send it on through AppleScript. That means you have to escape it all in python before sending it to osascript.

Escaping is fairly easy, analytically: replace every backslash with a double-backslash. In python I think that would be something like:

body.replace('','\')

Depending on the text you’re receiving you might need to tweak it some. For instance, if you have bald double-quotes you’ll need to escape them:

body.replace('"','"')

and if you have plain-text tabs, carriage returns, or line feeds (t, r, n) built into the text you may have to find some way of avoiding escaping those (you may have to dig into regular expressions for that). But the idea is to put the AppleScript escapes in first, in python, and then call your osacript command.

EDIT:

Now that I know that you’re getting the user text from display dialog, I’m going to suggest you cut out the python middle-man and handle the whole thing in AppleScript. The script you want is this:

set txt to text returned of (display dialog "Please Enter The Note To Add To Your iCloud Notes" default answer "nnn")
tell application "Notes"
    tell account "iCloud"
        make new note at folder "Notes" with properties {name:"myname", body:txt}
    end tell
end tell

You can either save this as a script file and call it from osascript — osascript scriptName.scpt — or you can enter it directly into osascript using sequential -e options:

osascript -e 'set txt to text returned of (display dialog "Please Enter The Note To Add To Your iCloud Notes" default answer "nnn")' -e 'tell application "Notes"' -e 'tell account "iCloud"' -e 'make new note at folder "Notes" with properties {name:"myname", body:txt}' -e 'end tell' -e 'end tell'

if you need to pass arguments into the script (like, say, a variable for the username or a path to an icon) you’ll need to use an explicit run handler. See ‘man osascript,’ or ask and I’ll show you how.

Answered By: Ted Wrigley

Here’s how to parameterize an AppleScript run via osascript:

#!/usr/bin/env python

import subprocess

header ='my name'
body = 'some text'

subprocess.call(['osascript', 
    '-e', 'on run {theHeader, theBody}',
    '-e', ' tell application "Notes"',
    '-e', '  tell account "iCloud"',
    '-e', '   make new note at folder "Notes" with properties {name:theHeader, body:theBody}',
    '-e', '  end tell',
    '-e', ' end tell',
    '-e', 'end run', '--', header, body])

For passing more complex data, consider py-applescript or PyObjC + AppleScript-ObjC.

Answered By: foo

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 "nnn" 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:testtest.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.

Answered By: RobC