Git Bash for loop through files - bash

Okay, I am trying to loop through all images in a directory and do some compression on them. Here is my code.
#!/bin/bash
for f in *.jpeg; do
echo "Processing $f file..";
magick convert -quality 85% $f $f
done
And here is the output from it:
$ bash compress.sh Processing *.jpeg file.. convert: unable to open
image '*.jpeg': Invalid argument # error/blob.c/OpenBlob/3094.
convert: no images defined `*.jpeg' #
error/convert.c/ConvertImageCommand/3254.
The $f reference is actually the string "*.jpeg", which is definitely not correct.
Here is my folder structure.

I ended up doing this, which does the job.
#!/bin/bash
find ./images -name '*.jpg' | while read FILE; do
magick convert -quality 75% "$FILE" "$FILE"
done

You should lookup files with appropriate filename mask (directory and extension). Also there should be skip path if bash can't expand filename:
#!/bin/bash
IMAGE_PATH=`dirname ${BASH_SOURCE[0]}`/nature
for f in $IMAGE_PATH/*.jpg $IMAGE_PATH/*.jpeg; do
echo -n "Processing $f file... ";
if [ ! -r "$f" ]; then
echo "skip!";
continue;
fi;
magick convert -quality 85% "$f" "$f";
echo "done.";
done
PS: "$f" (with quotes) used for files with spaces in their names.
UPDATE: Add using script directory.

Related

Put shell script for loop statement for multiple files in a function

#!/bin/sh
if [[ -d "$path" ]]; then
cd "$path" && echo "$PWD"
for file in *.jpg *.png *.jpeg *.gif *.gifv *.bmp; do
mv "$file" "$destpath"
done
fi
Since there are no arrays in POSIX defined. Can I put the *.jpg *.png *.jpeg *.gif *.gifv *.bmp; in a function? Because I am using these multiple times in a script.
Is it possible to refactor cd "$path" && echo "$PWD" in my example? e.g. get rid of the cd command but still achieve the same effect? Thanks!
You can use `ls *.jpg *.png *.jpeg *.gif *.gifv *.bmp` this syntax instead of making a array.
#!/bin/bash
if [[ -d "$path" ]]; then
cd "$path" && echo "$PWD"
for file in `ls *.jpg *.png *.jpeg *.gif *.gifv *.bmp`; do
mv "$file" "$destpath"
done
fi
Note: The destpath is should be a absolute path. If not, the destpath is treated as just a string.
This one works for multiple file types
for file in *.{jpg,jpeg,png,gif,gifv,bmp}; do
done

How to recursively resize images from one folder to another?

I would like to create thumbnails of images I have organised into a set of nested subdirectories into a mirror of the file structure so that a command of the type:
./imageresize.sh Large Small 10
...would convert any .jpg or .JPG files, in the directories nested under "./Large":
./Large/Holidays/001.jpg
./Large/Holidays/002.jpg
./Large/Holidays/003.jpg
./Large/Pets/Dog/001.jpg
./Large/Pets/Dog/002.jpg
./Large/Pets/Cat/001.jpg
into thumbnails of 10% to a mirror destination with a different top directory ("Small" instead of "Large" in this e.g.):
./Small/Holidays/001.jpg
./Small/Holidays/002.jpg
./Small/Holidays/003.jpg
./Small/Pets/Dog/001.jpg
./Small/Pets/Dog/002.jpg
./Small/Pets/Cat/001.jpg
This is what I have so far, but I can't seem to get it working. The $newfile variable seems invalid but I don't know why, and when testing, it outputs the result of the 'convert' command to the screen. Any help/suggestions greatly appreciated.
#!/bin/bash
#manage whitespace and escape characters
OIFS="$IFS"
IFS=$'\n'
#create file list
filelist=$(find ./$1/ -name "*.jpg" -o -name "*.JPG")
for file in $filelist
do
#create destination path for 'convert' command
newfile= ${file/$1/$2}
convert "$file" -define jpeg:extent=10kb -scale $3% "$newfile"
done
Don't know if it's just a copy/paste error or if it's actually in your script, but this line:
newfile= ${file/$1/$2}
would be an invalid assignment in Bash as spaces around = isn't allowed when assigning.
Try this instead:
newfile=${file/$1/$2}
As a side-note. find has a case-insensitive search too, -iname, so you could do:
filelist=$(find ./$1/ -iname "*.jpg")
It also has -exec for executing commands on the result set. It's explained very well here: https://unix.stackexchange.com/questions/12902/how-to-run-find-exec So you may be able to do it in one single find. (Note: That's not necessarily better, in most cases it's just a matter of preference, I just mention it as a possibility.)
So - revised working script with corrections suggested by madsen and 4ae1e1 (thanks both), and with rsync commands to create directory structure first, (then clean extraneous files from the destination) :). I've added an extra parameter and parameter checker so that now you can specify source, destination, approx destination file size in kb and percentage of original. Hope it helps someone else. :)
#!/bin/bash
#manage whitespace and escape characters
OIFS="$IFS"
IFS=$'\n'
#check parameters
if [ $# -lt 4 ] || [ "$1" = "--help" ]
then # if no parameters or '--help' as $1 - show help
echo "______________________________________________________________"
echo ""
echo "Useage: thumbnailmirror [Source] [Destination] [Filesize] [Percentage]"
echo "Source - e.g. 'Fullsize' (directory must exist)"
echo "Destination - e.g. 'Thumnail' (directory must exist)"
echo "Filesize - approx filesize in kb e.g. '10'"
echo "Percentage - % of reduction (1-100)"
echo "e.g. thumbnailmirror Fullsize Thumnail 18 7"
echo "______________________________________________________________"
else # parameters exist
#syncronise directory structure (directories only)
rsync -a --include '*/' --exclude '*' ./$1/ ./$2/
# delete any extraneous files and directories at destination
rsync -a --delete --existing --ignore-existing ./$1/ ./$2/
#create file list ( -iname means not case sensitive)
filelist=$(find ./$1/ -iname "*.jpg")
for file in $filelist
do
#define destination filename for 'convert' command
newfile=${file/$1/$2}
if [ ! -f "$newfile" ] #if file doesn't exists create it
then
convert "$file" -define jpeg:extent=$3kb -quality 100 -scale $4% "$newfile"
echo "$file resized"
else #skip it
echo "Skipping $file - exists already"
fi
done
fi

HandBrakeCLI bash script convert all videos in a folder

Firstly, I searched around for my problem. But none can solve it.
I want to convert all videos file in a directory and the output will be saved in another directory. I got a bash script from somewhere I dont remember.
#!/bin/bash
SRC="/home/abc/public_html/filex/store/vids/toriko/VIDEOS HERE"
DEST="/home/abc/public_html/filex/store/vids/toriko/51-100"
DEST_EXT=mp4
HANDBRAKE_CLI=HandBrakeCLI
PRESET="iPhone & iPod Touch"
for FILE in "`ls $SRC`"
do
filename=$(basename $FILE)
extension=${filename##*.}
filename=${filename%.*}
$HANDBRAKE_CLI -i "$SRC"/$FILE -o "$DEST"/"$filename".$DEST_EXT "$PRESET"
done
the problem is, the output of the file will be without filename.. only ".mp4".
and, there is only 1 file generated.. means, from 50 videos in the folder, only 1 files generated with name ".mp4" and after that, HandBrakeCLI exit.
can anyone fix my code?
I got no experince in bash coding.. so, the right script giiven will be appreciate :)
Your line
for FILE in "`ls $SRC`"
effectively creates only one iteration where FILE contains the list of the files (and it is not able to handle the space in $SRC). Better replace it with
for FILE in "$SRC"/*
Example:
$ ls test
1.txt 2.txt
$ SRC=test; for f in "`ls $SRC`" ; do echo $f; done
1.txt 2.txt
$ SRC=test; for f in "$SRC"/* ; do echo $f; done
test/1.txt
test/2.txt
Side note: you can have a space in there with no problem
$ ls "the test"
1.txt 2.txt
$ SRC="the test"; for f in "$SRC"/* ; do echo $f; done
the test/1.txt
the test/2.txt
I tried this script, and others like it, but I wanted to convert recursive directory tree's and have files placed in the same directory with .mp4 extension and delete .avi files, after much trial and error I gave up on this code and searched for a new code, id like to credit
http://www.surlyjake.com/blog/2010/08/10/script-to-run-handbrake-recursively-through-a-folder-tree/
For the original code!
Here is my modified script, barely modified BTW this script is short, sweet and easy to understand.
#!/bin/bash
# This Script Goes in Root Folder of TV show -- Example Folder Structure
# /Stargate/Season\ 1/Epiosde.avi
# /Stargate/Season\ 2/Epiosde.avi
# /Stargate/handbrake_folder.script
# Outputs all Files back inside same dir's and does all folders inside Startgate DIR
# /Stargate/Season\ 1/Epiosde.mp4
# /Stargate/Season\ 2/Epiosde.mp4
# PRESET = -o flags for CLI can be got from GUI under Activity Log or from https://trac.handbrake.fr/wiki/CLIGuide OR you can use actual Presets!
# PRESET="iPhone & iPod Touch"
PRESET="--modulus 2 -e x264 -q 20 --vfr -a 1 -E ac3 -6 5point1 -R Auto -B 384 -D 0 --gain 0 --audio-fallback ac3 --encoder-preset=veryfast --encoder-level="5.2" --encoder-profile=high --verbose=1"
if [ -z "$1" ] ; then
TRANSCODEDIR="."
else
TRANSCODEDIR="$1"
fi
find "$TRANSCODEDIR"/* -type f -name "*.avi" -exec bash -c 'HandBrakeCLI -i "$1" -o "${1%\.*}".mp4 --preset="$PRESET"' __ {} \; && find . -name '*.avi' -exec rm -r {} \;
BE WARNED: THIS WILL CONVERT THEN DELETE ALL .AVI FILES ABOVE THE SCRIPT IN FILE TREE!
Feel free to remove the
[-name "*.avi"] & [&& find . -name '*.avi' -exec rm -r {} \;]
to disable only converting .avi and removal of .avi or modify to suite another extension.
I have found the solution:
#!/bin/bash
SRC="/home/abc/public_html/filex/store/vids/toriko/VIDEOS HERE"
DEST="/home/abc/public_html/filex/store/vids/toriko/51-100"
DEST_EXT=mp4
HANDBRAKE_CLI=HandBrakeCLI
for FILE in "$SRC"/*
do
filename=$(basename "$FILE")
extension=${filename##*.}
filename=${filename%.*}
$HANDBRAKE_CLI -i "$FILE" -o "$DEST"/"$filename".$DEST_EXT
done
I just tried using this script with the modification suggested above. I found I need to to put double quotes around the two uses of $FILE in order to handle file names with spaces.
So...
filename=$(basename "$FILE")
and
$HANDBRAKE_CLI -i "$SRC"/"$FILE" -o "$DEST"/"$filename".$DEST_EXT "$PRESET"
I'd rather prefer this solution:
#!/bin/bash
SRC="$1"
DEST="$2"
EXT='mp4'
PRESET='iPhone & iPod Touch'
#for FILE in "`ls $SRC`"; do
for FILE in `find . -type f`; do
FILE=$(basename "$FILE")
filename=$(basename "$FILE")
extension=${filename##*.}
filename=${filename%.*}
HandBrakeCLI -i "$SRC"/$FILE -o "$DEST"/"$filename"."$EXT" "$PRESET"
done

Get multiple extensions in a for loop for transforming jpg,JPG and jpeg files

I want to convert all jpgs (independent from lower or upper case or jpeg extension) in my current directory to a 95% optimized one. However I do not get the files correctly in my for loop with JPG,jpg and jpeg files :/
#!/bin/bash
PIC=$(ls "$PWD"/*.{jpg,jpeg,JPG})
for i in $PIC
do
echo $i
# convert $i -quality 95 ${i%.*}_resaved.jpg
done
Try:
find -iname "*.jpg" -o -iname "*.jpeg" | while read f; do
echo "$f"
convert "$f" -quality 95 "${f%.*}_resaved.jpg"
done

Batch converting videos in folders and sub folders

I have a huge collection of videos that all need to be converted into mp4. The folder structure is like this
Events
Chicago
Boston
San Fran
London Expo
Inside each event holds all of videos in either avi format or .mkv. I want them to be converted to the same file name. but with the mp4 extension.
My question is how do I loop through a folders sub folders, and also keep the file names because at the moment they have spaces in them.. Here is what I have at the moment.
sourcedir="$1"
destdir="$2"
cd "$sourcedir"
for i in `ls`; do
HandBrakeCLI -i "$i" -o "$destdir/${i%.*}.mp4" --preset="AppleTV"
echo $i
done
Phillips Code:
cd "$sourcedir"
echo "Directory: $sourcedir"
destdir = sourcedir
for subdir in *
do
if [[ -d "$subdir" ]]
then
for file in "$subdir"/*
do
HandBrakeCLI -i "$file" -o "${file%.*}.mp4" --preset=AppleTV
echo "$file"
done
fi
done
Use a nested loop, and don't use ls:
for subdir in "$sourcedir"/*
do
if [[ -d "$subdir" ]]
then
for file in "$subdir"/*
do
HandBrakeCLI -i "$file" -o "$destdir/${file%.*}.mp4" --preset=AppleTV
echo "$file"
done
fi
done
Another option is to use find:
find "$sourcedir" -maxdepth 2 -mindepth 2 -type f -exec bash -c 'HandBrakeCLI -i "$0" -o "'"$destdir"'/${0%.*}.mp4" --preset=AppleTV' '{}' ';' -print
Both solutions will work with filenames containing spaces or newlines.
Either use for i in "$sourcedir/*" (or since you've already done a cd there you could do for i in *).
or do find "$sourcedir" -type f | while read -r i (with this, the variable i will include the source directory name, so you'll have to strip that off using a brace expansion or basename).
change program to iVI link to iVi.
this program does the trcik, even better than handbrake...
bash 4
shopt -s globstar
destdir="/somewhere"
for file in **/*.mkv **/*.avi
do
HandBrakeCLI -i "$file" -o "$destdir/${file%.*}.mp4" --preset=AppleTV
done

Resources