Macos – How to zip multiple files into separate archives

applescriptmacosshellterminalzip

I admit this question was asked here before:

Like Zip into separate files where the person who asked didn't specify the OS he used and received no answers.

I need to separate a huge directory into multiple .zip files that are not interdependent on each other. So, instead of:

file1.zip
file2.z01
file3.z02

I would like the following set of files instead:

file1.zip
file2.zip
file3.zip

Basically this is my question. I'm on OS X so a shell script or AppleScript would be the easiest way to go.

In addition, here is a guy who asked the same thing – only he wanted to create a .tar archive: How to Creating separate archives for a set of files

The answer is correct, but it will result in tar files:

for file in `ls *`; do tar -czvf $file.tar.gz $file ; done

PS: This last part is just for those of you who are fit in Keyboard Maestro:

I also tried to perform this in Keyboard Maestro, I have a "for each" action setup which determines the file paths and then triggers a shell script. The output is correct and the macro works if I paste it in the terminal (e.g. zip
/Users/me/Desktop/test /Users/me/Desktop/test.txt
).

However, when I pass the two variables to the shell script in Keyboard Maestro won't work:
zip "$KMVAR_zipPath" "$KMVAR_sourcePath"

Best Answer

The solution is pretty easy. If you want to do this for every file, recursively, use find. It will list all files and directories, descending into subdirectories too.

find . -type f -execdir zip '{}.zip' '{}' \;

Explanation:

  • The first argument is the directory you want to begin in, .
  • Then we will restrict it to find files only (-type f)
  • The -execdir option allows us to run a command on each file found, executing it from the file's directory
  • This command is evaluated as zip file.txt.zip file.txt, for example, since all occurrences of {} are replaced with the actual file name. This command needs to be ended with \;

Of course, find has more options. If instead you just want to stay in your current directory, not descending into subdirectories:

find . -type f -maxdepth 1 -execdir zip '{}.zip' '{}' \;

If you want to restrict it to certain file types, use the -name option (or -iname for case-insensitive matching):

find . -type f -name "*.txt" …

Anything else (including looping with for over the output of ls *) is pretty ugly syntax in my opinion and likely to break, e.g. on files with spaces in their name or due to too many arguments.