MACRO: Extract & Trash Archive (Watch Folder), v2.0

The following thread motivated me to document and share a macro that I use frequently:

In that thread, @iorek was looking for a way to watch ~/Downloads for an archive file matching a specified regular expression. With help from @kevinb, @kcwhat, @tiffle, and @Nige_S, a solution was developed that automatically extracts a matching archive and then moves the original archive to the Trash.

My use case is similar, but my workflow preferences are a little different, so I thought it would be better shared in a new thread.


In my ~/Downloads folder, I keep a subfolder named _ExtractAndTrashArchive. This combined with a macro with a Folder Trigger provides two simple workflows:

  1. Extract and trash the archive: Move the archive into _ExtractAndTrashArchive.

  2. Extract but keep the archive: Copy the archive into _ExtractAndTrashArchive.

Since the process is triggered only when I intentionally move or copy an archive into that folder, newly downloaded archives are not processed automatically. I use this approach since it gives me control over which archives are processed while still automating the repetitive extraction and cleanup steps.


MACRO: Extract & Trash Archive (Watch Folder)

PURPOSE

This macro watches a folder for new archive files (zip or cpio). When one appears, it:

  1. Extracts the archive's contents into the same folder

  2. Moves the original archive to the Trash


SETTINGS

local_Overwrite Determines how naming collisions are handled. This value is always set by a calculation earlier in the macro:

  • 1 : an existing file or folder with the same name is replaced.

  • 0 : nothing is replaced. The new item is given a numeric suffix ( 2, 3, 4, ...) instead. For example, Notes.txt becomes Notes 2.txt if the former already exists.


NOTES

Uses ditto. unzip is a general-purpose tool that does not understand macOS-specific file metadata, such as resource forks and extended attributes. ditto, an Apple tool, preserves metadata correctly during extraction, making it the more reliable choice on macOS.

Supported extensions. ditto can extract two archive formats: PKZip (.zip) and CPIO (.cpio), including gzip-compressed CPIO (.cpgz). The macro checks for these three extensions before attempting extraction.

Extraction occurs outside the watched folder. Extracting directly into the watched folder could generate extra filesystem activity that would retrigger the watching macro during the extraction process. Performing extraction in a temporary location minimizes this; the extracted contents are moved into the watched folder, which will retrigger the macro; but the macro will only process archives.


TESTED WITH

  • Keyboard Maestro 11.0.4
  • Tahoe 26.5.2 (25F84)/MacBookPro18,2
  • Mojave 10.14.16/Macmini6,2
  • High Sierra 10.13.6/iMac11,1445

VERSION HISTORY

( expand / collapse )

1.0 - Initial version

2.0 - Removed the local_Extensions setting. Extensions are now hardcoded to zip, cpio, and cpgz, matching what ditto is capable of extracting.



Download: Extract & Trash Archive (Watch Folder).kmmacros (8.0 KB)

Macro-Image


Macro-Notes
  • Macros are always disabled when imported into the Keyboard Maestro Editor.
    • The user must ensure the macro is enabled.
    • The user must also ensure the macro's parent macro-group is enabled.

System Information
  • macOS 26.5.2 (25F84)
  • Keyboard Maestro v11.0.4

MACRO: Touch _ExtractAndTrashArchive

PURPOSE

When any file is added to ~/Downloads, touch subfolder _ExtractAndTrashArchive.

The result: when ~/Downloads is sorted by Date Modified in the Finder, _ExtractAndTrashArchive appears at the top.

TESTED WITH

  • Keyboard Maestro 11.0.4
  • Tahoe 26.5.2 (25F84)/MacBookPro18,2
  • Mojave 10.14.16/Macmini6,2
  • High Sierra 10.13.6/iMac11,1445

VERSION HISTORY

1.0 - Initial version


Download: Touch _ExtractAndTrashArchive.kmmacros (5.2 KB)

Macro-Image


Macro-Notes
  • Macros are always disabled when imported into the Keyboard Maestro Editor.
    • The user must ensure the macro is enabled.
    • The user must also ensure the macro's parent macro-group is enabled.

System Information
  • macOS 26.5.2 (25F84)
  • Keyboard Maestro v11.0.4

2 Likes

I appreciate the great care that you have taken with the documentation! I look forward to trying the macro.

