I have a log file which i need to take a backup,
Then empty the file instead of deleting it,
Deleting the file will cause someother script to get triggered,
Hence i should only empty it.
Please suggest me a way?
After you've read from the file you can just overwrite the file with > filename This overwrites the file with nothing. It is also equivalent to cat /dev/null > filename.
similar solutions referenced here
To empty a file you can use truncate -s 0 filename
AFAIK there is no easy way to backup a file and empty it at the same time. I faced a similar problem and what I ended up doing is reading the original file line by line and copy them to a new file while keeping count of line numbers. Then I simply remove that number of lines from the original file. I use this to manually rotate some log files for which standard rotating approaches were not an option.
ORIGINAL_FILE="file.log"
NEW_FILE="$(date +%s).file.log"
unset n
while read line; do echo "$line" >> $NEW_FILE; : $((n++)); done < $ORIGINAL_FILE
if [[ -v n ]]; then
sed -i "1,$n d" $ORIGINAL_FILE
fi
Related
I'm sorry if anyone has already asked the question, I searched for it and have not yet found an answer.
I need to create a new text file using heredoc by only one command line.
What I have tried so far without success, something like this-
cat << "" >> newfile.txt
thanks
Triple quotes denote a herestring:
# append blank line, create if it doesn't exist
cat <<< "" >> newfile.txt
There's no reason to use the herestring. A simpler way to do it would be:
# append blank line, create if it doesn't exist
echo >> newfile.txt
You realize both of those are appending a blank line to the file? If you're just trying to create a completely empty file with size 0, do this instead:
# create empty file, truncate if it already exists
> newfile.txt
That will truncate the file if it already exists. If you just want to ensure a file exists but leave it alone if it already does:
# create empty file, do nothing if it already exists
touch newfile.txt
I have a file that contains a list of file names to be opened later.
After I load lines (file names) to variables, for a reason unknown to me I cannot open it as a file later.
Here is a simplified example of what i'm trying to do:
Main file's contents:
first_file.txt
second_file.txt
Bash commands:
read line < $main_file # file with the list, received as an argument
echo $line # to check that correct filename has been read
cat $line # attempt to dump "first_file.txt" contents <- FAILS
cat first_file.txt # read "first_file.txt" contents manually
Execution esult:
first_file.txt
: No such file or directory
*** this is 1st file's contents ***
*** ....
So, cat first_file.txt works, $line contains "first_file.txt", but cat $line fails...
I obviously misunderstand something here, suggestions are welcomed!
Edit:
As requested, here is cat -v $main_file's output:
first_file.txt^M
second_file.txt^M
third_file.txt^M
^M
^M
The ^M characters are carriage returns (a.k.a. \r) and are often part of a Windows line ending. They don't show up when you echo them, but they are messing up your ability to open a file with the text having it at the end.
The best solution is to remove them from your "main file." You could use the dos2unix tool if you have it, or you could use GNU sed like sed -i -e 's/\s+$//g' $main_file to edit it in place and remove the extra white space (which includes ^M) from the end of each line.
i was wondering weather is it possible to append data in a file without using cat command.
I've considered using sed to append data , but as of my knowledge sed only operates after loading the full data into the memory. please do correct me if i'm wrong on this.
If you want to append data to a file, you can simply use the append I/O-redirection >>. For instance:
echo "first line" > file
echo "next line" >> file
Or you could append an entire file
echo "$(<otherfile)" >> file
This command is however not advisable since it will load the entire file first into memory.
A better way is to use tee:
tee < otherfile >> file
Instead of cat, you can also use echo command to do the same.
And ofcourse, >> operator does it.
I am scripting a solution wherein, when a shared drive is removed from the server, we need to remove the entry from fstab as well.
What I have done till now :
MP="${ServerAddress}:${DirectoryPath} ${MountPoint} nfs defaults 0 0"
while read line; do
if [ "${MP}" = "${line}" ]; then
sed "/${line}/d"
fi
done < /etc/fstab
this is giving an error >> sed: 1: "/servername.net...": command I expects \ followed by text
Please suggest on this can be deleted.
PS: I am running this as a part of the script, so i dont have to run this individually. While running with the suggested options, I am able to delete.. but during the script, this does not work and gives that error. People commenting on the " or the formatting, its just that I cannot copy from there since that is a remote through terminal server.
Try the following:
sed -i.bak "\#^$SERVERADDR:$DIRPATH#d" /etc/fstab
After setting meaningful values for SERVERADDR and DIRPATH. That line will also make a backup of the old file (named fstab.bak). But since /etc/fstab is such an important file, please make sure to have more backups handy.
Let me point out that you only need that single line, no while loop, no script!
Note that the shell is case-sensitive and expects " as double quotes and not ” (a problem now fixed in the question). You need to configure your editor not to capitalize words randomly for you (also now fixed). Note that experimenting on system configuration files is A Bad Idea™. Make a copy of the file and experiment on the copy. Only when you're sure that the script works on the copy do you risk the live file. And then you make a backup of the live file before making the changes. (And avoid doing the experimenting as root if at all possible; that limits the damage you can do.)
Your script as written might as well not use sed. Change the comparison to != and simply use echo "$line" to echo the lines you want to standard output.
MP="${ServerAddress}:${DirectoryPath} ${MountPoint} nfs defaults 0 0"
while read line; do
if [ "${MP}" != "${line}" ]; then
echo "$line"
fi
done < /etc/fstab
Alternatively, you can use sed in a single command. The only trick is that the pattern probably contains slashes, so the \# notation says 'use # as the search marker'.
MP="${ServerAddress}:${DirectoryPath} ${MountPoint} nfs defaults 0 0"
sed -e "\#$MP#d" /etc/fstab
If you have GNU sed, when you're satisfied with that, you can add the -i.bak option to overwrite the /etc/fstab file (preserving a copy of the original in /etc/fstab.bak). Or use -i $(date +'%Y%m%d.%H%M%S') to get a backup extension that is the current date/time. Or use version control on the file.
What is the fastest / most elegant way to read out a file and then write the content to that same file?
On Linux, this is not always the same as 'touching' a file, e.g. if the file represents some hardware device.
One possibility that worked for me is echo $(cat $file) > $file but I wondered if this is best practice.
You cannot generically do this in one step because writing to the file can interfere with reading from it. If you try this on a regular file, it's likely the > redirection will blank the file out before anything is even read.
Your safest bet is to split it into two steps. You can hide it behind a function call if you want.
rewrite() {
local file=$1
local contents=$(< "$file")
cat <<< "$contents" > "$file"
}
...
rewrite /sys/misc/whatever
I understand that you are not interested in simple touch $file but rather a form of transformation of the file.
Since transformation of the file can cause its temporary corruption it is NOT good to make it in place while others may read it in the same moment.
Because of that temporary file is the best choice of yours, and only when you convert the file you should replace the file in a one step:
cat "$file" | process >"$file.$$"
mv "$file.$$" "$file"
This is the safest set of operations.
Of course I omitted operations like 'check if the file really exists' and so on, leaving it for you.
The sponge utility in the moreutils package allows this
~$ cat file
foo
~$ cat file | sponge file
~$ cat file
foo