15

I use the current Notes app on my iOS devices. It syncs nicely to my desktop. However there I prefer to work on text files usually in the command line.

Particularly I am looking for a way to export notes to a text format and to create or append a note from a text file from the command line.

Is there any information how to interact with these notes?

I found two possible ways to interact, but both turned out useless for my case:

  1. In iOS it is possible to create an action that converts notes to markdown. Unfortunately attachments get converted to links that lead nowhere. My notes contain lots of links from Safari, therefore it is not useful for me. Also wasn't able to find a way to define this action on desktop.

  2. I recalled Notes could also store in email accounts. Tried that. Turns out I can't select this destination from the sharing button. Also despite being HTML in the mail folder formatting text is no longer supported.

Description of my workflow

On desktop I do most coding in vim. This is my preferred way to edit text. Each project is managed by git. Usually in each project I have a markdown files in a folder containing ideas and notes to the code. These files I would like to make in some way available in the Notes app. I don't need to have all features of markdown, but some highlighting and inclusion of links would be nice.

On Mobile I do most research. I prefer to use the notes app as I can add text snippets and URLs quickly to any notes. So any app exporting the notes would need to reasonable export them.

Currently, I copy and paste between the two formats. This is not very satisfying. I imagine if I could get some way to import/export to the Notes app I could write scripts that would allow me to sync my local markdown notes.

4
  • please explain more details on how you work with text files from the command line Commented Jul 28, 2020 at 21:19
  • I would run nedit from the command line to edit the file. But that's not really using the command line, I'm just using an editor that is easy to access from command line.
    – Natsfan
    Commented Jul 29, 2020 at 17:31
  • 1
    You can access it via AppleScript (e.g. osascript -e 'tell application "Notes"' -e 'get get the body of the note "Thingamajigs" of the folder "Notes"' -e 'end tell'), but unfortunately even though the result is nominally in HTML format, it doesn't seem to contain the full format/link/etc info. But give it a shot and see if you get anything useful. Commented Jul 29, 2020 at 21:51
  • @GordonDavisson that seems very promising. Too bad I'm completely new with AppleScript.
    – bdecaf
    Commented Jul 31, 2020 at 9:54

5 Answers 5

2

It appears that Notes are stored in a SQLite DB in the folder:

~/Library/Containers/com.apple.notes/Data/Library/Notes

The sqlite3 command is included with MacOS.

Who knows what format the notes are in -- I doubt it's markdown -- so you'll probably have to convert between this internal format and markdown. Also, there are other files in the directory that SQLite might or might not maintain and any files in the directory may be accessed at any time by the system daemons that sync things with iCloud. Not sure if SQLite supports file locking directly.

EDIT:

You seem to have found nothing in those folders. I am on Catalina, and when I look at NotesV7.storedata I see multiple tables:

ATRANSACTION        ZATTACHMENT         ZNOTEBODY           Z_MODELCACHE      
ATRANSACTIONSTRING  ZFOLDER             ZOFFLINEACTION      Z_PRIMARYKEY

And in ZNOTE I see what look like note titles and other info and ZNOTEBODY has HTML-formatted notes. So that would answer the question: it evidently stores notes internally in HTML format.

EDIT2:

OK, I only see notes in those tables that correspond to On My Mac notes, not iCloud notes.

4
  • That is a good idea, but doesn't apper to be useful for me. I investigated with SQLiteStudio. The only tables with more than one row are called ACHANGE and ATRANSACTION - and they don't hold any content - just times and indices that don't make sense to me, the stored blobs are either NULL or just 2 byte long. In frustration grepped the files for the words of a note. I also tried similar folders such as ~/Library/Containers/com.apple.Notes/Data/CloudKit and ~/Library/Containers/com.apple.Notes.datastore - so far couldn't find the location of the content.
    – bdecaf
    Commented Jul 29, 2020 at 17:14
  • @bdecaf I've edited with some more details. Works for me. Are you on MacOS Catalina?
    – Wayne
    Commented Jul 29, 2020 at 19:01
  • Must be an iCloud thing. I'm also on Catalina. I have these tables - they just contain 0 rows.
    – bdecaf
    Commented Jul 29, 2020 at 20:00
  • @bdecaf OK, I'm only seeing "On My Mac" notes in there, not "iCloud" notes. I guess iCloud Notes live in some mysterious place other than iCloud Drive. And I can't figure out where iCloud Drive exists anyhow.
    – Wayne
    Commented Jul 29, 2020 at 21:20
