shell script to remove a file if it already exist - shell

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.

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.

Replace file only if not being accessed in bash

My requirement is to replace file only when it is not being accessed. I have following snippet:
if [ -f file ]
then
while true
do
if [ -n "$(fuser "file")" ]
then
echo "file is in use..."
else
echo "file is free..."
break
fi
done
fi
{
flock -x 3
mv newfile file
} 3>file
But I have a doubt that I am not handling concurrency properly. Please give some insights and possible way to achieve this.
Thanks.
My requirement is to replace file only when it is not being accessed.
Getting requirements right can be hard. In case your actual requirement is the following, you can boil down the whole script to just one command.
My guess on the actual requirement (not as strict as the original):
Replace file without disturbing any programs reading/writing file.
If this is the case, you can use a very neat behavior: In Unix-like systems file descriptors always point to the file (not path) for which they where opened. You can move or even delete the corresponding path. See also How do the UNIX commands mv and rm work with open files?.
Example:
Open a terminal and enter
i=1; while true; do echo $((i++)); sleep 1; done > file &
tail -f file
The first command writes output to file and runs in the background. The second command reads the file and continues to print its changing content.
Open another terminal and move or delete file, for instance with
mv file file2
echo overwritten > otherFile
mv otherFile file2
rm file2
echo overwritten > file
echo overwritten > file2
While executing these commands have a look at the output of tail -f in the first terminal – it won't be affected by any of these commands. You will never see overwritten.
Solution For New Requirement:
Because of this behavior you can replace the whole script with just one mv command:
mv newfile file
Consider lsof.
mvWhenClear() {
while [[ -f "$1" ]] && lsof "$1"
do sleep $delay
done
mv "$1" "$2" # still allows race condition
}

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

How can I delete a file only if it exists?

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

What is the most concise way to source a file (only if it exists) in Bash?

In Bash scripting, is there a single statement alternative for this?
if [ -f /path/to/some/file ]; then
source /path/to/some/file
fi
The most important thing is that the filename is there only once, without making it a variable (which adds even more lines).
For example, in PHP you could do it like this
#include("/path/to/some/file"); // # makes it ignore errors
Is defining your own version of #include an option?
include () {
[[ -f "$1" ]] && source "$1"
}
include FILE
If you're concerned about a one-liner without repeating the filename, perhaps:
FILE=/path/to/some/file && test -f $FILE && source $FILE
If you are concerned about warning (and lack of existence of sourced file isn't critical for your script) just get rid of the warning:
source FILE 2> /dev/null
You could try
test -f $FILE && source $FILE
If test returns false, the second part of && is not evaluated
This is the shortest I could get (filename plus 20 characters):
F=/path/to/some/file;[ -f $F ] && . $F
It's equivalent to:
F=/path/to/some/file
test -f $F && source $F
To improve readability, I prefer this form:
FILE=/path/to/some/file ; [ -f $FILE ] && . $FILE
If you want to always get a clean exit code, and continue no matter what, then you can do:
source ~/.bashrc || true && echo 'is always executed!'
And if you also want to get rid of the error message then:
source ~/.bashrc 2> /dev/null || true && echo 'is always executed!'
If you are not concerned with the output of the script, you could just redirect standard error to /dev/null with something like this:
$ source FILE 2> /dev/null
[I can post but I can't comment because I don't have enough reputation, so this is an expansion of the answer way up there. If I could comment, I wouldn't be posting what looks like a duplicate response. Please re-read what I just said before getting all pedantic and complaining that it's a duplicate.]
include () { test -f "$1" && . "$#"; }

Resources