Post

Shell Scripting Notes

I have been building some automation tools for creating images from other images and text. The next step is to get those images into an application that will allow for scheduling or posting. I am not entirely sure where I want to go with them, so I am starting with the Notes application. For people using the Apple ecosystem, Notes is a great way to share data: images, urls, text, etc. between the Mac and iPhone.

Lemme tell ya, you can search for “command line notes” in all of its iterations all day and not find one single helpful post if you are wanting to interact with the MacOS application “Notes” from the command line. As far as I have found, there are no serious command line tools for Notes. Although, I did find many neat approaches to taking notes from the command line - like the Noted approach that I already have.

It turns out that the Notes application does have fairly good code coverage through Applescript. I snooped around in the Script Editor application Library for Notes to see what kinds of things should be available.

While it is great that there are lots of scripts, I was briefly at a loss because my application for creating images thus far has been shell script. I was hoping to create a single shell script such that I can say: “hey, take these images and this list of strings, make images with strings on them in the right size, and send each to a new note with the string and the image.” The problem here being that I now need to figure out how to access Notes from the shell. I struggled.

At first, I tried just typing “Notes” from the command line, but that just sent be to my notes folder. Open notes and Open Notes.app faired no better. I began to feel a bit like a crazy person.

jess:~/ $ notes  
jess:notes/ $ cd ..                                              
jess:~/ $ open Notes                                             
jess:~/ $ open Notes.app                                        
  The file /Users/jess/Notes.app does not exist.
jess:~/ $ open Applications/Notes.app                            
  The file /Users/jess/Applications/Notes.app does not exist.
jess:~/ $ open /Applications/Notes.app       

I finally got Notes to open - phew! Unfortunately, I didn’t really want to open Notes, I wanted to be able to create new notes. For this, I was still at a loss. The best that I could come up with is that I would need to write an Applescript and then call it from my shell script. Not ideal for testing. I like to test with simple things. So, I started searching for how to run an Applescript from shell. It is a thing - you can do it with osascript! Seriously, the curious should stop reading right now and go type man osascript at the command line.

It took me a few tries to get it to work, the trick is to avoid any actual new lines and to use the -e and a properly treated quoted string for each script new line. Since Applescript is line sensitive, as in you open a block in a line and all of the code is in that block and then you close the block with a separate line.

jess:~/ $ osascript -e 'tell application "Notes"'\               
-e 'make new note in folder "Notes" with properties {name:"TEST", body:"this is some text that should be in the note that I just made"}'\
-e  'end tell'
  26:26: syntax error: Expected end of line but found end of script. (-2741)
jess:~/ $ osascript -e 'tell application "Notes"\                
-e make new note in folder "Notes" with properties {name:"TEST", body:"this is some text that should be in the note that I just made"}\
-e  end tell'
  24:25: syntax error: Expected end of line but found unknown token. (-2741)
jess:~/ $ osascript -e 'tell application "Notes"                 
-e make new note in folder "Notes" with properties {name:"TEST", body:"this is some text that should be in the note that I just made"}
-e  end tell'
  28:31: syntax error: Expected end of line but found “set”. (-2741)
jess:~/ $ osascript -e 'tell application "Notes"' -e 'make new note in folder "Notes" with properties {name:"TEST", body:"this is some text that should be in the note that I just made"}' -e 'end tell'
  note id x-coredata://DD97C356-E2E8-4F17-B543-9E76E371AB4B/IMAPNote/p8      

The last one finally works! I share all of my mistakes here, so that you can see what works and does not work and understand what I meant above about proper lines/quotes.

Let’s break apart this code block within tell.

  • make new note is saying that we want to create a new note in the application Notes.
  • in folder "Notes" is telling it the folder. This may not be completely obvious. I have a folder called “Notes” inside of two accounts in my Notes application (both Google and iCloud), and it just went to the Google one alphabetically. You can use any folder name, but the folder has to exist. that is a potential gotcha when coding for other people - see my hint below
  • with properties {name:[], body:[]} is telling it what we want to use for the title and the body. The name will be the title of the note. It is not required, but if you don’t include it, the title of the note will be “New Note” which makes it hard to distinguish all of your notes.

Hint Here is one way to ensure that your folder exists first or make it.

osascript -e 'tell application "Notes"' -e 'tell account "iCloud"' -e 'if not (exists folder "Testy") then' -e 'make new folder with properties {name:"Testy"}' -e 'end if' -e 'end tell' -e 'end tell'

Wait, weren’t we working with images?

Oh, yes we were.

Images are a real bugaboo with Notes. The main problem is that images are binary files, and that seems to be difficult for the script. Somehow, getting images from the web is fine. You need to include them in an image tag, so add a little bit of html to the body that we have here, and otherwise, the approach is the same.

osascript -e 'tell application "Notes"' -e 'make new note in folder "Testy" with properties {name: "Web", body:"<div>This is an image from the web</div><img src=\"http://jessachandler.com/assets/img/faces/jessiceland.jpg\"/><div>There is an image above this</div>"}' -e 'end tell'

I use fine in italics because it works, but the image does not show up until I open the Note on my iPhone, and even then, only if the Notes application was not open when I started.

So far, no matter what I do, I have not figured out how to include a local image in a Note in the Notes application. This holds even if I create a local server say with python3 -m http.server and then call the image from localhost, like so:

osascript -e 'tell application "Notes"' -e 'make new note in folder "Testy" with properties {name: "Localhost", body:"<html><div>local image</div><img src=\"http://localhost:8000/jessiceland.jpg\"/><div>There should be an image above this</div></html>"}' -e 'end tell'

I really expected to be able to do use a local image in the same way as a hosted image, as shown below. That did not work, so I tried locally hosting the image.

osascript -e 'tell application "Notes"' -e 'make new note in folder "Testy" with properties {name: "Local", body:"<html><div>local image</div><img src=\"file:///Users/jess/jessiceland.jpg\"/><div>there should be an image above this</div></html>"}' -e 'end tell'

I AM WALKING AWAY NOW TO THINK ABOUT THIS AND TO NOT THROW MY MAC. I’LL UPDATE WHEN I FIND AN ANSWER Also, shoutout to Jeffrey on twitter who chatted me up about this when Apple support was being useless. Thanks, man.

errata

bonus note We can add set theNoteID to in front of make if we need to access the note id that is created afterwards, so maybe we want to make an extension that will tell the user that the note was created - we would probably want to use theNoteID to check that the note exists before sending them a confirmation.

This post is licensed under CC BY 4.0 by the author.