Batch convert images files jpg, png to webp also in subfolders - linux

I have found a script to convert files in a directoy
but I need this with subdirectors
Can you help me ?
#!/bin/bash
PATH=/usr/local/bin:/usr/bin:/bin
# cd to the directory of the image so we can work with just filenames
dir="$(dirname "$1")"
cd "$dir" || exit 1
base="$(basename "$1" .png)"
# create a WebP version of the PNG
cwebp -q 80 "$base".png -o "$base".webp
# delete the WebP file if it is equal size or larger than the original PNG
if [[ `stat -c '%s' "$base".webp` -ge `stat -c '%s' "$base".png` ]]; then
echo "Deleting WebP file that is no smaller than PNG"
rm -f "$base".webp
fi
# delete the WebP file if it is size 0
if [[ -f "$base".webp && ! -s "$base".webp ]]; then
echo "Deleting empty WebP file"
rm -f "$base".webp
fi

Good news: You won't be have to change the script!
you can find all directories inside the root dir by the command:
find /path/to/root/dir -type d
and you can add execute of some command for each found dir:
Assuming your script name is script.sh and it is located in your home dir, and you want to run it on all sub dirs under current dir (include the current dir):
find . -type d -exec ~/script.sh "{}" \;

I had a similar issue when trying to find jpg images in a directory tree. The tree command helped me greatly. Below is sample code I used to iterate through all directories and only function on those with jpg images.
tree -dfi ${dir_name} | sed 's/$/\//g' | while read line
do
if [ `ls "${line}" | grep -ci jpg` -gt 0 ]
then
some code
fi
done
In your case you could grep for only PNG or perhaps do both jpg and png. One other option would be find commands using the file name and output to a while loop
find ${dir_name} -type f -name "*.jpg" | while read line
do
some code
done

Related

Renaming files in subdirectories deletes the files

