Archive Recent creates compressed archives containing files that have recently changed. Archive Recent is designed to simplify the creation of incremental back ups and copies of modified files.
Archive Recent can create encrypted archives suitable for uploading off site storage and archiving services.
Using archiverecent
To get started with Miln Archive Recent, download and expand the executable file onto your computer.
Archive Recent is a single executable file. It does not need to be installed, can be run from any directory, and can be removed by deleting the file.
Let’s walk through an example use of archiverecent
. Consider this command line call:
./archiverecent -from Photos -since 2025-01-31 -output latest-photos.tgz
The flags for this call to archiverecent
are:
-from Photos
- The directory to read files from.
Photos
is the relative path to a directory. -since 2025-01-31
- The date from which to include modified files. Every file within the
Photos
directory, that has been modified on or after 31st January 2025, will be included in the archive. -output latest-photos.tgz
- The filename of the archive to create.
What Happens
So what happens when this command is issued?
archiverecent
does the following:
- An archive is created and prepared for writing to
- The contents of
Photos
are read, looking at each file’s modification date…- …if the file’s modification date is on or after
2025-01-31
, the file is added to the archive
- …if the file’s modification date is on or after
The created file is a gzipped tar archive (.tar.gz
/.tgz
). This is a widely supported file format that will be recognised and accessible for decades to come. This makes it ideal for long term storage.
If no files have been modified, the final archive will still be created but will be empty.
Archive Recent will overwrite any existing archive file with the same name.
Why I wrote this tool
I wrote Archive Recent to help me prepare incremental archives of photos for backing-up.
Each month I like to back-up my new and changed photos to a long term store on a remote server. I used to prepare an archive manually by exporting photos from an application and then running a series of command line tools to finalise the archive.
The manual process was fine for many years. Sometimes it felt a little time consuming but never a real burden. Then the photos application I used changed and obscured the file layout I had been relying on for easily extracting recently changed files.
That application’s change was the first in a series of changes that pushed me away. I exported all my photos and metadata to a network drive.
I am now manually managing my photos as “just a bunch of images” on disk. Thankfully a welcome number of photo applications work well with this approach. As a bonus my reliance on a single photo application is behind me.
Without the ability to ask an authoritative photo application for recently changed photos, incremental back-ups remained a challenge.
I searched for various approaches to solving this problem. Many good solutions exist in the form of custom shell scripts using find
and rsync
. This should have been good enough but I worried my script would be fragile.
So rather than write my own shell script, I wrote archiverecent
. The problem felt common enough that it deserved a more general tool.
Today, with archiverecent
, this is the command I am using to back up our photos:
sudo archiverecent
-from "/var/services/homes/./megan/Photos:/var/services/homes/./graham/Photos/"
-since /var/services/homes/graham/photos-since.txt
-output '/var/services/homes/graham/miln-photos-${now}-${since}-${uuid}.tgz'
-passphrase -
The flags above are:
-from <directory paths>
- Two directories containing our photos. The
/./
path segment truncates the archived directory parent path to just our names, e.g.graham/photo1.jpg
rather thanvar/services/homes/graham/Photos/photo1.jpg
. -since <file path>
- Path to a text file containing the last
since
date. -output <templated path>
- Output path with variable substitutions of the time now, since, and a unique identifier.
-passphrase -
- Encrypt the archive using a passphrase provided interactively.
This command can be called once a month or whenever we have a need to create an archive of recently added photos. The since
date is dynamic and ensures everything modified after the last call is included in the new archive.
This tool has solved my incremental back-up problem. I expect it will be useful for you too.
If you have any suggestions for improvements or need additional options, please get in touch.
Examples
Below are examples of alternative uses of Archive Recent.
Last 24 hours
Archive documents that have changed in the last 24 hours:
./archiverecent -since 24h -from ~/Documents
Multiple Sources
Archive changed files in the last 30 days, from multiple directories, into one archive:
./archiverecent -since 720h -from Photos:Documents/projects:Documents/legal
Flags
archiverecent
accepts the following command line flags:
Usage of ./archiverecent:
-config string
File path to configuration.
-dry-run
list files that would be archived but do not create the archive
-exclude string
exclude directories and files with these names (default "@eaDir")
-f string
paths to archive files from (shorthand)
-from string
paths to archive files from
-h Show this help message and exit. (shorthand)
-help
Show this help message and exit.
-l string
Directory path to licence certificate files (PEM encoded) (shorthand) (default "~/.miln/")
-legal
Show legal notices and exit.
-licence string
Directory path to licence certificate files (PEM encoded) (default "~/.miln/")
-o string
path to create archive at (shorthand) (default "${now}-archive-since-${since}-${uuid}.tgz")
-output string
path to create archive at (default "${now}-archive-since-${since}-${uuid}.tgz")
-passphrase string
passphrase source: empty for no encryption, 'env' for env ARCHIVERECENT_PASSPHRASE, '-' interactive, or a path to a text file.
-s string
date or duration since file modification to archive (shorthand)
-show-licence
Show licence details and exit.
-since string
date or duration since file modification to archive
-v Show version details and exit. (shorthand)
-version
Show version details and exit.
Qualifying as Recently Changed
A file qualifies for inclusion in the final archive, if the file’s modifed date is more recent than the since
date. If no since
date is provided, all files qualify.
Since Dates and Time Periods
The since
value can been provided in a number of formats, including a dynamic value managed by a file.
Absolute Dates
A precise date with second accuracy and time zone can be passed as an RFC 3339 encoded value, i.e. 2006-01-02T15:04:05Z07:00
.
./archiverecent -since 2012-02-07T09:00:00 -from ~/documents
A day accurate date can be passed in the unambiguous numeric format YYYY-MM-DD, i.e. 2006-01-02
.
./archiverecent -since 2023-11-01 -from ~/documents
Relative Dates
A relative time period can be passed to calculate the since
date, i.e. 60s
, 30m
, 2h45m
, 48h
. The largest unit of time is h
for hour.
./archiverecent -since 24h -from ~/projects
Dynamic Rolling Dates
Passing a file path for since
will read the file contents for the date to use and, on completion, updated with the current date. This allows Archive Recent to be called periodically without needing to update the since
value manually.
Each time the command below is called, a new archive will be created containing the files from the photos
directory that have changed since the last archive was created:
./archiverecent -since ./lastarchive.txt -from photos
The initial contents of the file must be empty, a date, or a relative time.
If the file at the file path is empty or does not exist, Archive Recent will use the default since
value. This will cause the first archive to include all existing files. The subsequent archives will be incremental.
The file path must be absolute, beginning with a file path separator (/
), or relative with an intitial period (.
). Any other format is treated as a date or duration.
Archiving Multiple Directories
Multiple directories can be passed to Archive Recent for inclusion in the archive. Any qualifying files in the directories will be included in the final archive.
Pass multiple from paths to be archived using the path separator (:
). The command below passes two directories for inclusion:
/homes/alice/photos
/homes/bob/documents
./archiverecent -from "/homes/alice/photos:/homes/bob/documents"
Pass a glob pattern as the from path:
./archiverecent -from "/homes/*/photos"
Isolating Multiple Directories
When multiple directories are used for the from
paths, there is the possibility of file name collisions. Two paths can contain files with the same name, and when stored in a single archive they would conflict. Archive Recent uses the tar archive file format. While this format allows conflicting file names, the last written file will frequently be the only file that is extracted.
To avoid this problem, Archive Recent includes the unique parent directory path when multiple from
paths are used. Consider the single directory being archived below:
./archiverecent -from /homes/alice/photos
In the example above, Alice’s photos will appear at the top of the archive:
photo1.jpg
photo2.jpg
- …
If we modify the command to add a second directory, also called photos
, the layout of the archive changes:
./archiverecent -from /homes/alice/photos:/homes/bob/photos
This time the archive contains two isolating directories before the included contents:
homes/alice/photos/photo1.jpg
homes/alice/photos/photo2.jpg
homes/bob/photos/photo1.jpg
homes/bob/photos/photo_holiday.jpg
- …
Merging and Modifying Parent Directories
Archive Recent defaults to avoiding merging the contents of multiple directories. If multiple from
directories should be merged into a single directory in the archive, this is possible. from
paths can use a special prefix directory segment (/./
) to denote where the parent directory should begin.
./archiverecent -from /homes/./alice/photos
In the example above, Alice’s photos will be embedded in the directory alice/photos/
within the archive:
alice/photos/photo1.jpg
alice/photos/photo2.jpg
- …
Had the directory segment /./
not been included, the contents of the photos
directory would have been the top most items in the archive.
The use of the prefix directory segment (/./
) disables the use expansion of glob patterns.
When passing multiple paths to from
, the prefix segment can be used to merge the contents of matching directories.
./archiverecent -from /homes/alice/./photos:/homes/bob/./photos
In the multiple path from
example above, both Alice and Bob’s photos will be combined into a single directory in the archive:
photos/photo1.jpg
photos/photo2.jpg
photos/photo1.jpg
photos/photo_holiday.jpg
- …
Note the duplicate photo1.jpg
entries in the list above. Both Alice and Bob had a recently modified file called photo1.jpg
in their respective photos
directories. Archive Recent will write both files to the archive. The tar archive format permits this. Which file is extracted depends on the tar
extraction tool used.
Encryption
Archive Recent can encrypt the archive using a cryptographically secure and well known algorithm.
Use the passphrase
flag to set the secret passphrase needed to decrypt the archive. The passphrase can be provided in a few different ways:
Pass a hypen (
-
) to be interactively prompted for the passphrase;./archiverecent -from photos -passphrase -
Pass
env
to read the passphrase from the environment variableARCHIVERECENT_PASSPHRASE
;Pass a path to a text file containing the passphrase. The entire file will be read and used as the passphrase, including any trailing white space.
If the passphrase is forgotten or lost, it can not be recovered.
The algorithm used is aes-256-cbc pbkdf2 salted sha256
. This was chosen because the resulting archive can be decrypted with the open source and widely available openssl
.
Decrypting the Archive
To decrypt an archive with openssl
, use the following flags:
openssl aes-256-cbc -pbkdf2 -salt -d -in archive.tgz.aes -out archive.tgz
Archive Recent does not support decrypting archives. This role is left to openssl
and other tools.
Archive Name
The default archive name makes use of variables to ensure it differs every time an archive is created:
${now}-archive-since-${since}-${uuid}.tgz
Variables can be used to insert dynamic elements into the archive path. Environment variables are available, in addition to the named values below:
- now
now
date asyyyy-mm-dd
, i.e.2025-01-28
- now_y
now
year asyyyy
, i.e.2025
- now_m
now
month asmm
, i.e.01
- now_Month
now
month as English string, i.e.January
- now_d
now
day of the month asdd
, i.e.28
- now_Day
now
day of the month as English string, i.e.Tuesday
- since
since
date asyyyy-mm-dd
, i.e.2025-01-28
- since_y
since
year asyyyy
, i.e.2025
- since_m
since
month asmm
, i.e.01
- since_Month
since
month as English string, i.e.January
- since_d
since
day of the month asdd
, i.e.28
- since_Day
since
day of the month as English string, i.e.Tuesday
- uid
- Current user identifer
- gid
- Current user’s primary group identifier
- username
- Current user short name
- name
- Current user name
- home
- Current user’s home directory
- uuid
- A universally unique lexicographically sortable identifier (ulid); see https://github.com/ulid/spec for the properties of this value. The uuid value is constant per run.
Excluded Files and Directories
Archive Recent does not include irregular files or empty directories. Irregular files include devices, sockets, and symbolic links. Only directories containing qualifying files are included.
Files beginning with period (.
) are excluded. This avoids including macOS’s .DS_Store
files.
Additionally, Archive Recent has the option to exclude files and directories with given names. The exclude
flag defaults to @eaDir
, which is the name of the Synology DiskStation Manager’s (DSM) supporting directories.
./archiverecent -from photos -exclude '@eaDir:Cache'
If a file matches an excluded name, the file is skipped and not be included in the archive.
If a directory matches an excluded name, the directory is skipped and the contents of the directory are also skipped.