This question already has answers here:
Ignore case when trying to match file names using find command in Linux
(5 answers)
Closed 5 years ago.
I wrote a bash script a while back that resizes and compresses all images in directory with Image Magick.
# Usage: smartResize "2400x2400>"
function smartResize() {
find ./ -name "*.jpg" -exec magick mogrify -resize $1 -sampling-factor 4:2:0 -strip -interlace JPEG -quality 85 -colorspace RGB {} \;
find ./ -name "*.JPG" -exec magick mogrify -resize $1 -sampling-factor 4:2:0 -strip -interlace JPEG -quality 85 -colorspace RGB {} \;
find ./ -name "*.jpeg" -exec magick mogrify -resize $1 -sampling-factor 4:2:0 -strip -interlace JPEG -quality 85 -colorspace RGB {} \;
find ./ -name "*.JPEG" -exec magick mogrify -resize $1 -sampling-factor 4:2:0 -strip -interlace JPEG -quality 85 -colorspace RGB {} \;
}
I had to add extra lines to find some alternative cases for .jpg extensions. Which I don't particularly like but got the job done.
Does anybody have a better idea of how to handle case sensitivity and the optional e in jpeg extensions?
n.b. I'm running this in Cmder: Git Bash on Windows.
GNU versions of find have an -iname flag which enables case insensitive matching of file name globs,
find ./ -iname '*.jpg'
or if you are on a system without GNU utilities, use the bracket expressions to the glob
find ./ -name '*.[Jj][Pp][Gg]'
If you are interested in multiple name filters, just use the -o expression for including multiple name globs
find ./ \( -iname "*.jpg" -o -iname "*.jpeg" \)
Related
I wrote an shell script which
get list of all image files from directory
create new folder if needed for new image
optimize image in order to save storage resources
I've tried to use parallel -j "$(nproc)" before mogrify but found that it was wrong, because before mogrify is used DIR and mkdir, i need instead something like & at end of mogrify but to do it only for n processes.
the current code look like:
#!/bin/bash
find $1 -iname "*.jpg" -o -iname "*.jpeg" -o -iname "*.png" -o -iname "*.gif" -type f | while read IMAGE
do
DIR="$2"/`dirname $IMAGE`
echo "$IMAGE > $DIR"
mkdir -p $DIR
mogrify -path "$DIR" -resize "6000000#>" -filter Triangle -define filter:support=2 -unsharp 0.25x0.08+8.3+0.045 -dither None -posterize 136 -quality 82 -define jpeg:fancy-upsampling=off -define png:compression-filter=5 -define png:compression-level=9 -define png:compression-strategy=1 -define png:exclude-chunk=all -interlace none -colorspace sRGB "$IMAGE"
done
exit 0
Can someone suggest what will be the right way to run such script in parallel? as each run take about 15 seconds.
When you have a shell loop that does some setup and invokes an expensive command, the way to parallelize it is to use sem from GNU parallel:
for i in {1..10}
do
echo "Doing some stuff"
sem -j +0 sleep 2
done
sem --wait
This allows the loop to run and do its thing as normal, while also scheduling the commands to run in parallel (-j +0 runs one job per CPU core).
Make a bash function that deals correctly with one file and call that in parallel:
#!/bin/bash
doit() {
IMAGE="$1"
DIR="$2"/`dirname $IMAGE`
echo "$IMAGE > $DIR"
mkdir -p $DIR
mogrify -path "$DIR" -resize "6000000#>" -filter Triangle -define filter:support=2 -unsharp 0.25x0.08+8.3+0.045 -dither None -posterize 136 -quality 82 -define jpeg:fancy-upsampling=off -define png:compression-filter=5 -define png:compression-level=9 -define png:compression-strategy=1 -define png:exclude-chunk=all -interlace none -colorspace sRGB "$IMAGE"
}
export -f doit
find $1 -iname "*.jpg" -o -iname "*.jpeg" -o -iname "*.png" -o -iname "*.gif" -type f |
parallel doit
Default for GNU Parallel is to run one job per CPU-thread, so Çıproc is not needed.
This has less overhead than starting sem for each file (sem = 0.2 sec per call, parallel = 7 ms per call).
This question already has answers here:
How to loop through file names returned by find?
(17 answers)
Closed 4 years ago.
I wanna loop through jpg files, using find to locate them.
The result is the full path name including ./ in front. I want to remove the replace ./ with ./newsubdir/ so I can use that as the output file name in a process, creating a modified copy of the original in newsubdir using the same folder structure.
This is what I tried.
#!/bin/bash
find . -type f -name '*jpg'
for file do
echo ${file:1}
done
However the substring extraction didn't seem to work at all. Is there a reason for that or a different way to do this. Very new to Bash.
I was going for something like this as a end result. Trying to square a bunch of pictures but keep the folder structure.
#!/bin/bash
find . -type f -name '*jpg'
for file do
convert '$file[2048x2048]' -gravity center -extent 2048x2048 "./newsubdir${file:1}"
done
You were close! Sticking a little closer to the original code (and thus avoiding starting more shells than necessary):
#!/bin/bash
find . -type f -name '*.jpg' -exec bash -c '
for file do
convert "$file[2048x2048]" -gravity center -extent 2048x2048 "./newsubdir${file:1}"
done
' _ {} +
...or, using your original shell and avoiding -exec entirely:
#!/bin/bash
while IFS= read -r -d '' file; do
convert "$file[2048x2048]" -gravity center -extent 2048x2048 "./newsubdir${file:1}"
done < <(find . -type f -name '*.jpg' -print0)
These patterns and more are part of UsingFind.
find . -type f -name '*jpg' -exec bash -c '
file=$1; convert "${file}[2048x2048]" -gravity \
center -extent 2048x2048 "./newsubdir/${file:1}"' _ {} \;
Frankly, I think you're much better off writing a script to do the conversion and just calling it with find . -type f -name '*.jpg' -exec script {} \;. Doing that will help to avoid inevitable quoting problems.
I have the code below for image conversion.
I have a directory with many images, I would like to convert all images that the width was less than 200 pixel.
Regardless of the extension, jpg, gif or png
find . -iname \*.jpg -exec convert -verbose -resize 200x140! "{}" "{}" \;
I think you want this - or something very close to it - so make a backup first!
find . \( -iname \*.jpg -o -iname \*.png -o -iname \*.gif \) \
-exec bash -c '[ $(identify -format %w "$0" ) -lt 200 ] && convert "$0" -resize 200x140\! "$0"' {} \;
That says... "find, starting in the current directory (.), any files whose names end, in a case-insensitive fashion (-iname), in JPG, PNG or GIF and start a new bash shell for each one. Once inside the shell, get the width of the file and if it is less than 200 pixels, execute the convert command to resize the file to 200x140, ignoring aspect ratio."
The "first" part in there is: acquiring the width of all images in that folder. And if I read your question correctly, that is where you have problems with; thus you can look into the identify command coming with ImageMagick. It works like this
identify -format "%wx%h" pic.jpg
See here for handling formatting. As soon as you got your list of "width matching" files, you should be able to further convert them.
Say I wanted to change the size of a picture and keep the name the same, in terminal I would type: convert picture1.jpg -resize 1280x720! picture1.jpg Now what if I had a folder with hundred of these pictures and I wanted to change them all to 1280x720 and keep the same name. Is there any easier way then typing out that line for each picture?
cd myfolder
for file in *.jpg
do
convert "$file" -resize 1280x720! "$file"
done
In case there is tons of images you might run into trouble using an for f in *.jpg approach. In this case you might want to do something like:
find myfolder -maxdepth 1 -name *.jpg -exec convert \{\} -resize 1280x720! \{\} \;
That's exactly what mogrify is for:
mogrify -resize 1280x720! *.jpg
I am using mogrify to resize the images in a directory using the following command
mogrify -resize 100x100 *.jpg
due to the huge number of images, I get the following error
/usr/bin/mogrify: Argument list too long
Any suggestions?
Thanks
Actually, the answer is surprisingly simple. Rather than having the shell expand the argument list (which it cannot cope with), let ImageMagick expand the list itself internally, by protecting the arguments from the shell with single quotes.
So, your command becomes:
mogrify -resize 100x100 '*.jpg'
If the built-in glob expression does not work for you (eg. special file ordering), you may also use the special character '#':
mogrify -resize 100x100 #my_jpegs.txt
find or xargs come to mind, eg.
find . -name \*.jpg -exec mogrify '{}' -resize 100x100 \;
Cheers,
magick
said
Most shells limit the length of the command line. You can get around this by using ImageMagick filename globbing methods. So instead of mogrify -resize 50% *.jpg use mogrify -resize 50% "*.jpg"
Your case would be
mogrify -resize 100x100 "*.jpg"