Reputation: 865
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:
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)
Upvotes: 0
Views: 1309
Reputation: 25032
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)
To test the demo.py
gist above you'll need to:
chmod
command and changing the file path as necessary.chmod +x /some/path/to/demo.py
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 (above) showing the input text includes many special characters.
Output:
Screenshot (above) showing that the special characters in the input text have been preserved in the subsequent dialog.
Upvotes: 1
Reputation: 724
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.
Upvotes: 0
Reputation: 3194
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:\test\test.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 "\n\n\n")
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 "\n\n\n")' -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.
Upvotes: 1