Suprisingly the Notes application that comes with MacOS lack of export/import functionality.
One workaround of that limitation is using
Automator. However it exports only the notes text
without the attachments.
Exporting notes from Mac 1:
- Open Automator from the applications.
- Create a new Workflow
- Choose Run JavaScript
- Copy/paste following export script:
function run(input, parameters) {
const hexEncode = function(s){
let result = "";
for (let i=0; i < s.length; i++) {
hex = s.charCodeAt(i).toString(16);
result += ("0"+hex).slice(-2);
}
return result;
};
const pad = function(n, size) {
let s = n + "";
while (s.length < size) {
s = "0" + s;
}
return s;
};
const app = Application.currentApplication();
app.includeStandardAdditions = true;
const notesApp = Application('Notes');
notesApp.includeStandardAdditions = true;
const notes = notesApp.notes;
const selectedNotes = app.chooseFromList(notes.name(), { withPrompt: "Select notes to export ?", multipleSelectionsAllowed: true });
if (selectedNotes) {
var destFolder = app.chooseFolder().toString();
let exported = 0;
if (destFolder) {
for(let i = 0; i < notes.length; i++) {
if (selectedNotes.indexOf(notes[i].name()) > -1) {
const fileName = destFolder + "/"
+ pad(i, 9) +
+ "." + hexEncode(notes[i].name())
+ "." + notes[i].modificationDate().getTime()
+ "." + notes[i].modificationDate().getTime()
//+ "." + hexEncode(notes[i].id())
+ ".html";
var f = app.openForAccess(Path(fileName), { writePermission: true });
app.setEof(f, { to: 0 });
app.write(notes[i].body(), {to: f});
app.closeAccess(f);
//const attachments = notes[i].attachments();
//if (attachments.length > 0) {
// attachments[0].show();
// app.displayDialog(attachments[0].id().toString());
//}
exported++;
}
}
app.displayDialog("Done. Notes exported: " + exported);
}
}
return input;
}
- Click Run and select notes to be exported and destination directory (preferable empty one)
- Copy all notes files to destination Mac 2 (keep the original file names).
Importing notes to Mac 2:
- Open Automator from the applications.
- Create a new Workflow
- Choose Run JavaScript
- Copy/paste following export script:
function run(input, parameters) {
'use strict';
const hexDecode = function(s) {
const hexes = s.match(/.{1,2}/g) || [];
let res = hexes.map(h => String.fromCharCode(parseInt(h, 16))).join("");
return res;
}
const epochToStr = function(s) {
const lz = function (n) { return (n <=9 ? "0" : "") + n };
const d = new Date(parseInt(s))
return d.getFullYear() + "-" + lz(d.getMonth() + 1) + "-" + lz(d.getDate()) + " " + lz(d.getHours()) + ":" + lz(d.getMinutes());
}
const app = Application.currentApplication();
app.includeStandardAdditions = true;
const response = app.displayDialog("Prefix note titles with", {
withIcon: "note",
buttons: ["Modification date", "Note index", "Don't prefix"],
defaultButton: "Modification date"
});
//0index.1title.2create.3modify.
let prefixColumn = -1;
switch (response.buttonReturned) {
case "Modification date": prefixColumn = 3; break;
case "Creation date": prefixColumn = 2; break;
case "Note index": prefixColumn = 0; break;
}
const finder = Application('System Events');
const folderName = app.chooseFolder().toString();
const fileNames = finder.folders.byName(folderName).diskItems.name();
const notesApp = Application('Notes');
notesApp.includeStandardAdditions = true;
const prefix = function (fileName) {
const titleItems = fileName.split('.');
if (prefixColumn >= 0) {
titleItems[2] = epochToStr(titleItems[2]);
titleItems[3] = epochToStr(titleItems[3]);
return titleItems[prefixColumn];
} else {
return titleItems[0];
}
}
const prefixCoeff = prefixColumn == 2 || prefixColumn == 3 ? 1 : -1;
fileNames.sort(function(a,b) { return prefixCoeff * prefix(a).localeCompare(prefix(b)); });
for (let i = 0; i < fileNames.length; i++) {
const path = new Path(folderName + "/" + fileNames[i]);
const titleItems = fileNames[i].split('.');
titleItems[2] = epochToStr(titleItems[2]);
titleItems[3] = epochToStr(titleItems[3]);
const titlePrefix = (prefixColumn >= 0 ? "[" + titleItems[prefixColumn] + "] " : "");
const title = titlePrefix + hexDecode(titleItems[1]);
const f = app.openForAccess(path);
const size = parseInt(app.doShellScript("stat -f%z " + path.toString().replace(" ", "\\ ")));
try {
let content = "";
if (size > 0) {
content = app.read(f);
}
notesApp.make({ new:"note", withProperties: {name: title, body:content}});
} catch (e) {
const msg = "Error processing file: " + fileNames[i] + " (title: " + title + "). Error message:" + e.toString();
app.displayDialog(msg + ". Do you want to continue?");
notesApp.make({ new:"note", withProperties: {name: title, body:msg}});
} finally {
app.closeAccess(f);
}
}
app.displayDialog("Done. Notes processed: " + fileNames.length);
return input;
}
- Click Run and select the folder containing notes exported from Mac 1