keep renaming until file doesnt exist in directory - bash

I need to check if a file exists in a directory, if it does rename it by adding an extra extension like original.txt to original.txt.txt. BUT check whether the renamed file still exist.
My code only changes it one time and is not checking the rest of the contents before it renames it. It just overwrites all my original.txt.txt
Question:
How to check all files and add .txt as many times as needed so it doesn't conflict with any other name in directory.
if [ -e "$destination_folder/$basename_file" ]
then
cp "$file_name" "$destination_folder/$basename_file".txt
fi

If I understand you correctly, you want to rename a file only if there is no other file of the new name. This you want to prevent accidental overwriting of this other file.
To achieve this you can use mv (which I assume you use for renaming) with the option -i so it will ask before overwriting anything. In case you are quite sure you do not want to rename anything then, you can pipe yes n into the mv command:
yes n | mv -i "$old" "$new"
To prevent seeing ugly automatically answered questions, you can redirect the stderr of the commands to /dev/null:
(yes n | mv -i "$old" "$new") 2> /dev/null
This will now rename the file only if the new name is free. Otherwise it does nothing.
EDIT:
You can use a loop to find a "free" name:
name=$original
while [ -e "$name" ]
do
name="$name.txt"
done
mv "$original" "$name"
This way you will find a free name and then use it.
Be aware that this can still overwrite a just recently created file of the new name. Unix systems are multitasking and another process could create a file of the new name just after the loop and before the mv command.
To avoid this pathological case (in case you cannot be sure that this won't happen), you should use my method above to move only if nothing would be overwritten, and afterwards you should check if the original file (with the original file name) still exists, and if it still exists, try the whole thing again:
new=$old.txt
while true
do
(yes n | mv -i "$old" "$new") 2> /dev/null
if [ -e "$old" ]
then
new=$new.txt
else
break
fi
done

Related

How to check if a file exists or not and create/delete if does/does not exist in shell

In shell, I want to check if a file exists or not then create if it doesn't exist or delete if it exists. For this I need a one liner and am trying to do something like:
ls | awk '\filename\' <if exist delete else create>
I need the ls as my problem has some command that outputs a list of strings that need to be pipelined to awk then possibly touch/mkdir.
#!/bin/bash
if [ -z "$1" ] || [ ! -f "$1" ] # $1 is input filename and -f check if $1 is a regular file
then
rm "$1" #delete the file
else
touch "$1" #create the file
fi
save the file as filecreator.sh
change the permission to allow execution with sudo chmod a+rx
while running the script use ./filecreator.sh yourfile.extension
You can see the file in your directory.
Using oc projects and oc new-project instad of ls and touch as indicated in a comment.
oc projects |
while read -r proj; do
if [ -d "$proj" ]; then
rm -rf "$proj"
else
oc new-project "$proj"
fi
done
I don't think there is a useful way to write this as a one-liner. If you like, you can replace the newlines with semicolons, except after then and else.
You really should put your actual requirements in the question itself. ls is a superbly useless example because it cannot list a file which doesn't already exist, and you should not use ls in scripts at all.
rm yourfile 2>/dev/null || touch yourfile
If the file existed before, rm will succeed and erase the file, and the touch won't be executed. You end up with no file afterwards.
If the file did not exist before, rm will fail (but the error message is not visible, since it is directed to the bitbucket), and due to the non-zero exit code of rm, the touch will be executed. You end up with an empty file afterwards.
Caveat: If the file exists, but you don't have permissions to remove it, you won't notice this error, due to the redirection of stderr. Hence, for debugging and later diagnosis, it might be better to redirect stderr to some file instead.

Checkin if a Variable File is in another directory

I'm looking to check if a variable file is in another directory, and if it is, stop the script from running any farther. So far I have this:
#! /bin/bash
for file in /directory/of/variable/file/*.cp;
do
test -f /directory/to/be/checked/$file;
echo $?
done
I ran an echo of $file and see that it includes the full path, which would explain why my test doesn't see the file, but I am at a loss for how to move forward so that I can check.
Any help would be greatly appreciated!
Thanks
I think you want
#! /bin/bash
for file in /directory/of/variable/file/*.cp ; do
newFile="${file##*/}"
if test -f /directory/to/be/checked/"$newFile" ; then
echo "/directory/to/be/checked/$newFile already exists, updating ..."
else
echo "/directory/to/be/checked/$newFile not found, copying ..."
fi
cp -i "$file" /directory/to/be/checked/"$newFile"
done
Note that you can replace cp -i with mv -i and move the file, leaving no file left behind in /directory/of/variable/file/.
The -i option means interrogate (I think), meaning if the file is already there, it will ask you overwrite /directory/to/be/checked/"$newFile" (or similar) to which you must reply y. This will only happen if the file already exists in the new location.
IHTH
The command basename will give you just the file (or directory) without the rest of the path.
#! /bin/bash
for file in /directory/of/variable/file/*.cp;
do
test -f /directory/to/be/checked/$(basename $file);
echo $?
done

Bash If then that reads a list in a file condition

Here is the condition:
I have a file with all packages installed.
I have a folder with all kinds of other packages, but they include all of the ones in the list, plus more.
I need a bash script that will read the file and check a folder for packages that don't exist in the list then remove them, they are not needed, but keep the packages that are on the list in that folder.
Or perhaps the bash should read folder then if packages in the folder aren't on the list them rm -f that or those packages.
I am familiar with writing if then conditional statements, I just don't know how to do if making the items in the list a variable or variables (in a loop).
thanks!
I would move the packages on the list to a new folder, delete the original folder, and move the temporary folder back:
DIR=directory-name
mkdir "$DIR-tmp"
while read pkgname; do
if [[ -f "$DIR/$pkgname" ]]; then
mv "$DIR/$pkgname" "$DIR-tmp"
fi
done < package-list.txt
# Confirm $DIR-tmp has the files you want first!
rm -rf "$DIR"
mv "$DIR-tmp" "$DIR"
I think you want something like this:
for file in $(ls folder) ; do
grep -E "$file" install-list-file >/dev/null || \
echo $file
done > rm-list
vi rm-list # view file to ensure correct
rm $(<rm_list)
There are ways to make this faster (using parameter substitution to avoid fork/exec's), but I recommend avoiding fancy shell stuff [${file##*/}] until you've got the basics down. Also, this script basically translates the description into a script and is not intended to be much more than a guide on how to approach the problem.

