0

I am a complete beginner with linux, and need help making a bash script to create a file with a specific date in the file name.

I have a directory called /backups/, in which I would like to create a file according to the current date and time with some prefix text, such as /backups/backup_2023_09_15_14_00_00.txt. This part is already answered here).

The catch is that I want to remove the previous backup file if one already exists (which would be named according to the format backup_****_**_**_**_**_**.txt), and if the new file creation was successful.

Is there a way to do something like this?

teeeeee
  • 157
  • 1
    Possible duplicate: https://unix.stackexchange.com/questions/101884/remove-all-files-except-for-last-file-of-each-month – Gerald Schneider Sep 15 '23 at 11:54
  • 2
    Does this answer your question? How to append date to backup filename – Panki Sep 15 '23 at 12:25
  • @Panki Partly, except that link is about how to name the file according to the date. In addition, I need to delete the old file if the creation of the new one was successful. – teeeeee Sep 15 '23 at 13:13
  • Questions are supposed to focus on a single problem. You are asking for a solution to multiple. Have you tried researching? https://stackoverflow.com/questions/638975/how-do-i-tell-if-a-file-does-not-exist-in-bash/40046642#40046642 – Panki Sep 15 '23 at 13:39

3 Answers3

0

The following bash script fragment would use the list of positional parameters (see further down for a variant that uses a named array instead) to store all the pathnames of old backups. When the new backup has been created, here simulated using touch, the remembered old backups are deleted.

# Set backup dir variable and name of the new backup file.
backup_dir=/backups
printf -v backup_name 'backup_%(%Y_%m_%d_%H_%M_%S)T.txt' -1

Remember any old backups.

shopt -s nullglob # expand globs to nothing if no match set -- "$backup_dir"/backup_????????????_??.txt

Debugging output.

if [ "$#" -gt 0 ]; then printf 'Old file: %s\n' "$@" else echo 'No old files' fi

Create the new backup at "$backup_dir/$backup_name".

Terminate if not successful.

touch "$backup_dir/$backup_name" || exit

Remove old files if there were any.

rm -f "$@"

Using a named array to hold the old backup files rather than the list of positional parameters. The code is identical, except for the assignment to oldfiles and the expansions used with rm and for producing the debugging output.

# Set backup dir variable and name of the new backup file.
backup_dir=/backups
printf -v backup_name 'backup_%(%Y_%m_%d_%H_%M_%S)T.txt' -1

Remember any old backups.

shopt -s nullglob # expand globs to nothing if no match oldfiles=( "$backup_dir"/backup_????????????_??.txt )

Debugging output.

if [ "${#oldfiles[@]}" -gt 0 ]; then printf 'Old file: %s\n' "${oldfiles[@]}" else echo 'No old files' fi

Create the new backup at "$backup_dir/$backup_name".

Terminate if not successful.

touch "$backup_dir/$backup_name" || exit

Remove old files if there were any.

rm -f "${oldfiles[@]}"

Instead of terminating the script if the new backup isn't created successfully, we could instead do the removal of the old files in an if statement, for example,

# Create the new backup.
# Remove old files if successful.
if touch "$backup_dir/$backup_file"
then
    rm -f "$@"    # or "${oldfiles[@]}" if you used the array and like typing longer things
fi
Kusalananda
  • 333,661
  • Thanks for your answer. I think I have almost followed it properly. Two things: 1) in the second version, with the named array, does the "rm" command at the end somehow cycle through all items in the array? 2) Could you add some code after the creation of the "oldfiles" variable, which echoes each file name to the terminal output (just so I can check it picks them all up correctly for debugging)? – teeeeee Sep 15 '23 at 14:23
  • @teeeeee The expansion "${oldfiles[@]}" expands to the list of names that the pattern matched. I will add an output statement. – Kusalananda Sep 15 '23 at 14:24
  • 1
    @teeeeee Yes, you failed to copy the shopt line. – Kusalananda Sep 15 '23 at 14:41
  • Awesome, it works beautifully! Thanks so much for your time. – teeeeee Sep 15 '23 at 15:27
-1

If you only want a single backup file then its easy - pseudo code looks like this:

Capture list of existing backup files
create new backup file
if new filecreated OK, delete files in list 

If create new backup file is simply a matter of copying a single small file, then this is probably overkill - its unlikely to fail. In such a case, just delete the existing files before creating the new one. But creating a large tar/cpio archive or offloading to back server is something else.

But that's a bit boring. What about maintaining multiple backup files? If you know they will be created on a regular basis then you could do this:

find $BACKUPDIR -maxdepth 1 -mtime +7 -exec rm -f {} \;

which would keep the files created (or modified) in the last 7 days.

Or if you wanted to keep a number of versions (12 in below)....

ls -1 $BACKUPDIR/backup | sort | awk -v KEEP=12 '(NR>KEEP) { print $1 }' | xargs rm -f
symcbean
  • 5,540
  • In your step 1), how do you capture the list of existing backup files? Remember, backup files are only those in the format "backup_**_**__**.ext". I don't want to delete everything in the directory - only those which are backup files, based on the criterion above. – teeeeee Sep 15 '23 at 14:01
  • FILES2DEL="$( ls -1 $BACKUPDIR/backup_????_??_??_??_??_??.txt)" (i.e. same as @Kusalananda) – symcbean Sep 15 '23 at 14:05
  • 1
    This answer says it's bad to parse the output of "ls" for getting the list? https://unix.stackexchange.com/a/191650/413987 – teeeeee Sep 15 '23 at 14:07
  • Read it again - it causes problems if the filenames contain spaces. You already told us they did not. – symcbean Sep 15 '23 at 14:08
  • Yep, you're right :) – teeeeee Sep 15 '23 at 14:10
  • The filenames may not contain spaces, but someone is bound to come around and change the top-level backup directory path to ~/Library/Application Support Files or whatever. Remember that we're not writing exclusively for the person that asked the question. – Kusalananda Sep 15 '23 at 14:13
-2

You can do like this:

# Save the list of files in the backup folder
files=$(ls /backups/backup_[0-9][0-9][0-9][0-9]_[0-9][0-9]_[0-9][0-9]_[0-9][0-9]_[0-9][0-9]_[0-9][0-9].txt)

[Do your backup here, exit if it fails]

Delete the files previously in the backup folder

for file in $files do rm -f "${file}" done