Bulk-Export Mails from Apple Mail

Apple’s mail client has an annoying limitation: If you want to export mail messages as .eml or .emlx you have to do it one by one, i.e. dragging one message at a time from the Mail App’s window to a Finder window. It won’t let you drag several messages at the same time.

applemail-icon

However with a small script this limitation can be circumvented easily.

Update 2017-03-29:

This script export your messages as .emlx. If you prefer .eml as export format, then please see Bulk-Export Mails from Apple Mail, Part II which provides an AppleScript for that. (Which is also a bit easier to use, as a side note.) The differences between .eml and .emlx are also explained there.

If you prefer .emlx, then read on.

Usage:
  1. In Mail.app create a local (“On my Mac”) mailbox named “Export”.
  2. Drag all the messages you want to export into the “Export” mailbox.
  3. Run the script (double-click).
    • The script will export all messages from the “Export” mailbox to a new “Exported Mails” folder on your desktop.
    • Messages will be renamed as [date] [subject] [author]. You can customize the renaming pattern in the script. Spotlight has to be enabled for this to work.
  4. Delete the messages from the “Export” mailbox, if desired.
  5. Move the exported messages from the “Exported Mails” folder on your desktop to the desired location.

If the script doesn’t run or crashes your computer, make sure permissions of the script are set correctly to 755.

Please note:

With macOS 10.12 (Sierra) Apple has changed the way how Mail is filing the messages. For example, on my system the “Export” mail box is now in a folder named with an account ID: ~/Library/Mail/V4/0CB67705-6830-4E6B-B129-8834DE89F521/Export.mbox

That means you will probably have to open the Mail folder in ~/Library and look for the exact location of Export.mbox. Then set the correct location in the script.

Download (1.1.2 / 2017-02-08)

This is the content of the script:

As you can see several different renaming patterns are already provided. Just uncomment/comment the corresponding lines in the script to your need.

#!/bin/bash

# Version 1.1.2
# El Capitan, Sierra

# What it does:

# Exports contents of an Apple Mail mailbox as individual emlx files to a local
# destination. Exported messages will be time-stamped and renamed according to
# their spotlight metadata.

# Usage:

# 1) In Apple Mail create a local ('on my Mac') mailbox and name it "Export".
# 2) Copy or move the messages you want to export into that mailbox.
# 3) Run this script.
# 4) You should find the exported and renamed files in a folder "Exported Mails"
#    on your Desktop.

# Note: Set your preferred time-stamp format, naming scheme, etc. by
# un/commenting the corresponding lines in the script below.


## Script

## Define source and target directories
# macOS 10.12
# It may look like this (find out the correct ID of the folder on your system):
# sourceDir=~/Library/Mail/V4/0CF67405-6830-4A6B-B162-8934DE09F521/Export.mbox
# OS X 10.11
sourceDir=~/Library/Mail/V3/Mailboxes/Export.mbox
# For older systems use:
# sourceDir=~/Library/Mail/V2/Mailboxes/Export.mbox
expDir=~/Desktop/Exported\ Mails

## Make target directory if not already present
mkdir "$expDir"

## Copy mail messages from source mailbox to target directory
find "$sourceDir" -name '*.emlx' -exec ditto {} "$expDir" \;

## Index target directory
mdimport "$expDir"
sleep 1

