Need script to move and rename files without overwriting duplicate filenames - linux

Maybe i'm just going about this wrong and making it harder than it has to be.
This is my problem. I have 2 different scripts that download various picture files. the first downloads from email and the downloaded files go into the /attachments/ directory. The second script copies the contents of google drive, all files and folders get copied into ~/gdrive/ directory. i want to be able to move all picture files from both these folders as well as any subfolders to ~/Pictures/$today and prevent any overwriting in the case of duplicate file names. I don't mind having 2 separate scripts to handle the pictures in the 2 different directories, but I do need it to be able to get all files in subdirectories of the starting point. it also needs to be able to handle a variety of file extensions. my current solution adds a numbered extension such as .~1~ after the files normal extension .jpg, .png, .tiff, etc. I dont lose any files this way but any that wind up with a backup number after the extension are rendered useless to my project. This is what I am currently using
TODAY=$(date +"%m-%d-%Y")
mkdir -p ~/Pictures/$TODAY &&
sudo find /attachments -type f -exec mv --backup=numbered -t ~/Pictures/$TODAY {} +
My result if there are duplicate file names looks like this:
DSC07286.JPG
DSC07286.JPG.~1~
Is there a better approach than what i am doing? Is there a way to dissect the filename parts and reorganize them and do it recursively for all files in the directory? Thanks

Something like this should do it (untested; uses standard lowercase variable names and puts the index just before the extension to not mess with sorting):
for path in ~/Pictures/"$today"/*.JPG
do
index=0
for duplicate_path in "$path".~[0-9]*
do
new_path="${duplicate_path%%.*}${index}.JPG"
echo "$duplicate_path" "$new_path"
((++index))
done
done
When you're confident it's doing the right thing, simply replace echo with mv to actually move the files.

Here is my solution.
#!/bin/bash
TODAY=$(date +"%m-%d-%Y")
NOW=$(date +"%D %T")
sudo mkdir -p /home/pi/Pictures/emailpics/$TODAY &&
sudo find /attachments -type f -exec mv --backup=numbered -t /home/pi/Pictures/emailpics/$TODAY/ {} + &&
for f in /home/pi/Pictures/emailpics/$TODAY/*.~?~
do
fullfilename=$f
filepath=$(dirname "$fullfilename")
filename=$(basename "$fullfilename")
fname="${filename%.*}"
bkpnum="${filename##*.}"
file="${fname%.*}"
ext="${fname##*.}"
sudo mv $f $filepath/$file$bkpnum.$ext
done
Can't say i fully understand all the syntax for the parsing bits, but it works. maybe someone else can explain what is going on.

Related

Deleting backup appendix form file name in multiple files (Linux)

I recently went from windows to Linux (Debian based) and I imported all my files from a backup drive. The problem is that now all the files are named with the original name + an appendix with the date of the backup inside a pair of brackets.
Example:
index (2017_12_01 15_56_03 UTC).html
Instead of index.html
How do I delete from all the files the informations added by the backup?
I am searching for a function that could go trough all the files in a directory and delete the brackets part.
Or is there a way I can reimport without it?
The shell solution, assuming everything after the first ( should be replaced with .html, is
for f in *.html; do
mv "$f" "${f%%\(*}.html"
done
which is easily extended to work recursively:
find . -name '*.html' |
while read f; do
mv "$f" "${f%%\(*}.html"
done

Create directory based on date/time and copy files to it?

I'm attempting to create a script that will create a folder based on the current time and date. I then need the script to copy the files from a source folder to the newly created folder. I then need it to copy folders from a second source folder to the original source folder, overwriting everything that's in there.
Below is what I've tried, and it's failing in quite an epic fashion.
#!/bin/bash
d="/home/$(date +%d-%m-%y")"
mkdir "$d"
cp /home/test "$d"
cp /home/test2 /home/test
I'm aware that I don't have to define the variable, as the time between copies should be seconds and not lapse a day, but I wanted to make sure and honestly, I'm interested in learning to use variables in scripting.
There is one too many double quote here:
d="/home/$(date +%d-%m-%y")"
Actually no quoting is necessary here at all, write like this:
d=/home/$(date +%d-%m-%y)
In the rest of the script, if you want to copy directories, you will need to use cp -r instead of simply cp.
Finally, note that when you do cp -r dir1 dir2 when dir2 already exists, then dir1 will be copied inside dir2, rather than overwriting its content. That is, it will create dir2/dir1. If dir1 doesn't contain hidden files, then you can write like this to overwrite the content of dir2:
cp -r dir1/* dir2/

Bash script to delete a file in all sub directories.

I have a directory that is filled with subdirectories exceeding 450 GBs. Inside of these subdirectories is an instruction file in each subdirectory. I have a script that copies the instruction file in the directory I am currently in and puts it inside every subdirectory via:
#!/bin/bash
for d in */; do cp "INSTALLATION INSTRUCTIONS.rtf" "$d"; done
I need to remove all of these files in the subdirectories and replace them with new instructions. Can I simple write another script that does this:
#!/bin/bash
for d in */; do rm "INSTALLATION INSTRUCTIONS.rtf" "$d"; done
I am very hesitant and wanted to make sue as these files are vitally important and I don't want to accidentally remove anything and making a backup of 450+ GBs is very taxing.
find . -mindepth 2 -name "INSTALLATION INSTRUCTIONS.rtf" -exec rm -f '{}' +
Since this is "vitally important" data, I would first list all files that match the file name you want to delete/overwrite, without taking any action on it (other than listing):
find /folder/ -type f -name "INSTALLATION INSTRUCTIONS.rtf" -print > /tmp/holder
That would create a list of matches on /tmp/holder. Then you could analyze this list before taking any action (either visually or programatically) to make sure that the list does not include anything you don't want to delete (when dealing with big amounts of data, strange things can happen, so be proactive on protecting the data).
If you are happy with what the list shows, then you could delete the old instructions, or if possible, overwrite them with the new file. Here's an example to overwrite the old file with the new one:
while read -r line; do cp --no-preserve=all /folder/newfile "$line"; done < /tmp/holder
The cp --no-preserve=all command (available on GNU bash) would ensure that the new file has permissions that are "adequate" to the folder where they are located. You may change that to a simple cp if you don't want that to happen.

