Mac OS service to ZIP a folder
Mac OS has integrated Archive Utility for packing files and folders into archives. It works fine, but what I don’t like about it, is that it packs not only files and folders I want to pack, but also hidden system stuff like __MACOSX
folders and .DS_Store
files. You can use some 3rd-party archiver instead (Keka is a very good one) that provides the possibility to exclude such files, but this is also possible to achieve with the integrated ZIP utility.
ZIP utility in Terminal
First, let me show you those system files in an actual archive.
So, let’s take some folder with files in it:
user@MacBook-Pro:~/Desktop$ ls -la somefolder/
total 80
drwxr-xr-x 6 user staff 204B Jul 31 09:39 ./
drwx------+ 7 user staff 238B Jul 31 09:48 ../
-rw-r--r--@ 1 user staff 6.0K Jul 31 09:38 .DS_Store
-rw-r--r-- 1 user staff 9.7K Jul 30 20:29 and-one-more.markdown
-rw-r--r-- 1 user staff 13K Jul 21 17:02 another-text.markdown
-rw-r--r-- 1 user staff 1.1K Jul 12 11:21 some-text.markdown
Now we right-click on this folder in Finder and press standard Compress "somefolder"
. Archive somefolder.zip
will be created. Let’s inspect it:
user@MacBook-Pro:~/Desktop$ zipinfo somefolder.zip
Archive: somefolder.zip
Zip file size: 11704 bytes, number of entries: 8
drwxr-xr-x 2.1 unx 0 bx stor 17-Jul-31 09:39 somefolder/
-rw-r--r-- 2.1 unx 6148 bX defN 17-Jul-31 09:38 somefolder/.DS_Store
drwxrwxr-x 2.1 unx 0 bx stor 17-Jul-31 10:05 __MACOSX/
drwxrwxr-x 2.1 unx 0 bx stor 17-Jul-31 10:05 __MACOSX/somefolder/
-rw-r--r-- 2.1 unx 120 bX defN 17-Jul-31 09:38 __MACOSX/somefolder/._.DS_Store
-rw-r--r-- 2.1 unx 9972 bX defN 17-Jul-30 20:29 somefolder/and-one-more.markdown
-rw-r--r-- 2.1 unx 12989 bX defN 17-Jul-21 17:02 somefolder/another-text.markdown
-rw-r--r-- 2.1 unx 1130 bX defN 17-Jul-12 11:21 somefolder/some-text.markdown
8 files, 30359 bytes uncompressed, 10402 bytes compressed: 65.7%
As you can see, beside the actual files there is some system stuff inside the archive.
Okay, now here’s how to zip a folder in Terminal, excluding system files:
zip -r9 folderToArchive.zip folderToArchive -x "*.DS_Store"
Let’s inspect the archive now:
user@MacBook-Pro:~/Desktop$ zipinfo somefolder.zip
Archive: somefolder.zip
Zip file size: 10912 bytes, number of entries: 4
drwxr-xr-x 3.0 unx 0 bx stor 17-Jul-31 09:39 somefolder/
-rw-r--r-- 3.0 unx 9972 tx defX 17-Jul-30 20:29 somefolder/and-one-more.markdown
-rw-r--r-- 3.0 unx 12989 tx defX 17-Jul-21 17:02 somefolder/another-text.markdown
-rw-r--r-- 3.0 unx 1130 tx defX 17-Jul-12 11:21 somefolder/some-text.markdown
4 files, 24091 bytes uncompressed, 10170 bytes compressed: 57.8%
As you can see, now our archive contains only files we wanted to pack there.
Dataforks
From the man zip
you can discover this section:
-df
--datafork
[MacOS] Include only data-fork of files zipped into the archive. Good for
exporting files to foreign operating-systems. Resource-forks will be
ignored at all.
Which implies that a more correct way to exclude system files from packing is to use this option instead of a hardcoded -x
. However, trying to use -df
option gives me this error:
zip error: Invalid command arguments (specify just one action)
or, in case of a long option --datafork
:
zip error: Invalid command arguments (long option 'datafork' not supported)
Some *nux’es legacy, huh? Giving the documentation for something that is not there.
But anyway, the point here - I have to use -x "*.DS_Store"
.
Mac OS service
Now we will create a Mac OS service around this, so we could perform this command by right-clicking on folders in Finder.
Here’s the workflow:
And here’s the main script:
on run {input, parameters}
set logScript to load script "/path/to/your/scripts/write2log.scpt"
set fnameScript to load script "/path/to/your/scripts/getFname.scpt"
#write2log("/path/to/your/logs/some.log", input) of logScript
if (count of input) is not 1 then # it was easier to implement it with just one folder
display dialog "You can archive only one folder." with icon stop with title "Error" buttons {"Okay, jeez"} default button {"Okay, jeez"}
return
else
set filePath to POSIX path of input
set fname to getFolderName(filePath) of fnameScript # gets "folder" from "/some/path/to/folder/"
set archName to text 1 thru -2 of filePath & ".zip" # replace last "/" with ".zip"
#write2log("/path/to/your/logs/some.log", archName) of logScript
# prepare the command: set the context to the same directory and zip the folder
set cmd to "cd " & quoted form of filePath & "..; zip -r9 " & fname & ".zip " & fname & " -x \"*.DS_Store\""
#write2log("/path/to/your/logs/some.log", cmd) of logScript
# execute command
do shell script cmd
# preparation for the result window
set rez to do shell script "du -h " & quoted form of archName & " | awk '{print $1}'"
display dialog "Archive was successfully created: " & archName & " (" & rez & ")" with title "Job's done" buttons {"OK"} default button "OK"
end if
end run
Function getFolderName(filePath)
is available from this script.
I want to give you some explanation about cmd
. After all substitutes it looks like this:
cd /path/to/the/folderToArchive/..; zip -r9 folderToArchive.zip folderToArchive -x "*.DS_Store"
First half (the part before ;
) of the command is an environment (working directory) setting - we go the target directory and then we go one level up (..
). That way zip
utility will “know” the path where to operate. Otherwise it will try to create archive somewhere in the system path and will fail with an access error. Of course, you can specify the full path where to create archive, but then inside the archive you will get the full hierarchy of folders. And also there is no need to provide the full path to folder that needs to be archived as we are already inside that directory.
Currently this service works only with a single folder, because in was easier that way and also because I only need that functionality for a single folder. I usually don’t pack several folders into one archive.
As you might have noticed, I added a dialog to the end of workflow, showing the result of packing. Here’s how it looks like in action:
Social networks
Zuck: Just ask
Zuck: I have over 4,000 emails, pictures, addresses, SNS
smb: What? How'd you manage that one?
Zuck: People just submitted it.
Zuck: I don't know why.
Zuck: They "trust me"
Zuck: Dumb fucks