bash, detecting mp4 files - linux

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

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

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

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

Please tell me in detail about the following Script

I want to delete email "sudar.dudi#in.xyz.com" from all the files which lies in a particular directory. I used the below command after searching a lot.
The problem with this command is, it keeps on searching other than the desired directories and due to this command my other files got corrupted.
Please help me to correct this:
rrcg0/utioi
(in utioi direcotry are my files and the files are with specific names
like mpp.properties.JKH,mpp.properties.KJL,mpp.properties.KHF
likewise)
yes n|for file in $(find . -type f)
do
sed 's/sudar.dudi#in.xyz.com//g' $file > $file.tmp
mv $file.tmp $file
done
You do not need find when you know the directory.
When you want to check that an entry given by ls is a file, you can use a test:
mydir=rrcg0/utioi
for file in ${mydir}/*
do
if [ -f "${file}" ]; then
sed 's/sudar.dudi#in.xyz.com//g' $file > $file.tmp && mv $file.tmp $file
fi
done
When you only want properies files you can use
for file in ${mydir}/*properties*
I have used &&. This makes sure the mv command will only be done when the redirection succeeded.
I did not use the sed -i option, that option is not always supported.
EDIT: What about spaces in the filename?
When you might have spaces in the filename, the above script must be corrected:
sed 's/sudar.dudi#in.xyz.com//g' "$file" > "$file.tmp" && mv "$file.tmp" "$file"
Make it simpler, try using exec option of find and GNU sed option for inline editing
find <absolute dir path> -name "*" -type f -exec sed -i '/hello/d' {} \;
You can make changes to the existing files also, as then there is no need to move the file. Include -i in sed command.
Modifying codes of Walter A
mydir=rrcg0/utioi
for file in ${mydir}/*
do
if [ -f "${file}" ]; then
sed -i 's/sudar.dudi#in.xyz.com//g' $file
fi
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