How can I delete a file only if it exists? - shell

I have a shell script and I want to add a line or two where it would remove a log file only if it exists. Currently my script simply does:
rm filename.log
However if the filename doesn't exist I get a message saying filename.log does not exist, cannot remove. This makes sense but I don't want to keep seeing that every time I run the script. Is there a smarter way with an IF statement I can get this done?

Pass the -f argument to rm, which will cause it to treat the situation where the named file does not exist as success, and will suppress any error message in that case:
rm -f -- filename.log
What you literally asked for would be more like:
[ -e filename.log ] && rm -- filename.log
but it's more to type and adds extra failure modes. (If something else deleted the file after [ tests for it but before rm deletes it, then you're back at having a failure again).
As an aside, the --s cause the filename to be treated as literal even if it starts with a leading dash; you should use these habitually if your names are coming from variables or otherwise not strictly controlled.

Touch the file first, which will create it if it's not present, but only change the timestamp if it is present.
touch filename && rm filename
Less efficient, but easy to remember.

if [ ! -f 'absolute path of file' ]
then
echo "File does not exist. Skipping..."
else
rm 'absolute path of file'
fi
If you use the following then it should work.

You can use
rm -rf my/dir || true

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.

rm -f won't handle all arguments in bash script

Got a bit of a peculiar issue in bash. I want to author a script that takes a prefix of a file name and deletes all files that begin with that prefix and end in some given suffixes. To do this, I have the following line of code:
#!/bin/bash
if [ $# -lt 1 ]; then
echo "Please provide an argument";
exit 1
fi
# Ending dot already included in $1
rm -f $1"aux" $1"bbl" $1"blg" $1"synctex.gz" $1"log"
# But even if you call it wrong...
rm -f $1".aux" $1".bbl" $1".blg" $1".synctex.gz" $1 ".log"
exit 0
Unfortunately, when I call my script (which is called cleanlatex), as intended:
cmd$ cleanlatex lpa_aaai.
only the .aux file is deleted. Clearly rm -f doesn't expand its application to all arguments, which is what would've been done if I explicitly ran rm -f lpa_aaai.aux lpa_aaai.bbl .... What am I doing wrong here?
Edit: To answer #Etan's question, this is what I see when I add those commands:
+ '[' 1 -lt 1 ']'
+ rm -v lpa_aaai.aux lpa_aaai.bbl lpa_aaai.blg lpa_aaai.synctex.gz lpa_aaai.log
removed ‘lpa_aaai.aux’
removed ‘lpa_aaai.bbl’
removed ‘lpa_aaai.blg’
removed ‘lpa_aaai.synctex.gz’
removed ‘lpa_aaai.log’
+ rm -v lpa_aaai..aux lpa_aaai..bbl lpa_aaai..blg lpa_aaai..synctex.gz lpa_aaai. .log
rm: cannot remove ‘lpa_aaai..aux’: No such file or directory
rm: cannot remove ‘lpa_aaai..bbl’: No such file or directory
rm: cannot remove ‘lpa_aaai..blg’: No such file or directory
rm: cannot remove ‘lpa_aaai..synctex.gz’: No such file or directory
rm: cannot remove ‘lpa_aaai.’: No such file or directory
rm: cannot remove ‘.log’: No such file or directory
The second set, consisting of the cannot remove messages, does not concern me: I only have those removals as a fail-safe anyway. This did exactly what I needed. Thank you.
It's not clear what your problem actually is; however, you can reduce your script to two lines of bash:
: ${1?Please provide an argument}
rm -f "$1".{aux,bbl,log,synctex.gz,log}
The first line has the same effect as your if statement, using standard POSIX parameter expansion.
The second line properly quotes $1 and uses brace expansion to produce the sequence of file names with the desired list of suffixes. It makes the simplifying assumption that the user typed the basename of the files without the trailing period: who would type foo. when foo would suffice? You're already making the assumption that there are not files named (for instance) foo.aux and foo..aux, so you might as well make an assumption that makes for less work.
I removed exit 0 because either rm succeeds and your script will exit with status 0 anyway, or rm fails, in which case you shouldn't be exiting with 0 status, but whatever status rm exits with.

shell script to remove a file if it already exist

I am working on some stuff where I am storing data in a file.
But each time I run the script it gets appended to the previous file.
I want help on how I can remove the file if it already exists.
Don't bother checking if the file exists, just try to remove it.
rm -f /p/a/t/h
# or
rm /p/a/t/h 2> /dev/null
Note that the second command will fail (return a non-zero exit status) if the file did not exist, but the first will succeed owing to the -f (short for --force) option. Depending on the situation, this may be an important detail.
But more likely, if you are appending to the file it is because your script is using >> to redirect something into the file. Just replace >> with >. It's hard to say since you've provided no code.
Note that you can do something like test -f /p/a/t/h && rm /p/a/t/h, but doing so is completely pointless. It is quite possible that the test will return true but the /p/a/t/h will fail to exist before you try to remove it, or worse the test will fail and the /p/a/t/h will be created before you execute the next command which expects it to not exist. Attempting this is a classic race condition. Don't do it.
Another one line command I used is:
[ -e file ] && rm file
You can use this:
#!/bin/bash
file="file_you_want_to_delete"
if [ -f "$file" ] ; then
rm "$file"
fi
If you want to ignore the step to check if file exists or not, then you can use a fairly easy command, which will delete the file if exists and does not throw an error if it is non-existing.
rm -f xyz.csv
A one liner shell script to remove a file if it already exist (based on Jindra Helcl's answer):
[ -f file ] && rm file
or with a variable:
#!/bin/bash
file="/path/to/file.ext"
[ -f $file ] && rm $file
Something like this would work
#!/bin/sh
if [ -fe FILE ]
then
rm FILE
fi
-f checks if it's a regular file
-e checks if the file exist
Introduction to if for more information
EDIT : -e used with -f is redundant, fo using -f alone should work too
if [ $( ls <file> ) ]; then rm <file>; fi
Also, if you redirect your output with > instead of >> it will overwrite the previous file
So in my case I wanted to remove a FIFO file before I create it again, so this worked for me:
#!/bin/bash
file="/tmp/test"
rm -rf $file | true
mkfifo $file
| true will continue the script even if file is not found.

keep renaming until file doesnt exist in directory

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

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