fangxing fangxing - 4 months ago 9
Linux Question

How to have bash script's log file be auto-created after removal

I have a bash script file date.sh:

#!/bin/bash
while true
do
sleep 1
echo "date------ "$(date)
done


I run it

$ ./date.sh >> date.log 2>&1 &


I can see a date.log in there and be updating, but after I removed it, it won't be auto created, even I manually recreated it, the file won't update , I want date.log be auto created and update after it be removed.

Answer

In this code, date.log is opened just once:

./date.sh >> date.log 2>&1 &

If you want date.log to recreate itself if missing, you need to re-open each time that you write to it:

#!/bin/sh
while true; do
  sleep 1
  echo "date------ $(date)" >>date.log
done

Because the redirection >>date.log is inside the loop, the file is opened (and closed) with each loop. That is what is needed to re-create the file.

You can then run it:

./date.sh &

Now, if you delete or rename date.log, a new file called date.log will be created and written to.

Note that re-opening and re-closing the file with each loop is less efficient. Unless you want the re-create-itself feature, it is faster to open and close just once.

Example

This shows that we can delete date.log while the script is running in the background and the file will soon be recreated and appended to:

$ ./date.sh &
[1] 15678
$ cat date.log
date------ Sat Jul 30 00:51:28 PDT 2016
date------ Sat Jul 30 00:51:29 PDT 2016
date------ Sat Jul 30 00:51:30 PDT 2016
date------ Sat Jul 30 00:51:31 PDT 2016
$ rm -f date.log
$ cat date.log
date------ Sat Jul 30 00:51:38 PDT 2016
date------ Sat Jul 30 00:51:39 PDT 2016
date------ Sat Jul 30 00:51:40 PDT 2016
date------ Sat Jul 30 00:51:41 PDT 2016

What if date.sh cannot be modified

Suppose that date.sh is owned by others and we cannot modify it. In that case:

./date.sh | awk -v f=date.log '{print>>f; close(f)}' &

awk loops through each line of input and, for each line, it opens date.log, appends to it, and closes it.

Alternatively, if for some reason we wanted to stick with pure shell:

./date.sh | while IFS= read -r line; do printf "%s\n" "$line" >>date.log; done &