35

I have a lot of images that I would like to process with pngquant. They are organized in a pretty deep directory structure, so it is very time-consuming to manually cd into every directory and run pngquant -ext .png -force 256 *.png

Is there a way to get this command to run on every *.png in every directory within the current one, as many layers deep as necessary?

cmal
  • 1,751
  • 5
  • 18
  • 29

2 Answers2

75

If you have limited depth of directories and not too many files, then lazy solution:

pngquant *.png */*.png */*/*.png

A standard solution:

find . -name '*.png' -exec pngquant --ext .png --force 256 {} \;

and multi-core version:

find . -name '*.png' -print0 | xargs -0 -P8 -L1 pngquant --ext .png --force 256

where -P8 defines number of CPUs, and -L1 defines a number of images to process in one pngquant call (I use -L4 for folders with a lot of small images to save on process start).

Kornel
  • 97,764
  • 37
  • 219
  • 309
  • 2
    `find . -name '*.png' -exec pngquant -ext .png -force 256 {} \;` works beautifully for me. Thank you. – cmal Mar 10 '12 at 20:05
  • Thank you for the answer, worked like a charm for me. However, could you explain the syntax regarding the number of cores? Is that a pngquant option or a OS option which divides the tasks and gives some images to each thread? – Nicolas Dec 10 '12 at 20:23
  • @Stewie that's `xargs`' option that tells it to run number of tasks in parallel. – Kornel Dec 11 '12 at 18:16
  • 3
    Alternatively, depending on whether you have a reasonably recent `bash` or `zsh`, and the option is enabled (`shopt -s globstar` in `bash`), you can use a recursive glob: `pngquant **/*.png` – gid Jun 19 '13 at 13:40
  • 4
    `find . -name '*.png' -exec pngquant --ext .png --force 256 {} \;` fails with `File not found - '*.png'` – Sprintstar Jan 06 '14 at 10:15
  • Sometimes the XARGS version doesn't work. It prints 'Bad file number'. I think problem in paths – arm.localhost May 27 '14 at 12:13
  • @porneL: The multi-core version wasn't actually multi-core, as without `-L1` it adds all arguments to a single pngquant call, which processes them serially :). Fixed. – Tomáš Kafka Jul 10 '14 at 11:22
  • What could be a Windows equivalent of the multi-core command? – Nicolas Jul 10 '14 at 21:50
  • thanks for the answer, very useful indeed. The verbose flag is a nice addition to it as it can often take a while for the process to finish. The command I eventually went with on my macbook intel core i5 2.3 (I believe this has 2 cores) was: `find . -name '*.png' -print0 | xargs -0 -P2 -L4 pngquant --ext .png --force -v 256` – Tisch Sep 22 '14 at 20:14
  • Mini benchmark of process start overhead (Ryzen 1700, 8c16t @ 2.7GHz), 1364 files on Win/cygwin: `xargs -0 -L 32 -P 8 ./optipng.exe ...` - real = 19.7s || `xargs -0 -I{} -P 16 ./optipng.exe ...` - real = 22s || `xargs -0 -I{} -P 8 ./optipng.exe ...` - real = 27.7s – BotOfWar Sep 18 '19 at 22:52
21

With the fish shell you can run the following from the root of your project directory

pngquant **.png

Which will generate new files with extensions like -or8.png or -fs8.png.

If you want to overwrite the existing files, you can use

pngquant **.png --ext .png --force
Dennis
  • 56,821
  • 26
  • 143
  • 139