archive_fileJuly 31, 2016
Save Copies of Files in an Archive
Introduction
This is my oldest script that I still use almost every day. It came into being in the late '80s. It was first written in the Bourne shell, then the Korn shell, and finally with Bash. It is a short simple script and it is usually the first script I upload to a Linux/Unix server after I setup my account. The script simply copies a file to an archive directory and notes the file's complete path, the date, the user, and a comment read from the user as to why the file was archived. Usually my comments are like:
Before changing something to correct a bug or add a new feature.
This script has saved my butt several times throughout the years. It is easy to save a version of a file before changing it without cluttering up the file's current directory with all sorts of backup copies. It is great for saving configuration files before changing them.
The convenience, and therefore the usefulness, of archive_file
is greatly increased by:
- enabling the invocation of
archive_file
with a single letter (a
), and - setting a one-character environment variable (
$a
) to the archive directory path.
Shared Archives
The archive_file
script was originally written as a personal tool saving backup files
in a personal archive directory. The script has been enhanced so that multiple users in the same
security group can share the same archive. Simply create a directory in /var/archive
with
the same name as the user's group name and archive_file
will automatically start using
that archive directory to save the backup files.
Tricks
I am a long time vi
user and usually find myself archiving my files just before I change something.
Within vi
, the characters :!
execute an external command (like archive_file) and
vi
substitutes the current file name for the %
character. Together with the above
recommendations of invoking archive_file
with just the a
command and assigning the
archive directory path to the $a
environment variable, the following tricks are available.
To archive the file currently being edited:
:!a %
To view the previous version of the current file:
:n $a/%
To view the fourth version of the current file archived:
:n $a/%.004
To find the differences between the current file and the previously archived version:
:!diff % $a/%
To view the log of archive files:
:!a -l | less
Script
#!/bin/bash # Archive/Backup file(s) # # This program copies the named file(s) to an archive directory. Version numbers # are appended to archived files, beginning with version 000. The lastest # version of a file is linked to a file with its same name -- the name without # the version number. # # A log file named LOG is kept of all the files archived. It has the file's original path, # the date it was archived, and it has a comment that is asked for from the user if it # is not specified on the command line. # # This version tries to save files in a common archive shared by a login group # located in /var/archive. If the directory /var/archive/GROUP_NAME does not exist, # the archive is located in the user's home directory. # # Copyright 1989-2019, Mack Pexton (mack@mackpexton.com) # License: https://opensource.org/licenses/mit-license.php USAGE="Usage: ${0##*/} [ -a ] [ -l ] [ -ccomment ] [ file_name ... ]" GROUP_ARCHIVE=/var/archive/$(id -gn) # group archive directory path USER_ARCHIVE=~/archive # personal archive directory path if [ -d "$GROUP_ARCHIVE" ] then archive_dir="$GROUP_ARCHIVE" else archive_dir="$USER_ARCHIVE" fi log="$archive_dir/LOG" user="${REMOTE_USER:-$(id -un)}" # used to brand comments in log file if [ ! -d "$archive_dir" ] then # Initialize archive. mkdir -m771 "$archive_dir" || exit 1 > "$log" chmod 666 "$log" fi for arg do case "$arg" in -a) echo "$archive_dir" ;; -c*) comment=${arg#-c} ;; # like: -c"This is a Comment" -l) cat "$log" ;; -*) echo "$USAGE" 1>&2; exit 1 ;; *) # Argument is a file name. if [ -d "$arg" ] then # Do nothing for directories echo "$arg is a directory. Only regular files can be archived." elif [ -e "$arg" ] then # Get user comment to record in log file. if [ -z "$comment" ] then echo -n "Comment: " read comment fi file_name="${arg##*/}" # Determine source file's absolute path. src="$arg"; if [ ${arg:0:1} != '/' ] then src="$(pwd)/$src" fi ( cd "${archive_dir}" # Find next available version number for archive file. # 0 fill the number to 3 places. typeset v=0 while vv=$(echo $v | sed -e 's/^/000/' -e 's/.*\(...\)$/\1/') test -a "${file_name}.${vv}" do v=$[ v+1 ] done archive_file="${file_name}.${vv}" # Copy file to archive file. cp --preserve -- "$src" "${archive_file}" # Link last archive version to archive file name (w/o version). if [ -a "${file_name}" ] then rm -f -- "${file_name}" fi ln -s -- "${archive_file}" "${file_name}" # Make archive file readonly. chmod a-w -- "${archive_file}" # Write archive details to log file. echo >> $log \ "$archive_file $(date +"%m/%d/%y %H:%M") $user $src $comment" ) else echo "File $arg not found." 1>&2 fi ;; esac done
Installation
Copy the script to a directory in your $PATH. You can copy it to your personal bin directory ~/bin
but
I usually copy it to /usr/local/bin
so that it is available to all my login accounts.
As user root:
# cd /usr/local/bin # cp /PATH/TO/DOWNLOADED_SCRIPT_FILE archive_file # chmod a+x archive_file
Create a one-character synonym for archive_file.
# ln -s archive_file a
Automatically set environment variable $a when logging in.
echo 'export a=$(a -a)' >> ~/.bash_profile