## Rename exported mails by Spotlight data
for f in "$expDir"/*.emlx
  do

    ## Get message author's name

    aut=$(mdls -name kMDItemAuthors -raw "$f" |
    head -n 4 | tr -ds '\n"()' ' ' |
    sed '
    s/^ //
    s/\\U[0-9a-f]\{4\}//g')

    ## Or: author's address:

    # aut=$(mdls -name kMDItemAuthorEmailAddresses -raw "$f" |
    # head -n 4 | tr -ds '\n"()' ' ' | sed 's/^ //')


    ## Get addressee names

    # adr=$(mdls -name kMDItemRecipients -raw "$f" |
    # head -n 4 | tr -ds '\n"()' ' ' |
    # sed '
    # s/^ //
    # s/\\U[0-9a-f]\{4\}//g' |
    # head -c 40)

    ## Or: addressee addresses

    # adr=$(mdls -name kMDItemRecipientEmailAddresses -raw "$f" |
    # head -n 4 | tr -d '[:space:]"()' | head -c 40)


    ## Get message subject

    suj=$(mdls -name kMDItemSubject -raw "$f" | head -c 80 |
    sed 's/\//∕/g')


    ## Content creation date to time stamp

    ccd=$(mdls -name kMDItemContentCreationDate -raw "$f")

    ## POSIX time (complete)
    # ccd=$(date -j -u -f '%Y-%m-%d %H:%M:%S %z' "$ccd" +"%s")

    ## UTC (basic, truncated year)
    # ccd=$(date -j -u -f '%Y-%m-%d %H:%M:%S %z' "$ccd" +"%y%m%dT%H%M%SZ")

    ## Local time (basic, truncated year, w/o time zone)
    # ccd=$(date -j -f '%Y-%m-%d %H:%M:%S %z' "$ccd" +"%y%m%dT%H%M%S")

    ## Local time (basic, truncated year/time, w/o time zone)
    ccd=$(date -j -f '%Y-%m-%d %H:%M:%S %z' "$ccd" +"%y%m%dT%H%M")

    ## Local time (truncated year, w/ short time zone)
    # ccd=$(date -j -f '%Y-%m-%d %H:%M:%S %z' "$ccd" +"%y%m%dT%H%M%S%z" |
    #   head -c 16)


    ## Final naming of exported messages

    # msgName="$ccd [$aut] $suj"
    msgName="$ccd $suj [$aut]"
    # msgName="$ccd [$aut → $adr] $suj"
    # msgName="$ccd [$aut - $adr] $suj"
    # msgName="$ccd $suj [$aut - $adr]"
    # msgName="$suj [$aut - $adr - $ccd]"

    mv "$f" "${f%/*}"/"$msgName"."${f#*.}"

    aCounter=$[aCounter + 1]

  done

  afplay '/System/Library/Sounds/Glass.aiff'
  osascript -e 'display notification with title "'"$aCounter"'" & " mails exported"'

  echo "Done"
  exit 0

19 thoughts on “Bulk-Export Mails from Apple Mail”

  1. Hi – love this script, really useful thanks. Can you show me how to modify the date command so that the export date is Australian style – i.e., day, month, full year (hyphen or colon in between) with no time – e.g.
    12:4:2016
    I’ve worked out how to modify the rest of the export data. Thanks!

    1. Colons are not possible because the Mac uses colons internally as path separators. But with hyphens the string is %e-%-m-%Y. That is, the whole line for example:

      ccd=$(date -j -f '%Y-%m-%d %H:%M:%S %z' "$ccd" +"%e-%-m-%Y")

      For padding zeros use %d-%m-%Y

      Reference

  2. Hi, thank you for the script.

    I have been looking for some way of doing this for a very long time (actually up to now I used to access my mail from a Windows machine and use a windows utility to do this).

    As I am completely useless in scripts and commands, is there any possibility in helping with the following:

    Is there a way that the script can be amended to export per folder, e.g. if the Export folder has other subfolders under it, that e-mails are exported with this structure, i.e. separated per folder?

    Thank you in advance for your help and insight on this.

    1. Here is a modification that should do it (beta!, only briefly tested). Let me know if it works for you. Note that it works only for 1 subfolder level. If you have nested subfolders it will get a bit more complicated 😉

      This script will be slower than the original one. Not sure if it’s significantly slower, but if you have thousand of mails, consider copying them folder-wise to the Export folder and working with the original script.

  3. Hi Tom,

    Once again thank you for the revised script and I can confirm that it works perfectly, with only one small amendment.

    If you run the script as is, it cannot find the folder where the Export mailbox is. So one has to replace the “Mailbox” part of the path in

    sourceDir=~/Library/Mail/V3/Mailbox/Export.mbox

    with the correct string which in my case is “6C040741-7093-4757-A0FB-E85A5845224E”.

    The only other thing that I have noticed is that when running the script (this happens in your original script with no folders/subfolders), there are some errors pertaining to the naming I beleive, which do not seem to hinder the script from working.

    The exact error is:

    2016-08-04 12:11:22.682 mdimport[3143:201755] Metadata.framework [Error]: Boolean _MDPlistBytesAddObject(MDMutablePlistBytesRef, CFTypeRef): Unsupported CFTypeID (1) — replaced <CSPerson:0x7fc1b3f54da0 Apple with kCFNull

    but I will investigate this on my side as I enjoy debugging and indirectly learning code.

    Thanks,
    Nikolaos

    1. Hi Nikolaos,

      with the correct string which in my case is “6C040741-7093-4757-A0FB-E85A5845224E”.

      Hmm. This looks like you haven’t placed the Export mailbox On My Mac (that is as a local mailbox) but into an IMAP account, for example your iCloud account. If you do this the Export box will be synced every time you put stuff into it, and IMO this isn’t ideal; it was thought as a temporary place, just for the exporting task.
      I just verified it with a Test.mbox On My Mac, and it is created in the …/Mail/V3/Mailbox/… path (just like my Export box).

      Unsupported CFTypeID (1)

      Thanks for mentioning it. I noticed this too, yesterday, and wasn’t sure if this is a problem only on my Mac. IIRC I haven’t seen this before.
      Since it is a message from mdimport (Spotlight) I guess it’s only telling us that it couldn’t correctly associate one of the metadata found in the mail. So probably the issue will only be solved when Apple tweaks/updates the mdimporter for mail messages. Just a guess.
      Let me know when you find something out.

      Another thing:

      You will note that sometimes a mail gets duplicated in the Desktop/Export folder and labelled with something like “(null)(null)”. I don’t know when and why this happens. It happens also with the original version of my script. But since these mails always seem to be duplicates, this shouldn’t be a critical issue. But keep an eye on it, and the first times when you spot such a mail open it and make sure it’s a duplicate of another —correctly labelled— mail.

      Glad that the script works for you 🙂 Have fun!

  4. Thanks for creating this script. Is it also possible to bulk-export emails to .txt, .rft. and .epub formats? I installed a script that did that many years ago, but it no longer seems to work in Mavericks.

    1. No, this is not the point of this script. The message format eml/emlx is a format on its own, containing html markup, encoded image data and lots of email meta info. For convering this to rtf, text or whatever you need a fully-fledged converter.

      For rtf you can use Mail’s built-in rtf export (which does not always work well, and multiple messages are merged into one document). There’s also an online converter (http://www.zamzar.com) that seems to work fine and it converts to many formats. (For uploading you have to change the message extension from emlx to eml.)

      Do you remember which converter was used in your script?

  5. Hello, that sounds great, thanks.
    Do you think it is possible to export mails as .eml from my mail.app (sierra) spam-folder automatically to a folder on my desktop? Perhaps a modification of your script? And, if possible, I don’t want to change the name or date of the mails.

    1. No, this cannot be done with the script. The script accesses the messages files on file system level (i.e. it gets them from the folder where they are saved.)

      To export messages as eml you have to speak to Mail via AppleScript.

      Try this script:

      use AppleScript version "2.4" -- Yosemite (10.10) or later
      use scripting additions
      
      # Set your export folder here
      set exportDestination to (path to desktop) & "Exported Junk Mails" as text
      
      set junkBoxes to {"Junk", "Spam"}
      
      set junkCount to 0
      set spamCount to 0
      set msgCount to 0
      set accCount to 0
      
      set msgNames to {}
      set msgDates to {}
      set msgSources to {}
      
      tell application "Mail"
        repeat with theAccount in accounts
          set accountID to id of theAccount
          tell account id accountID
            repeat with junkBox in junkBoxes
              if mailbox junkBox exists then
                tell mailbox junkBox
                  repeat with theMsg in messages
                    # Remove the If clause to get also deleted messages
                    if deleted status of theMsg is false then
                      set msgID to id of theMsg
                      set end of msgNames to subject of theMsg
                      set d to date received of theMsg
                      set end of msgDates to short date string of d
                      set end of msgSources to source of theMsg
                    end if
                  end repeat
                  if junkBox contains "Junk" then
                    set junkCount to junkCount + 1
                  else
                    set spamCount to spamCount + 1
                  end if
                end tell
              end if
            end repeat
          end tell
          set accCount to accCount + 1
        end repeat
      end tell
      
      repeat with i from 1 to count of msgNames
        set msgName to item i of msgDates & " - " & item i of msgNames
        set msgName to do shell script "echo " & quoted form of msgName & " | head -c 80 | tr : - "
        set newFile to (open for access file (exportDestination & ":" & msgName & ".eml") with write permission)
        set msgSource to item i of msgSources
        write msgSource to newFile
        close access newFile
        set msgCount to msgCount + 1
      end repeat
      
      display alert "Completed" message "Exported " & msgCount & " messages from " & junkCount & " “Junk” mail boxes and " & spamCount & " “Spam” mail boxes of " & accCount & " mail accounts."

      Create a folder “Exported Junk Mails” on your Desktop. If your folder has a different name, then set the correct name at the beginning of the script.

      Copy the text into Script Editor and run it. Watch out for error messages in the results window; I have tested it only briefly. It runs fine here but that doesn’t mean it runs also on your system.

      The script iterates thru the Junk mailboxes of every account and exports all messages to the defined folder on the Desktop. The files are named with the Received Date and the Subject of the message. I had to limit the length of the file name, because otherwise I got “filename too long” errors. Currently it is set to 80 chars, but you can experiment with higher values, depending on the total path length on your system.

      Here is a downloadable file.

      1. Hi Tom,
        THANKS a lot! It works, and it works fine! You are me heroe for today and tomorrow! 🙂
        Thanks and all the best!
        Regards, Michael

        1. Glad to hear that it works! Thanks for the feedback.

          You inspired me to write another, similar, script to export selected messages as eml. Maybe there are more people who prefer eml to emlx. I will post it shortly.

          However it should be noted that eml files are not indexed by Spotlight. (Unless you have third-party software installed, like for example EagleFiler or MailMate.)

          – Tom

          1. Just need the eml to send them to my hoster to feed the spamfilter thing. Funny thing about your script: when I delete the exported Mails from my spam folder in mail.app and from the trash and run the script again… it gives me the same exported mails again. Wow, where does it get them from? I deleted them.
            Hm.

            1. Yes, I noticed that with one of my accounts the exported junk mails didn’t correspond exactly to the content of the junk mail box as displayed by the Mail app. I thought it was a weird thing just with that one account, but since you are reporting a similar behavior, I will look into that.

              Thanks for the note.

            2. OK, the thing is that deleted messages somehow remain cached, but fortunately they get the “deleted” flag.

              I’ve added a condition that tests for that flag. If it is set then the message is ignored.

              I have updated the script text above and the download file.

              To get back the old behavior just remove the If clause (including the end if).

              – Tom

            3. Hi,

              I found another pitfall: The junk box of some accounts is named “Spam”, even if Mail.app displays it as “Junk”.
              On top of that, some accounts have both “Spam” and “Junk” boxes, sometimes both containing messages.

              Don’t ask me why, probably a wonky IMAP sync.

              I updated the script: It now goes through “Junk” and “Spam” boxes. At the end it displays a message telling you some stats.

              Download the new version if you want.

              – Tom

              1. Great Job! Thanks, no it shows me, how many mails from which folder have been exported, fine 🙂

Leave a Reply

Your email address will not be published.