Can a zsh script to batch zip files work recursivly? (VikingOSX script in post)

Last month VikingOSX graciously coded a script to batch process particular sets of files into archive zips that take on the name of the files. From this post. It rocks. I've zipped about 50,000 files since then.


I have recently learned a new vocabulary word: recursive. Yes, I'm new to the command line.


Could the script below be changed so that it works recursively on all sets of designated files (jpg, png, eps, svg, in this case) in the directory AND subdirectories of that directory? If so, would it work even if some directories had no files? And would it ignore (not archive) any other file type than the ones designated in the script ((jpg, png, eps, svg)?


#!/bin/zsh

: <<"COMMENT"
For a given folder containing filenames identified by product code and suffix,
aggregate the four image files associated with it into a zip container.

For example: Dzn-4-n-m-no.zip contains the four images: Dzn-4-n-m-no.{eps,jpg,png,svg}

Tested: macOS 14.1.2 (Zsh 5.9), macOS 10.14.6 (Zsh 5.3)
Revision: 2
Author: VikingOSX, 2023-12-03, Apple Support Communities, No warranties implied.

Usage: lab.zsh folder
COMMENT

# absolute path of folder passed as only argument to the script
ZIPDIR="${1:a:s/\~\//}"

# image types to process
EXTPAT="eps|jpg|png|svg"
last_ext=
last_fname=

setopt nocaseglob
for f in "${ZIPDIR}"/*.(${~EXTPAT})(.Non);
do
    # Two captures: product name and two or three character suffix
    [[ "${f:t}" =~ "(^.*\-)([[:alpha:]]{2,3})\..*$" ]] || continue
    [[ $last_fname != "${match[1]}"  || $last_ext != "${match[2]}" ]] || continue
    last_fname="${match[1]}"
    last_ext="${match[2]}"
    # capture unique suffixes to their respective zip file excluding dot files
    zip -qj -6 "${ZIPDIR}/${(j::)match[@]}".zip "${ZIPDIR}/${(j::)match[@]}".{eps,jpg,png,svg} -x ".*"
    # rm -f "${ZIPDIR}/${(j::)match[@]}".{eps,jpg,png,svg}
done
zipcnt=( "${ZIPDIR}"/*.zip )
echo "Zip archives created: $#zipcnt"
unset zipcnt
exit 0



Posted on Jan 14, 2024 1:10 AM

Reply

Similar questions

41 replies

Jan 15, 2024 1:17 PM in response to VikingOSX

After saving the script to my desktop and naming it golden.zsh (and changing line 13 of the script to


Usage: golden.zsh folder


to match the changed script name, I entered the following in to Terminal:


myhomedirectory$ cd ~/Desktop
My-MacBook-Pro:Desktop myhomedirectory$ chmod +x ./golden.zsh
My-MacBook-Pro:Desktop myhomedirectory$ ./golden.zsh ~/Desktop/1


Which returned the error message:


./golden.zsh:36: no matches found: /Users/myhomedirectory/Desktop/1/*.zip


Here is the full script:


#!/bin/zsh

: <<"COMMENT"
For a given folder containing filenames identified by product code and suffix,
aggregate the four image files associated with it into a zip container.

For example: Dzn-4-n-m-no.zip contains the four images: Dzn-4-n-m-no.{eps,jpg,png,svg}

Tested: macOS 14.1.2 (Zsh 5.9), macOS 10.14.6 (Zsh 5.3)
Revision: 2
Author: VikingOSX, 2023-12-03, Apple Support Communities, No warranties implied.

Usage: golden.zsh folder
COMMENT

# absolute path of folder passed as only argument to the script
ZIPDIR="${1:a:s/\~\//}"

# image types to process
EXTPAT="eps|jpg|png|svg"
last_ext=
last_fname=

setopt nocaseglob
for f in "${ZIPDIR}"/**/*.(${~EXPTPAT})(.Non);
do
    # Two captures: product name and two or three character suffix
    [[ "${f:t}" =~ "(^.*\-)([[:alpha:]]{2,3})\..*$" ]] || continue
    [[ $last_fname != "${match[1]}"  || $last_ext != "${match[2]}" ]] || continue
    last_fname="${match[1]}"
    last_ext="${match[2]}"
    # capture unique suffixes to their respective zip file excluding dot files
    zip -qj -6 "${ZIPDIR}/${(j::)match[@]}".zip "${ZIPDIR}/${(j::)match[@]}".{eps,jpg,png,svg} -x ".*"
    # rm -f "${ZIPDIR}/${(j::)match[@]}".{eps,jpg,png,svg}
done
zipcnt=( "${ZIPDIR}"/*.zip )
echo "Zip archives created: $#zipcnt"
unset zipcnt
exit 0


Do you see what I did wrong?

Jan 15, 2024 2:04 PM in response to VikingOSX

OK, I made the changes above, although this appeared to be identical to what I had in the script already:


# image types to process
EXTPAT="eps|jpg|png|svg"


I made it executable again (do I do that each time the script is changed?):


chmod +x ./golden.zsh


I also removed the "#" in front of rm -f so it would delete originals.


Running it returned the following error, I think 630 times, which would be the amount of zip files it would have created from 2520 total files:


...

zip error: Nothing to do! (/Users/my-home/Desktop/1/Italian-Greyhound-1-v-u-tau.zip)

zip error: Nothing to do! (/Users/my-home/Desktop/1/Italian-Greyhound-1-v-u-ter.zip)

zip error: Nothing to do! (/Users/my-home/Desktop/1/Italian-Greyhound-1-v-u-tur.zip)

zip error: Nothing to do! (/Users/my-home/Desktop/1/Italian-Greyhound-1-v-u-yel.zip)
./golden.zsh:36: no matches found: /Users/my-home/Desktop/1/*.zip
My-MacBook-Pro:Desktop my-home$ 


So I'm still missing something?

Jan 16, 2024 11:20 PM in response to VikingOSX

Hi VikingOSX,


In your last reply, you said,


VikingOSX wrote:

A typo courtesy of me in the extension pattern:

# image types to process
EXTPAT="eps|jpg|png|svg"


However, this matches what was already in the script. Does this need to changed? Those are the correct file extensions, but I don't know enough about the code to understand if you were referring to something else needing to be changed.


I have tried the new, "recursive" script multiple times, but I continue to get the same errors I posted in my last post:


Running it returned the following error, I think 630 times, which would be the amount of zip files it would have created from 2520 total files:




zip error: Nothing to do! (/Users/my-home/Desktop/1/Italian-Greyhound-1-v-u-tau.zip)


zip error: Nothing to do! (/Users/my-home/Desktop/1/Italian-Greyhound-1-v-u-ter.zip)


zip error: Nothing to do! (/Users/my-home/Desktop/1/Italian-Greyhound-1-v-u-tur.zip)


zip error: Nothing to do! (/Users/my-home/Desktop/1/Italian-Greyhound-1-v-u-yel.zip)

./golden.zsh:36: no matches found: /Users/my-home/Desktop/1/*.zip

My-MacBook-Pro:Desktop my-home$



The error(s) refer to the four files being archived in a zip ("Italian-Greyhound-1-v-u-yel.zip"), but the files are not being zipped. In my test directory, all of the original eps, jpg, png, and svg files are still there after running the script.


Any other thoughts? I would love to get this to work! Thanks.

Jan 17, 2024 11:04 AM in response to VikingOSX

Sorry, didn't see your post yesterday (Jan 16). It got inserted into the middle of the discussion on this thread and I was looking for a reply at the bottom. I updated and tried it this morning. Same error. Here's what I did:


  1. Copy/pasted the new code from Jan 16 post to BBEdit, saved to my desktop as "golden.zsh"
  2. Had a test folder/directory on my desktop named "1" that has multiple sub-folders. I don't know how to show file tree directory from Terminal, but it is structured like this (Note that there are no files until the second, third or forth sub directory in the main directory that I want to run this on):



3. Opened Terminal, navigated to Desktop: cd ~/Desktop/

4. Ran script command: ./golden.zsh ~/Desktop/1


But I got the same error(s) as yesterday:


...

zip error: Nothing to do! (/Users/myhome/Desktop/1/French-Bulldog-1-v-u-tau.zip)

zip error: Nothing to do! (/Users/myhome/Desktop/1/French-Bulldog-1-v-u-ter.zip)

zip error: Nothing to do! (/Users/myhome/Desktop/1/French-Bulldog-1-v-u-tur.zip)

zip error: Nothing to do! (/Users/myhome/Desktop/1/French-Bulldog-1-v-u-yrl.zip)
./golden.zsh:37: no matches found: /Users/myhome/Desktop/1/*.zip
My-MacBook-Pro:Desktop myhome$ cd ~/Desktop/1


My OS is 10.14.6 Mojave. My Command Line/xCode version is 10.3.0.0.1.1562985497. I have gotten messages in Terminal (while installing ImageMagick with Home Brew) that I need to update my xCode, but I haven't done so because I figured that the most recent version is only for Monterey.


Thanks for your patience with a rookie.

Jan 18, 2024 1:17 AM in response to VikingOSX

OK, here's some more details:


The directories that contain the files are named:


Bowl

Futbol

Futbol2

Futbol3

Volley


As seen the the screen shot below, all of the files reside four "levels" away from the main directory ("1" in example below.



After the main directory ("1" in example above), the names of the next (sub)directories change:


1 - French Bulldog

2 - Labrador Retriever

3 - Golden Retriever

...


And the the next (sub)directory after that also change:


1

2

3

4

...


I can confirm that the original script you made does work on all of the sets of four same-named files (eps, jpg, png, svg) in ONE directory. It does exactly what I had hoped - it grabs each set of four same-named files, archives them into a .zip, and deletes the originals.


I just have to drag each directory of files out of the finder, onto the desktop, run the script on it, and then drag it back into the finder to the "place in the tree" from which it came. I'm stoked about that.


I just wondered if the script could be tweaked to work recursively so the folders of files wouldn't have to drug back and forth from a Finder window to run the script on.


If it's too much work, no worries. I'm grateful for what you've done!


Thanks for the info on Tree and clarifying that I can update my xCode with my OS.

Jan 18, 2024 10:12 AM in response to VikingOSX

I just tested it and quickly got this error message:


My-MacBook-Pro:Desktop my-home$ ./golden.zsh ~/Desktop/1
./golden.zsh:25: unrecognized modifier


Side note: The email notifications of your updates to this thread come in many hours after you post, and they are not chronologically being inserted into the thread. So I'm not always seeing them. I'm not on this board much, so not sure if that's normal behavior.

Jan 18, 2024 11:57 AM in response to swapot

The code that I posted on Jan 17 @ 5pm just worked for me without any line 25 error about unrecognized modifier.



If there had been a modifier error on this end for line 25, it would have blown up for me too, and I would not have posted the code until the error was resolved.


Rather than a delayed email notification, I sign into this site and immediately click my avatar in the upper right top, and within it, I select My Subscriptions. There will be entries in there of your posts (or my replies), and right now, here is what I see:


In your case, instead of your name, it will show VikingOSX and you know that I have replied to your post.

Jan 18, 2024 4:41 PM in response to swapot

Although I have a Zsh script working fine on Sonoma 14.2.1, the Zsh features that allow it to work break on Mojave 10.14.6 due to certain features not being available to Zsh 5.8.1. So yes, that explains the line 25 error you reported.


I have just spent the last two hours trying to introduce alternatives on Mojave and so far, these do not work. I am done for today.

Jan 18, 2024 5:03 PM in response to VikingOSX

I just tried again and got the same error.


My-MacBook-Pro:Desktop my-home$ ./golden.zsh ~/Desktop/1
./golden.zsh:25: unrecognized modifier


Since it's working for you, I'm sure I'm doing something different. Here's what I am doing:


Copy text of script from your post.

Paste into BBEdit.

Save as golden.zsh (to Desktop)

Open Terminal, and write:

cd ~/Desktop

./golden.zsh ~/Desktop/1


The folder "1" resides on the desktop, and the subdirectoies are structured as such:




The code of the script, copied from your post:


#!/bin/zsh

: <<"COMMENT"
For a given folder containing filenames identified by product code and suffix,
aggregate the four image files associated with it into a zip container without paths

Reference: https://discussions.apple.com/thread/255411922?sortBy=oldest_first
Tested: macOS 14.1.2 (Zsh 5.9), macOS 10.14.6 (Zsh 5.3)
Revision: 4 (2023-01-17)
Author: VikingOSX, 2023-12-03, Apple Support Communities, No warranties implied.

Usage: golden.zsh folder
COMMENT

# absolute path of folder passed as only argument to the script
ZIPDIR="${1:a:s/\~\//}"

# image types to process
EXTPAT="eps|jpg|png|svg"

# case-insensitive
setopt nocaseglob
for f in "${ZIPDIR}"/**/*.(${~EXTPAT})(.Non);
do
	ZIPNAME="${ZIPDIR}/${${f:h:r:t4}:gs/\//-}".zip
	zip -qj -6 "${ZIPNAME}" ${ZIPDIR}/${f:h:r:t3}/*.{eps,jpg,png,svg} -x ".*"
    # rm -f "${ZIPDIR}/${f:h:r:t3/*.{eps,jpg,png,svg}
done

zipcnt=( "${ZIPDIR}"/*.zip )
echo "Zip archives created: $#zipcnt"
unset zipcnt
exit 0


I also tried renaming the script, re-initializing the newly named script (chmod +x ./newname.zsh), and I got the same error. I also replaced the files/folders in "1", but with the same tree structure and same four file types, and got the same error.


What should I try next?

Jan 20, 2024 7:25 AM in response to swapot

I cannot spend any more time on trying to work around old Zsh functionality. What does work (I have tested it) on macOS 10.14.6 is to use homebrew to install Zsh 5.9 into /usr/local/bin:


brew install zsh


and edit the first line of the Zsh script that I last provided you to read:


#!/usr/local/bin/zsh


That will force the script to use Zsh 5.9 and the existing Zsh syntax will work to produce your zip files.

Feb 17, 2024 12:19 PM in response to VikingOSX

Works perfect! Again, so appreciated!


Would it be easy to add something to the code so that something (anything) gets appended to the front/prefix of 3-letter zip filename? So, instead of getting "ber.zip", it would be "1ber.zip" or "1-ber.zip"?


This would allow multi-file find/replace to create custom prefix of all files in a particular directory using Mac Finder after the zip files are created. If not, no big deal. I will just key or paste in prefix.


Again, this will be SO much more efficient at making the zips. Especially because Mac's built in archive/zip app does not allow zipping multiple files when shown in Finder's search results.


THANK YOU!

Feb 17, 2024 1:20 PM in response to VikingOSX

It's actually not a number, but a letter. And the letter changes in some of the three-digit ending suffixes that need to be captured in a zip together, so I don't think it works to capture back two more places (last 5 characters of filename, for example). That's why I was wondering if a random letter or number could just be appended/added to the front of the three-digit file ending. If yes, awesome. If not, no problem.

Feb 18, 2024 8:25 AM in response to swapot

What are the possible characters that can appear in these (x) locations of the filenames?


filename-name-(x)-(xxx).ext


The current regular expression capture is assuming that the first (x) is a number and the second is a three-character alpha string. Are you now stating that both captures may be alphanumeric? If this is true, then I would need to alter the RE to show the capture as two locations of alphanumeric and the script should still work.



Feb 19, 2024 1:03 PM in response to VikingOSX

"This is a contradiction to your earlier request of 1-ber.zip format that

I changed in the last posted script solution on 2024-02-17."


- Yes, I understand that I confused the request. The reason why I originally requested "1-ber.zip" as final output from the script was because I knew it would be easy for me to take the "1-ber.zip" and use Mac Finder to rename "1-" to "LOVE-Block-" (or other prefixes, such as "AMOR-Block-" and "LOVE-Lines" etc. With nothing in front of the "ber.zip", there is nothing to rename. I also thought that may be easiest on your end, since I didn't understand how a script would be able to grab and zip all of the 3-letter ending files, keep part of the whole filename, but not segment zips by changes by different filenames.


"(l) (o) (v) (e) (a) (m) (r) (h) (d) are not alphanumeric characters, they are alpha only (e.g. a-z)."


-My mistake on calling those alphanumeric. I actually thought alphanumeric meant letter OR number. Thanks for correcting.


"I have a revised version of the script that constructs the

Filename-blr.zip or Filename-ber.zip from your examples above. I will

post that when we are agreed on the changes necessary."


-There are files that contain any other charachers in the -x- position than these:


(l) (o) (v) (e) (a) (m) (r) (h) (d)


However, I as I mentioned in parenthesis above, not all file prefixes are "LOVE-Block-" before the -x- place, but only in different directories. They all contain the same "Filename-File-" prefix in one directory. Because I didn't think that was going to be included in the final zip name, I didn't mention.

This thread has been closed by the system or the community team. You may vote for any posts you find helpful, or search the Community for additional answers.

Can a zsh script to batch zip files work recursivly? (VikingOSX script in post)

Welcome to Apple Support Community
A forum where Apple customers help each other with their products. Get started with your Apple Account.