I am writing a script that renames *.MP4 files on an inserted SD card and then rsyncs them.
The directory with the *.MP4 files does not always have the same name:
eg: it could be /DCIM/123_PANA/ or /DCIM/141_PANA/ etc
So I'm trying to write a script that will see what folders are in the /DCIM path, and rename all the *.MP4 files, (there is also a MISC folder in this path which I suspect is causing the issue)
I am using a couple of variables to rename the files also
What I have is:
for f in /media/pi/LUMIX/DCIM/*; do
if [ -d "$f" ]; then
echo $f
for file in $(find $f -name 'P*.MP4')
do
echo $file ">" $(dirname "${file}")/$(date +"%d")$cardname$(basename $file)
mv $file $(dirname "${file}")/$(date +"%d")$cardname$(basename $file)
done
fi
done
But what seems to happen is I end up with a single file with the prefix only (say 08_nb1_) in the _PANA folder, all the others have been deleted. Obviously this is not my desired result!
UPDATE:
$cardname is of the format _nb2_
When I do as asked replace mv with echo here is the output:
/media/pi/LUMIX/DCIM/141_PANA
mv /media/pi/LUMIX/DCIM/141_PANA/P1410192.MP4 /media/pi/LUMIX/DCIM/141_PANA/09_nb2_P1410192.MP4
mv /media/pi/LUMIX/DCIM/141_PANA/P1410193.MP4 /media/pi/LUMIX/DCIM/141_PANA/09_nb2_P1410193.MP4
mv /media/pi/LUMIX/DCIM/141_PANA/P1410194.MP4 /media/pi/LUMIX/DCIM/141_PANA/09_nb2_P1410194.MP4
mv /media/pi/LUMIX/DCIM/141_PANA/P1410195.MP4 /media/pi/LUMIX/DCIM/141_PANA/09_nb2_P1410195.MP4
mv /media/pi/LUMIX/DCIM/141_PANA/P1410196.MP4 /media/pi/LUMIX/DCIM/141_PANA/09_nb2_P1410196.MP4
mv /media/pi/LUMIX/DCIM/141_PANA/P1410197.MP4 /media/pi/LUMIX/DCIM/141_PANA/09_nb2_P1410197.MP4
mv /media/pi/LUMIX/DCIM/141_PANA/P1410198.MP4 /media/pi/LUMIX/DCIM/141_PANA/09_nb2_P1410198.MP4
mv /media/pi/LUMIX/DCIM/141_PANA/P1410199.MP4 /media/pi/LUMIX/DCIM/141_PANA/09_nb2_P1410199.MP4
mv /media/pi/LUMIX/DCIM/141_PANA/P1410200.MP4 /media/pi/LUMIX/DCIM/141_PANA/09_nb2_P1410200.MP4
mv /media/pi/LUMIX/DCIM/141_PANA/P1410201.MP4 /media/pi/LUMIX/DCIM/141_PANA/09_nb2_P1410201.MP4
mv /media/pi/LUMIX/DCIM/141_PANA/P1410202.MP4 /media/pi/LUMIX/DCIM/141_PANA/09_nb2_P1410202.MP4
mv /media/pi/LUMIX/DCIM/141_PANA/P1410203.MP4 /media/pi/LUMIX/DCIM/141_PANA/09_nb2_P1410203.MP4
mv /media/pi/LUMIX/DCIM/141_PANA/P1410204.MP4 /media/pi/LUMIX/DCIM/141_PANA/09_nb2_P1410204.MP4
/media/pi/LUMIX/DCIM/MISC
OK I fixed it by filtering the additional directory names and limiting to only the ones with *_PANA - which solved the issue. I also added a the rsync part and demounted the SD card (if required using Zenity)
A text file placed on the SD card identifies it as a unique card giving each file a unique name when rsyncing it to the backup folder. Renaming on the SD card means
that it can still be used an written to if not full, but we then know which files have been backed up.
Very useful in the field when filming with multiple cards, crews. All running on a Rpi4
for f in /media/pi/LUMIX/DCIM/*_PANA/; do
if [ -d "$f" ]; then
echo "$f"
for file in $(find $f -wholename '*_PANA/P*.MP4')
do
mv "$file" $(dirname "${file}")/$(date +"%d")"$cardname"$(basename "${file}")
done
rsync --stats -u --progress "$f"/*.MP4 /media/pi/VDRIVE/ | tee /home/pi/Documents/ytu/rsync.txt | zenity --icon-name="dialog-warning" \
--width=300 --progress --pulsate --auto-close --auto-kill \
--title="Copying $sdn"
zenity --question --text="Unmount Card?"
if [ $? = 0 ]; then
umount /media/pi/LUMIX
else
exit
fi
fi
done

Command script recognizes files as directories

The following code should count the number of elements that a directory contains, but as well as it does it correctly, it also recognizes every element inside the current directory as a directory .
I don't know how not to show the elements that are not directories. How could I do it?
Code is here: http://pastebin.com/9R4eB4Xn
termlog.txt:
https://justpaste.it/tgsl
As you may see, some files like .jpg or .zip are recognized as directories.
Your echo "Element is a directory" is between the if and the then. Move it after then :
for i in *
do
if [ ! -f "$i" ] && [ -d "$i" ]
then
echo "Element is a directory"
FILES=`ls -l "$i" | wc -l` # List the content of "$i" directory
# and count the number of lines
FILES2=`expr $FILES - 1` # Substract one because one line is
# occupied with the number of blocks
echo "$i: $FILES2" # Shows the name of the directory and
# the number of inputs that it has
fi
done
for i in `find DIRECTORY -maxdepth 2 -type d`; do echo "$i: `ls -1 $i | wc -l`"; done
If only interested in current directory, replace DIRECTORY with .

bash, detecting mp4 files

if [ -f *.mp4 ]; then
for file in *.mp4; do
ffmpeg2theora -v 8 --nometadata "$file"
rm -f "$file"
done
fi
if [ -f *.avi ]; then
for file in *.avi; do
ffmpeg2theora -v 8 --nometadata "$file"
rm -f "$file"
done
fi
I can't figure out why if [ -f *.mp4 ]; then is not detecting mp4 files, i use this method it to find .avi, .epub, .chm and some other extensions and it works perfectly. I wonder me if it is because of the 4. i also tried by doing if [ -f *."mp4" ]; then but it didn't work.
I find weird that for file in *.mp4; do detects .mp4 files and if [ -f *.mp4 ] not!
Use the bash option nullglob to allow globs to expand to zero elements. This way, the loop will simply not run if there are no matching files:
shopt -s nullglob
for file in *.mp4; do
ffmpeg2theora -v 8 --nometadata "$file" && rm -f "$file" || \
echo Problem transcoding "$file"
done
As for [ -f *.mp4 ], it only works correctly if there is exactly one matching file, because then it expands to [ -f myfile.mp4 ].
If there are no matching files, it may still work (without nullglob) because it ends up checking for a file with an asterisk in the name, which hopefully doesn't exist.
If there are multiple files, it fails, because [ -f file1.mp4 file2.mp4 file3.mp4 ] is not valid test syntax.
if [ -f *.mp4 ]; then
There is no expansion inside the test. The test is looking for a file named *.mp4 which doesn't exist. What you should do it:
for i in *.mp4; do
<do something>
done
Here expansion will provide all files with .mp4 extensions in the present working directory. You can also you find /path/to/dir -type f -name "*.mp4" to get your filenames as well. (note: this expects .mp4 files to be present in the directory. If not, i=*.mp4. You are safer doing:
find /path -type f -name "*.mp4" | while read i; do
<do whatever>
done

Rewrite a script so it takes option arguments to control its behaviour

I created a script and it moves files with different extensions to their specified directories.
If the directory is not there, it creates another one (where the files will go), and it creates another directory where the remaining files with different extensions will go.
My first problem is that I want when I put -d and full path on the terminal it should move only media files, -l and full path to move all text files, then -x to change the extension to uppercase, then -u to lowercase.
Can somebody modify it for me and show me how to overcome this problem?
#!/bin/bash
From="/home/elg19/lone/doc"
To="/home/elg19/mu"
WA="/home/elg19/du"
MA="/home/elg19/dq"
WQ="/home/elg19/d2"
# this function checks if the directory exits and creates one if it does not then moves all doc files
function mama(){
if [[ ! -d "$WA" ]]; then
mkdir -p "$WA"
fi
cd "$From"
for i in pdf txt doc; do
find . -type f -name "*.${i}" -exec mv "{}" "$WA" \;
done
}
# this function checks if the directory exits and creates one if it does not then moves all media files
function so(){
if [[ ! -d "$To" ]]; then
mkdir -p "$To"
fi
cd "$From"
for i in mp3 mp4 swf; do
find . -type f -name "*.${i}" -exec mv "{}" "$To" \;
done
}
# this function checks if the directory exits and creates one if it does not then moves all image files
function soa(){
if [[ ! -d "$MA" ]]; then
mkdir -p "$MA"
fi
cd "$From"
for i in jpg gif png; do
find . -type f -name "*.${i}" -exec mv "{}" "$MA" \;
done
}
# this function checks if the directory exits and creates one if it does not then moves all the remaining files
function soaq(){
if [[ ! -d "$WQ" ]]; then
mkdir -p "$WQ"
fi
cd "$From"
for i in *; do
find . -type f -name "*.${i}" -exec mv "{}" "$WQ" \;
done
}
mama
so
soa
soaq
I don't know if the options suggested are mnemonic in your native language, but they are counter-mnemonic in English. I would suggest something more like:
-m path Move media files
-t path Move text files
-u Change extensions to upper-case
-l Change extensions to lower-case
The command to use for regular argument parsing like this is getopts (plural - many systems also have a command getopt, singular, which has different characteristics altogether).
The referenced page gives an example of how to use it:
The following example script parses and displays its arguments:
aflag=
bflag=
while getopts ab: name
do
case $name in
a) aflag=1;;
b) bflag=1
bval="$OPTARG";;
?) printf "Usage: %s: [-a] [-b value] args\n" $0
exit 2;;
esac
done
if [ ! -z "$aflag" ]; then
printf "Option -a specified\n"
fi
if [ ! -z "$bflag" ]; then
printf 'Option -b "%s" specified\n' "$bval"
fi
shift $(($OPTIND - 1))
printf "Remaining arguments are: %s\n" "$*"
The option -a doesn't take an argument; the option -b requires an argument.

Appending rather than overwriting files when moving

I have the following directory structure:
+-archive
+-a
+-data.txt
+-b
+-data.txt
+-incoming
+-a
+-data.txt
+-c
+-data.txt
How do I do the equivalent of mv incoming/* archive/ but have the contents of the files in incoming appended to those in archive rather than overwrite them?
# move to incoming/ so that we don't
# need to strip a path prefix
cd incoming
# create directories that are missing in archive
for d in `find . -type d`; do
if [ ! -d "../archive/$d" ]; then
mkdir -p "../archive/$d"
fi
done
# concatenate all files to already existing
# ones (or automatically create them)
for f in `find . -type f`; do
cat "$f" >> "../archive/$f"
done
This should find any file in incoming and concatenate it to an existing file in archive.
The important part is to be inside incoming, because else we'd had to strip the path prefix (which is possible, but in the above case unnecessary). In the above case, a value of $f typically looks like ./a/data.txt, and hence the redirection goes to ../archive/./a/data.txt.
run it on the current directory.
find ./incoming -type f | while read -r FILE
do
dest=${FILE/incoming/archive}
cat "$FILE" >> "$dest"
done
the one in incoming/c would not be appended though
Here's a version with proper quoting:
#!/bin/sh
if [ -z "$1" ]; then
# acting as parent script
find incoming -type f -exec "$0" {} \;
else
# acting as child script
for in_file; do
if [ -f "$in_file" ]; then
destfile="${in_file/incoming/archive}"
test -d "$(dirname "$destfile")" || mkdir -p "$_"
cat "$in_file" >> "$destfile" &&
rm -f "$in_file"
fi
done
fi

Resources