How to write an idempotent file update in a Bash script?

I am writing a bash script that will update a configuration file. Right now it simply moves the file into place:
if [ ! -e /path/to/file.conf ]; then
mv file.conf /path/to/file.conf
fi
However, now I realize that I may want to make changes to that file in the future, so a simple move won't work if the file is already there. How do I write this so that the file is updated with the right contents in a way that allows me to execute the same script multiple times (idempotent)?
Consider:
if cmp -s file.conf /path/to/file.conf
then : OK - identical
else mv /path/to/file.conf /path/to/file.conf.$(date +%Y%m%d.%H%M%S)
mv file.conf /path/to/file.conf
fi
That preserves a dated copy of the previous version of the configuration file, which makes it easier to rollback if something goes wrong. There are other, arguably better, ways to handle that. The trouble is that it dates the configuration file with when it was replaced, not when it was created.
So, an alternative is:
if cmp -s file.conf /path/to/file.conf
then : OK - identical
else now=$(date +%Y%m%d.%H%M%S)
mv file.conf /path/to/file.conf.$now
rm /path/to/file.conf
ln -s /path/to/file.conf.$now /path/to/file.conf
fi
This leaves you with a symlink called /path/to/file.conf that points to a dated version of the configuration file — at the time it was created. You can remove the symlink and put in a different version at any time, or change it to point to an older version without necessarily removing the newer version, etc.

Recycle bin in bash problem

I need to make a recycle bin code using bash. Here is what I have done so far. My problem is that when I move a file with the same name into the trash folder it just overwrites the previous file. Can you give me any suggestions on how to approach this problem?
#!/bin/bash
mkdir -p "$HOME/Trash"
if [ $1 = -restore ]; then
while read file; do
mv $HOME/Trash/$2 /$file
done < try.txt
else
if [ $1 = -restoreall ]; then
mv $HOME/Trash/* /$PWD
else
if [ $1 = -empty ]; then
rm -rfv /$HOME/Trash/*
else
mv $PWD/"$1"/$HOME/Trash
echo -n "$PWD" >> /$HOME/Bash/try
fi
fi
fi
You could append the timestamp of the time of deletion to the filename in your Trash folder. Upon restore, you could strip this off again.
To add a timestamp to your file, use something like this:
DT=$(date +'%Y%m%d-%H%M%S')
mv $PWD/"$1" "/$HOME/Trash/${1}.${DT}"
This will, e.g., create a file like initrd.img-2.6.28-11-generic.20110615-140159 when moving initrd.img-2.6.28-11-generic.
To get the original filename, strip everything starting from the last dot, like with:
NAME_WITHOUT_TIMESTAMP=${file%.*-*}
The pattern is on the right side after the percentage char. (.* would also be enough to match.)
Take a look how trash-cli does it. It's written in Python and uses the same trash bin as desktop environments. Trash-cli is available at least in the big Linux distributions.
http://code.google.com/p/trash-cli/
Probably the easiest thing to do is simply add -i to the invocation of mv. That will prompt the user whether or not to replace. If you happen to have access to gnu cp (eg, on Linux), you could use cp --backup instead of mv.

Resources