Copy Filters: Subfolders + Latest Files

I recently learned a really useful technique in Directory Opus that has already made a big difference for me. It helps with backing up files, moving them, and keeping multiple computers in different locations up to date. The method I’m using comes from the discussion in this post.

I’m using this together with Amazon S3 to maintain online mirrors of my physical drives. In the post I referenced, I realized I had been making a mistake by leaving the Copy Filter enabled. Ironically, now that I understand it better, I can see how powerful that filter actually is.

I have two questions about the Copy Filter:

  1. Is it possible to apply a filter only to certain subdirectories during a copy operation?
  2. I see how to filter files by date, but is there a way to tell the filter to select only the last three or four created files within a specific folder?

Thanks

I think this filter definition may be possible.
A modified filename or a file copied to a sub-directory does change the folder modified date.
Perhaps not the last three or four created files, but maybe within a time range.

I know nothing about Amazon S3.
I'm still sftp to linux with my personal .camp domain name that I want to keep.

I'll think on this some, but my answer may not be the best solution.
The idea is to mirror the directory structure ?
Yeah, that's the problem.
I'll try !

Hi, and thanks for the reply.

A modified filename or a file copied to a sub-directory does change the folder modified date.

That’s not the idea I was thinking. I think we are talking about two different ideas, so let me clarify what I’m trying to do.

Filtering only certain subfolders
For example I want to move Temp.

Temp
 ├─ Folder1
 ├─ Folder2
 └─ Folder3

but I only want the filter to apply to specific subfolders—namely Folder2 and Folder3. As for Folder1 the files inside should not be affected by the filter at all. What I’m trying to figure out is whether a filter can be restricted so it only applies to certain directories during the copy/move.

Selecting only the last few created files
The second thing I’m trying to understand is whether there’s a way to build a filter that selects only the last 3–4 created files in a folder. I know I can filter by date ranges, but in some folders I don’t track dates closely. In those cases, the only relevant files are simply the most recently created ones, and I’m trying to see if a filter can express that logic.
I recently discussed and helped with something in Directory Opus in this post. It’s not directly about filters, but it made me wonder whether I could combine that approach which covers newest files with filtering. I will look into it and report back.
Thank You

Filters can match on location, path and subfolder, so yes.

You’d probably need to use scripting for that instead of copy filters.

Thanks so much!

Yes, if you get this column:

Can you set up a list of files to copy or move to different folders?

Relating to your comment in this post where you show this:
Copy TO=E:\ CREATEFOLDER={filepath|..|noroot}
is this the correct way to combine it with RankByCreated?
Copy TO={destpath} CREATEFOLDER={filepath|..|noroot} FILTERDEF script match RankByCreated/RankByCreated < 4
?
This is so fantastic, Thank You!

Is there a way to add conditional logic to a filter that states IF a folder is named "Backup" while copying apply the filter, ELSE copy normally?

Try

Copy TO={destpath} CREATEFOLDER={filepath|..|noroot} FILTERDEF location nomatch "*\\Backup" or match (location match "*\\Backup" and script match RankByCreated/RankByCreated < 4)

This is just too useful! FANTASTIC work my friend! Thank You so much!

I am having luck adding a mask for one type of file type. However, I do not have luck with adding more than one.
This works:

