If you're open to using find
, then this becomes pretty easy:
#!/usr/bin/env bash
find "$1" \( -iname \*.jpg -o -iname \*.jpeg \) -print0 | while read -r -d $'\0' file; do
# base="${file##*/}" $base is the file name with all the directory stuff stripped off
# dir="${file%/*} $dir is the directory with the file name stripped off
mogify -resize '112x112!' "$file"
done
Put that in a file named mymog.bash
then
$ chmod 755 mymog.bash
$ mymog.bash /some/dir
Notes:
!
is special to bash
, so putting that in the single quotes make it "unspecial", passing it along to the mogrify
command unmolested.
- The double quotes around
$1
and $file
are needed in case a directory or file name has spaces in it. If you had a directory named /Users/alice/my pictures
and didn't use the quotes, mogrify
would get one argument named /Users/alice/my
and another one named pictures
.
- Make sure you use the
\(
and \)
for find
. That makes the whole condition ("match *.jpg" OR "match *.jpeg") apply to the action -print0
.
- I used
find
's -print0
action which prints each matching file name with a null-terminated (zero-terminated) string. You can have filenames that have newline characters in the middle. This protects against that.
bash
's built-in read
command reads until a newline by default. I used the -d $'\0'
to make it read each "line" or "record" (filename) up to the null (zero) character at its end. (Each ends with null because of the -print0
.)
This solution (one of many) has two parts:
- It uses the
find
utility to find (under the directory given) all files that end in .jpg
or .jpeg
, ignoring the case of the filenames. [So it will match .JPG
or even \.JpEg
.]
- It spits out one record for each file.
- If you give it an absolute path like
/some/dir
, it will find /some/dir/a.jpg
and /some/dir/sub1/sub2/sub3/b.jpg
.
- If you give it a relative path like
../../nearby/dir
, it will find ../../nearby/dir/c.jpg
and ../../nearby/dir/sub1/sub2/sub3/d.jpeg
.
- The
find
part ends with the first |
on that line. After that, it is a bash
while
…do
loop.
- The variable
file
takes on the value of each record spit out by find
.
- The loop (everything between
do
and done
) runs once for each value that file
takes on.
- The two rows that start with
#
are comments. They contain commands that are ignored (skipped). You can remove the #
to have bash
run those commands too. I included them as examples in case you needed the directory part or just the filename part of the record.
${file[@]%.jpg}
should emit? you might just needmogrify -resize 112x112! "${file}"
, but hard to be sure as you haven't specified what you need to work withmogrify
. AND you're hoping for your process to be fast; don't get hung up on that, it is more important that your code doesn't cause any unfixable errors. Once you are certain about the safety of your code, then you can think about "what can it do to make it run faster", but given the constraints of your project, a shellfor
loop (what you have) is likely as fast as you'll be able to achieve (IHMO). Good luck.[@]
bit is normally used to get all elements of an array, butfile
isn't an array, so this doesn't make sense. Also, the%.jpg
part means "remove '.jpg' from the end", so if your script findssomefolder/12345.jpg
, it'll try to runmogrify
onsomefolder/12345
instead.