How to create empty txt files in a directory reflecting files in another directory?

I need to do some testing and need the same file names as I have in directory /home/recordings in /home/testing folder.
For example, if i have a file recording01.mp4 in /home/recordings, i would want to have the an empty file recording01.txt or recording01.mp4 or in /home/testing
I understand I can use the following command?
for i in /home/recordings/*; do touch "$i"; done
Not sure how to specify extension or the destination directory in this case?
A simple addition of /home/testing/ to touch command will do it.
for i in /home/recordings/*; do
temp=`echo $i|cut -f3 -d'/'`
cd /home/testing/
touch "$temp";
cd ../..
done
I assume you are not in home directory and running this script file from anywhere else.
You can also do this without a loop
find /home/recordings/ -type f -printf /home/testing/%f'\n' | xargs -n1 touch
Try this:
for i in /home/recordings/*; do touch "/home/testing/$i"; done
You need only specify absolute paths and things will work fine. A bunch of 0-length files are created, their names corresponding to those in /home/recordings.

Packet jpeg optimization in linux

I have many JPEG images on my server and they added evry day. I need optimize this images.
To optimize it I have use next command
find . -iname "*.jpg" -exec jpegoptim -m85 --strip-all {} \;
But find command finds all images, not only new! I know, that I may specify -ctime and -mtime params, but when jpegoptim optimizes image, image creation time changes to now! Therefore I can not specify last mod time for find command.
I think, that solution is save already processed files names in text file and when find command runs again exclude already processed file.
How can I do this? How to add finded file name in text file, and how check is file name in text file in next path?
You could use inotifywait from the inotify-tools (or some other inotify frontend) to continuously monitor the folder which contains your jpegs and have them converted on the fly, as soon as they are uploaded.
$ inotifywait -mrq --format '%w%f' -e create . | while read file; do
[ "${file##*.}" = "jpg" ] || continue;
[ -f "${file}" ] || continue;
echo "jpegoptim -m85 --strip-all '$file'";
done
This watches for all file creations in the current folder (-e create .) recursively (-r) and you have to check for the file extension (.jpg) yourself as shown, which means that in case there will be lots of non-jpeg creations in the same folder this is an unnecessary overhead. If this is a problem for you, you could use the --exclude argument and specify a negated regex to filter out files that do not match the desired extension (which is cumbersome and does not look very nice and straight-forward) or you can checkout the current version from the git repo yourself which provides an --include filter (v3.14 does not have it) and have inotifywait only report files already matching your criteria.
Also note that this will silently skip files with newlines in their filenames.

Resources