Copy TO={destpath} CREATEFOLDER={filepath|..|noroot} FILTERDEF location nomatch "*\\Backup" or name nomatch "*.png" or (location match "*\\Backup" and name match "*.png" and script match RankByCreated/RankByCreated < 4)`

This does not work:

Copy TO={destpath} CREATEFOLDER={filepath|..|noroot} FILTERDEF location nomatch "*\\Backup" or name nomatch "*.(png|jpg)" or (location match "*\\Backup" and name match "*.(png|jpg)" and script match RankByCreated/RankByCreated < 4)

where did I go wrong? Thanks

There's a match missing before the subclause, but that applies to both filters. Otherwise, the syntax looks correct. Why exactly is the second filter not working as expected?

Copy TO={destpath} CREATEFOLDER={filepath|..|noroot} FILTERDEF location nomatch "*\\Backup" or name nomatch "*.(png|jpg)" or match (location match "*\\Backup" and name match "*.(png|jpg)" and script match RankByCreated/RankByCreated < 4)

The way I wrote it I was expecting it to leave all the other files alone in the directory and limit the amount of listing of only PNG files and JPG files. Now that I really look at it understanding what the filter does, I see my thinking may not fit. Or just the way it’s written here it can’t work because it’s saying less than 4. I notice that when I filter in the directory where I try to do the copy “RankByCreated” is still what would normally show; it doesn’t adjust for the filter. Meaning when files are filtered whose “RankByCreated” is 1,2,3,4 the other do not adjust to become the new 1,2,3,4, they are 5,6,7 and so on.
Thanks

Building on your great work, I tried to refine the script so it would do exactly what I needed for backing things up online. After looking closely at how it behaved, I realized that if I could sort the files individually using your idea —everything else I wanted would fall into place. So I started digging into how Opus actually creates this column.
I had never made a column before; I’ve only written other Opus scripts. Your comments in the code gave me the hint I needed, especially the part about the cache. I’ve written multiple scripts that work with file extensions, so I thought: if we can figure out where the cache is told to count, then we can make it count files individually instead of as one big group.
This is what I came up with, and I hope it’s safe — I don’t know how destructive Opus’s sorting cache can be. To be clear, I worked directly from what you provided, which already worked extremely well, and I simply extended it so the sorting happens per file type instead of across all files together.
The result is that files are now sorted based on their position within their own types creation order, not as part of a single combined list. The original version you wrote — the “all files together” approach — is still extremely useful, and I was already enjoying that. Extending your script just adds another powerful way to use it.
Now I can tell Opus’s copy command to look inside a folder, leave certain files alone, and upload only the last few files of a given type — or even multiple types, like we discussed earlier.
For backup workflows, especially when dragging files to online repositories, this is unmatched. I really mean unmatched — I’ve never seen a backup program that can naturally refer to the latest files you’ve worked on. Normally you’d have to sort by date or manually pick them from a long list. That’s helpful, but nowhere near as convenient as this. Now I don’t have to micromanage which dated files I want to upload before leaving town with my laptop; I already know it’s the last three or four I worked on.
I’ll keep testing this to make sure it’s stable and doesn’t cause any issues. And hopefully it helps anyone else who wants to copy files more intelligently too.

function OnInit(initData) {
    initData.name = 'RankByCreatedInd';
    initData.version = '2026-04-27';
    initData.desc = 'Building on the excellent work of Directory Opus member LXP, this script modifies the existing constructs to individually count the files based on their file type specific individual rank of creation.';
    initData.default_enable = true;
    initData.min_version = '12.0';
}

function OnAddColumns(addColData) {
    var col = addColData.AddColumn();
    col.name = 'RankByCreatedInd';
    col.method = 'OnColumn';
    col.justify = 'right';
    col.type = 'double';
}

var fsu = DOpus.FSUtil();
var folderRankCache = null;

function OnColumn(scriptColData) {
    scriptColData.value = 0;

    var item = scriptColData.item;

    if (item.is_dir) return;

    var parentFolder = item.path;
    if (!parentFolder) return;

    if (!folderRankCache) {
        folderRankCache = DOpus.Create().Map();
    }

    var rankLookup = null;

    if (folderRankCache.exists(parentFolder)) {
        rankLookup = folderRankCache.get(parentFolder);
    } else {
        var extGroups = {};

        var folderEnum = fsu.ReadDir(parentFolder);

        while (!folderEnum.complete) {
            var folderItem = folderEnum.Next();

            if (folderItem.is_dir) continue;

            var ext = String(folderItem.ext).toLowerCase();

            if (!extGroups[ext]) {
                extGroups[ext] = [];
            }

            extGroups[ext].push([
                folderItem.realpath,
                folderItem.create.GetTime()
            ]);
        }

        folderEnum.Close();

        rankLookup = DOpus.Create().Map();

        for (var ext in extGroups) {
            extGroups[ext].sort(function (a, b) {
                return b[1] - a[1]; // newest first
            });

            for (var i = 0; i < extGroups[ext].length; i++) {
                rankLookup.set(extGroups[ext][i][0], i + 1);
            }
        }

        folderRankCache.set(parentFolder, rankLookup);
    }

    scriptColData.value = rankLookup.get(item.realpath) || -1;
}