2

There's also the mac_apt MacOS Artifact Parsing Tool - It provdides for extraction of Notes, amoungst many other data types. The authour has also written a blog post on extracting Notes from the Notes DB.

1
  • An interesting find. Unfortunately a bit too technical to be useful for me.I worry it suffers from the same problem as the SQLite answer (the storedata file does not contain the notes). The post being from 2018 I'm uncertain how it addresses Big Sur (now) and iCloud .
    – bdecaf
    Commented Nov 19, 2020 at 8:45
2

MacNotesApp

Found this today: https://github.com/RhetTbull/macnotesapp

Work with Apple MacOS Notes.app from the command line. Also includes python interface for scripting Notes.app from your own python code. Interactive browsing of notes in a TUI (Terminal User Interface? Textual User Interface?) coming soon!

1
  • This is a great find! Looks promising, but the documentation is a bit rough on how the export works. Unfortunately I switched machines and only use the Mac rarely, so will take some time to see if it solves my scratch.
    – bdecaf
    Commented Aug 7, 2023 at 9:08
1

There is an application called Note2txt which will convert your notes from the Notes app to text files. One file for each note. It creates a file for all your notes and writes them into a directory of your choice. It runs pretty quickly taking about 30 seconds for my 700+ Notes.

2
  • The note2txt app is interesting. Unfortunately it tells me it is not available in my country/region (Europe). I would need to confirm whether Safari links are properly exported. (In the plain text the note-to-markdown action produces they are unusable.)
    – bdecaf
    Commented Jul 29, 2020 at 17:17
  • interesting. Don't know why it wouldn't be available. Sorry. I didn't know that.
    – Natsfan
    Commented Jul 29, 2020 at 17:27
1