Could you please explain the meaning of :-zip, cpio in this line of the shell script?

EXTS="${KMVAR_local_Extensions:-zip, cpio}"

I wonder why, when the variable local_Extensions has already been set to zip,cpio, it is necessary to mention those file extensions again.

Defensive programming?

Saves muppets like me from myself -- if I delete the values in the "Set Variable local_Extensions" Action and then forget to add my own, the shell script will still use the defaults.

Hi, @kevinb and @Nige_S.

@kevinb, good question, but one small correction: the actual code is

EXTS="${KMVAR_local_Extensions:-zip,cpio}"

No space after the comma.

As @Nige_S points out, if local_Extensions wasn't set, the script defaulted to matching files ending in zip or cpio (case-insensitive).

More fundamentally, though, @kevinb's question highlighted that this design was unnecessarily complicated: local_Extensions let you configure a list of extensions, but the script could still only ever hand files to ditto, and ditto itself only extracts two archive formats: PKZip (.zip) and CPIO, including gzip-compressed CPIO (.cpgz). Anything else added to local_Extensions would pass the filter and then fail at the ditto step regardless.

So the setting was giving a false impression of configurability where none actually existed. I will update the macro to:

  • drop local_Extensions entirely
  • hardcode the extension check to EXTS="zip,cpio,cpgz", matching exactly what ditto -x is capable of extracting

This removes the possibility of setting the variable to something ditto can't handle (.tar, .7z, .rar, .gz, etc.), since those were never going to work anyway.

Thanks. I have now found some Web pages about :-, and learn from those and your reply that :- is used to provide a fallback value.[1] It’s obvious once you know. :grinning_face_with_smiling_eyes:

Oops. I had noticed that, but I didn’t notice that my use of OCR on your screenshot had added an extra space.

Users could of course build upon your macro to add other methods besides ditto, if needed.

I find that BetterZip covers most bases (not just zip) very well, and it preserves extended attributes (whether it is worth the asking price will depend on a user’s needs, of course, but for me, it has been).

The reference to resource forks seems archaic, but I suppose it will still be an important consideration for some! I just get the image of a jack-in-the-box[2], covered in dust…


  1. Shell Script Input and Default Values | Fernando Basso
    Shell Scripting - Default Shell Variable Values — linuxvox.com ↩︎

  2. ResEdit - Wikipedia ↩︎

1 Like

archaic, like me :wink:

As long as your resources aren’t yet forked, don’t worry about it!

1 Like

I'm seriously tempted to fire up OS 9 Classic on the box beside me and spam you with ResEdit screenshots...

1 Like

I think I should be embarrassed that your comment made me chuckle. Should I ask my wife? She's an old school artist that hates computers. :rofl:

Here, @Nige_S, I'll save you the trouble:

1 Like

I've updated the OP with Version 2.0 of Extract & Trash Archive (Watch Folder):

  • Removed the local_Extensions setting. Extensions are now hardcoded to zip , cpio , and cpgz , matching what ditto is capable of extracting.

Ditto.

@kevinb, I'm also a big fan of BetterZip. It's currently $24.95 and is also included with Setapp.

For several years, I've used BetterZip both interactively and through the excellent BetterZip Alfred Workflow created by BetterZip developer Robert Rezabek. Until now, though, I hadn't tried driving BetterZip from Keyboard Maestro. But, @kevinb, your comment got me thinking! :thinking:

With help from the BetterZip Automation documentation and the code in the BetterZip Alfred Workflow, I've created a Keyboard Maestro macro that behaves similarly to the one shared in this thread. While experimenting, I also wrote another shell script that's launched by a custom BetterZip Preset named Extract, Clean, Scan & Trash. After extraction, it automatically scans the extracted files with ClamAV.

Because BetterZip Presets handle much of the heavy lifting, the shell script in the Keyboard Maestro macro is considerably simpler than the ditto-based approach used here. At the same time, BetterZip provides a much richer feature set and broader archive support, making this version substantially more capable.

I'll share the new macro in a separate thread once I've had more time to test it thoroughly. I'll also need to spend some time documenting the setup. Since BetterZip is commercial software, I suspect the audience for the macro will be smaller than for a macro based entirely on built-in macOS tools.

1 Like

The base for all automation of BetterZip is its AppleScript support.

Oooh, yummy!

1 Like