Batch resize and rename image files in subdirectories - bash

I'm trying to resize and rename several hundred subdirectories of images. The files that I need to be changed:
End with A.jpg
Need to be resized down to 400x400
Renamed to A#2x.jpg
Example:
images/**/A6919994719A#2x.jpg
I got the resizing bit down in one directory. I'm having some trouble finding a way to rename just the end of the file, not the extension, and executing it through the subdirectories.
Any help would be appreciated.
#!/bin/bash
for i in $( ls *A.jpg); do convert -resize 400x400 $i

You can do this:
#!/bin/bash
find . -name "*A.jpg" | while read f
do
newname=${f/A.jpg/A#2.jpg}
echo convert "$f" -resize 400x400 "$newname"
done
Remove the word echo if it looks correct, and run only on files you have backed up.
You can also do it in a one-liner, if you really want to:
find . -name "*A.jpg" -exec bash -c 'old="$0";new=${old/A.jpg/A#2.jpg};echo convert "$old" -resize 400x400 "$new"' {} \;

Related

Changing all images in a directory and its sub-directories using a single ImageMagick command?

I have a directory with multi level sub-directories containing .jpg files that I want to change using ImageMagick.
To change a single file I do convert image0.jpg -resize x1000 -quality 82 small_image0.jpg
But how can I do the same but for all the jpgs in every directories, with a single command?
That is:
Apply -resize x1000 -quality 82 to every jpg in all of the directories.
Every output file should be in the same directory as its input file but with small_ prepended to the name.
With find and bash.
#!/usr/bin/env bash
path=("$#")
while IFS= read -ru9 -d '' pic; do
dirname=$(dirname "$pic")
basename=$(basename "$pic")
printf 'Converting %s to %s\n' "$pic" "$dirname/small_$basename"
convert "$pic" -resize x1000 -quality 82 "$dirname/small_$basename" || exit
done 9< <(find "${path[#]}" -type f -name '*.jpg' -print0)
Assuming you name your script myscript, execute it with the path to the pictures as the argument.
./myscript /path/to/pictures
Change /path/to/pictures with the correct path/directory, or add more path/directory separated by a space e.g. ./myscript path1 path2 anotherpath morepath
instead of just one directory.

Bash convert resize recursively preserving filenames

Have images in subfolders that need to be limited in size (720 max width or 1100 max height). Their filenames must be preserved. Started with:
for img in *.jpg; do filename=${img%.*}; convert -resize 720x1100\> "$filename.jpg" "$filename.jpg"; done
which works within each directory, but have a lot of subfolders with these images. Tried find . -iname "*.jpg" -exec cat {} but it did not create a list as expected.
This also didn't work:
grep *.jpg | while read line ; do `for img in *.jpg; do filename=${img%.jpg}; convert -resize 720x1100\> "$filename.jpg" "$filename.jpg"`; done
Neither did this:
find . -iname '*jpg' -print0 | while IFS= read -r -d $'\0' line; do convert -resize 720x1100\> $line; done
which gives me error message "convert: no images defined." And then:
find . -iname '*.jpg' -print0 | xargs -0 -I{} convert -resize 720x1100\> {}
gives me the same error message.
It seems you're looking for simply this:
find /path/to/dir -name '*.jpg' -exec mogrify -resize 720x1100\> {} \;
In your examples, you strip the .jpg extension, and then you add it back. No need to strip at all, and that simplifies things a lot.
Also, convert filename filename is really the same as mogrify filename. mogrify is part of ImageMagick, it's useful for modifying files in-place, overwriting the original file. convert is useful for creating new files, preserving originals.
Since all of the subdirectories are two levels down, found this worked:
for img in **/*/*.jpg ; do filename=${img%.*}; convert -resize 720x1100\> "$filename.jpg" "$filename.jpg"; done
Thanks to #pjh for getting me started. This also worked:
shopt -s globstar ; for img in */*/*.jpg ; do filename=${img%.*}; convert -resize 720x1100\> "$filename.jpg" "$filename.jpg"; done
But I got the error message "-bash: shopt: globstar: invalid shell option name" but all of the images larger than specified were resized with filenames preserved.

How to automate conversion of images

I can convert an image like this:
convert -resize 50% foo.jpg foo_50.jpg
How can I automate such a command to convert all the images in a folder?
You can assume every image has .jpg extension.
A solution easily adaptable to automate the conversion of all the images inside the subdirectories of the working directory is preferable.
You can use a for loop with pattern expansion:
for img in */*.jpg ; do
convert -resize 50% "$img" "${img%.jpg}"_50.jpg
done
${variable%pattern} removes the pattern from the right side of the $variable.
You can use find -exec:
find -type f -name '*.jpg' -exec \
bash -c 'convert -resize 50% "$0" "${0%.jpg}"_50.jpg' {} \;
find -type f -name '*.jpg' finds all .jpg files (including those in subdirectories) and hands it to the command after -exec, where it can be referenced using {}.
Because we want to use parameter expansion, we can't use -exec convert -resize directly; we have to call bash -c and supply {} as a positional parameter to it ($0 inside the command). \; marks the end of the -exec command.
You can also try this (less elegant) one-liner using ls+awk:
ls *.jpg | awk -F '.' '{print "convert -resize 50% "$0" "$1"_50.jpg"}' | sh
this assumes that all the .jpg files are in the current directory. before running this, try to remove the | sh and see what is printed on the screen.

Converting all .pdf files in folder tree to .png

I'm on Ubuntu, and I have a tree of folders containing .pdf files. I need to convert each one to a .png format. The bash script I am currently using is:
for f in $(find ./polkadots -name 'image.pdf'); do
convert -transparent white -fuzz 10% $f image.png;
done
I have tested the for loop by itself, and it works (it produces a list of all the .pdf files under the ./polkadots folder that I need to convert):
for f in $(find ./polkadots -name 'image.pdf'); do
echo "$f";
done
I have tested the imagemagic convert command by itself, and it works (it converts a single file in my current directory from .pdf to .png):
convert -transparent white -fuzz 10% image.pdf image.png
However, when I combine them... the console sits and thinks for a while, and then concludes.. but no files have been created or changed, and no error messages are produced. What am I doing wrong?
EDIT: The new .png files are being created, but they are being created in my current directory, instead of in the sub-directory where the .pdf was found. How do I fix this?
Try using find alone. No need to use a loop.
I haven't tested this command but it should work.
find ./polkadots -name 'image.pdf' -exec convert -transparent white -fuzz 10% {} image.png \; -print
The -print at the end is optional. I prefer to see which files have been modified.
Maybe you can find output option in convert command directly, which can export the png file to your expect folder. Anyway follow your idea, here is the updated code:
find ./polkadots -type f -name "image.pdf" |while read line
do
dir=${line%/*}
convert -transparent white -fuzz 10% $line image.png
mv image.png ${dir}/image.png
done
If you need convert all pdf files under polkadots folder, try this:
find ./polkadots -type f -name "*.pdf" |while read line
do
dir=${line%/*}
file=${line##*/}
file=${file%.*}
convert -transparent white -fuzz 10% $line ${file}.png
echo mv ${file}.png ${dir}/${file}.png
done
If you are using bash 4+, you should use globstar instead of find
#!/bin/bash
shopt -s globstar
for f in ./polkadots/**/image.pdf; do
convert -transparent white -fuzz 10% "$f" "${f%/*}/image.png"
done
If you're using an older bash, your original answer is close to fine, but has a few bugs/potential bugs,
If you directories have spaces in them, your original script will case errors, so use the | while read -r -d '' f syntax.
Don't put a ; at the end of command lines
Quote all variables to prevent expansion problems
As you pointed out, main issue in your case was not specifying destination dir, so you can use ${f%/*} parameter expansion to get the dir (this will delete everything including and after the last / in $f, then put the / back and append the filename like below.
Edited
#!/bin/bash
find ./polkadots -name "image.pdf" -print0 | while read -r -d '' f; do
convert -transparent white -fuzz 10% "$f" "${f%/*}/image.png"
done

finding and renaming large *.jpg to *.jpeg

I have a large and messy collection of file--hey who doesn't--some of these are large JPGs (large in this case is an arbitrary number, say 2.5MB) that I want to rename--I want to change the extension from *.jpg to *.jpeg.
I'd love to do this with a shell script, I'm running BASH 3.2.39(1), and I have a feeling this is "simple" task with find, alas I find find's syntax difficult to remember and the man page impossible to read.
Any and all help with be most appreciated.
Finding and renaming large files could be done like this:
find . -size +2500k -exec rename -s .jpg .jpeg '{}' ';'
What OS are you using? In most repositories there is an app called mmv which is perfect for these kinds of things..
usage:
mmv \*.jpg \#1.jpeg
Install rename (standard tool in your linux installation or with homebrew for mac), then:
rename -s .jpg .jpeg *
or, if you have files in subdirectories too:
rename -s .jpg .jpeg $(find . -name '*.jpg')
for i in *.jpg
do
new_name= $(echo $i|sed 's/.jpg/.jpeg/')
mv $i $new.name
done

Resources