I tried doing this via sqlite and some other ways. Eventually settled on AppleScript. The script below lets you pick one or all Notes folders, and a place to save their notes. Then it puts them there in one big HTML file, with each one a <div> with a bunch of attributes for mod time, id, etc. Attachments become links within lists items of a list placed at the end of the note (at least in the version I've been looking at, images are stored in a folder near the sqlite file).

This is a slightly awkward way to export, but works pretty well for some purposes.

-- Export Notes.scpt: 2018-09-18.
-- Save all Apple Notes, or one category of them, to HTML.
-- The notes all go to one file; split later if desired.
--      
tell application "Notes"
    activate

    set folderNum to my pickFolderNum()
    display dialog "Chose: " & (folderNum as string)

    set resp to choose file name with prompt "Where should I save the Notes?" default name "NotesExport.htm" default location (the path to the desktop)
    set tgtFile to (POSIX path of resp)
    set fh to open for access file resp with write permission
    set eof of the fh to 0

    set foldersDone to 0
    set totalNotesDone to 0
    if (folderNum > 0) then
        set nWritten to my doOneFolder(folderNum, fh)
        set totalNotesDone to totalNotesDone + nWritten
        set foldersDone to 1
    else
        repeat with folderNum from 1 to the number of folders
            set nWritten to my doOneFolder(folderNum, fh)
            set totalNotesDone to totalNotesDone + nWritten
            set foldersDone to foldersDone + 1
            -- exit repeat -- for testing
        end repeat
    end if

    close access fh
    display dialog ("Done with " & foldersDone & " folders, " & totalNotesDone & " notes.")
end tell

on doOneFolder(folderNum, fh)
    tell application "Notes"
        set f to folder folderNum
        set fname to (the name of f) as string
        set fcount to the number of notes of f
        display dialog ("Exporting Notes folder '" & fname & "', notes: " & fcount)
    
        set buf to "<!DOCTYPE html>" & return & ¬
            "<html><head><title>Exported Notes: " & fname & "</title></head>" & return & ¬
            "<body>" & return
        write buf to fh
    
        set notesDone to 0
        repeat with j from 1 to fcount
            set the cursor to busy
            set theNote to note j of f
        
            set atts to ""
            if (the number of attachments of theNote > 0) then
                set atts to "<ul class='attachments'>" & return
                repeat with k from 1 to the number of attachments of theNote
                    set att to (attachment k of theNote)
                    set aname to the name of att
                    set aurl to the content identifier of att
                    set atts to atts & "<li><a href='" & aurl & "'>" & aname & "</a></li>" & return
                end repeat
                set atts to atts & "</ul>" & return
            end if
        
            set nm to (the name of theNote) as string
            set idv to the id of theNote as string
            set idVal to my makeAttr("id", idv)
            -- display dialog ("id: " & idVal)
            -- set cont to (the container of theNote) as string -- a folder
            set bod to (the body of theNote) as string
            -- display dialog "Note #" & j & ": " & bod
            set cdate to the creation date of theNote
            set mdate to the modification date of theNote
            set cdates to my makeAttr("cdate", my isoDate(cdate))
            set mdates to my makeAttr("mdate", my isoDate(mdate))
            set clas to my makeAttr("class", "note")
            -- set hasPW to (the password protected of theNote) as string
        
            --
            -- copy/paste into TextEdit, then save as HTML?
            --
            set buf to return & "<div" & idVal & cdates & mdates & clas & ">" & return & ¬
                "<h1>" & nm & "</h1>" & return & ¬
                bod & return & atts & "</div>" & return
            write buf to fh
            set notesDone to notesDone + 1
        end repeat
    end tell
    return notesDone
end doOneFolder

-- Escape, quote, and assemble as needed for an attribute.
on makeAttr(nam, val)
    set s to ""
    try
        if val is "" then
            return ""
        end if
        set s to " " & nam & "=" & quote & val & quote
    end try
    return s
end makeAttr

-- Turn chars not allowed inside attributes, into character refs.
--
on escapeAttr(s)
    set s2 to ""
    repeat with i from 1 to length of theName
        set c to character i of s
        if (c is "<") then
            set s2 to s2 & "&lt;"
        else if (c is "&") then
            set s2 to s2 & "&amp;"
        else if (c is quote) then
            set s2 to s2 & "&quot;"
        else
            set s2 to s2 & c
        end if
    end repeat
    return s2
end escapeAttr

-- Make a yyyy-mm-ddThh:mm:ss ISO date/time string.
--
on isoDate(dat)
    -- display dialog "Date is: " & (dat as string)
    set {year:y, month:m, day:d} to dat
    set {hours:hr, minutes:mn, seconds:sc} to dat
    set d2 to (y * 10000 + m * 100 + d) as string
    set d3 to (text 1 thru 4 of d2) & "-" & (text 5 thru 6 of d2) & "-" & (text 7 thru 8 of d2)
    set t2 to (hr * 10000 + mn * 100 + d) as string
    set t3 to (text 1 thru 2 of t2) & ":" & (text 3 thru 4 of t2)
    if (length of t2 ≥ 6) then
        set t3 to t3 & ":" & (text 5 thru 6 of t2)
    end if
    return d3 & "T" & t3
end isoDate

on pickFolderNum()
    set buf to my getFolderList()
    set buf2 to "Enter the folder number you want, or 0 for all:" & return & buf

    set theResponse to display dialog buf2 default answer "" buttons {"Cancel", "Continue"} default button "Continue"
    --> {button returned:"Continue", text returned:"Jen"}
    return (text returned of theResponse) as integer
end pickFolderNum

on getFolderList()
    tell application "Notes"
        activate
        set buf to ""
        repeat with i from 1 to the number of folders
            set fname to the name of folder i as string
            set buf to buf & (i as string) & ": " & fname & return
        end repeat
        return buf
    end tell
end getFolderList
1
  • 2
    Thanks for sharing. Too bad it's still so cumbersome. I really had hoped with all the updates to Notes we would finally see a usable API ;)
    – bdecaf
    Commented Nov 12, 2022 at 8:01

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .