Scripts that delete all files without a corresponding *.jpg file

I need to write a script that deletes all the files within a lister that does not contain a corresponding *.jpg file. For example, in this listing:

HumptyDumpty.mp4
HumptyDumpty.mp4.jpg
MaryHadALittleLamb.avi
MaryHadALittleLamb.txt
MaryHadALittleLamb.avi.jpg
ItsyBitsySpider.mov
ItsyBitsySpider.mov.jpg
LittleJackHorner.avi

The only file that would be deleted would be the:

LittleJackHorner.avi
because there is not a

LittleJackHorner.avi.jpg
file.

And what about "MaryHadALittleLamb.txt" ?!

Okay, I made my jpg extensions a little simpler. Now the listing would like like this:

HumptyDumpty.mp4
HumptyDumpty.jpg
MaryHadALittleLamb.avi
MaryHadALittleLamb.txt
MaryHadALittleLamb.jpg
ItsyBitsySpider.mov
ItsyBitsySpider.jpg
LittleJackHorner.avi

So, MaryHadALittleLamb.txt It would remain. In fact, what would remain is:

HumptyDumpty.*
MaryHadALittleLamb.*
ItsyBitsySpider.*

But,

LittleJackHorner.*

would be deleted.

Put this code in a script-button, it is already setup to work for your specific use case (or download button below). Looks like much code at first, but it's a generic filter/selection approach. It's quite easy and quick to customize for other use cases I think. I use it to select and delete jpg-files, for which there is a raw-variant and finished photoshop acr exports e.g.

So, if you need to change conditions or filetypes, you can do so in the PreFilter(), MainFilter(), Evaluate() methods. This will only select the files you'd like to delete, so you can check results first before proceeding. If you want them to be deleted right away, alter the command in ExecuteForEach().

@script jscript
////////////////////////////////////////////////////////////////////////////////
var PreFilter = function( file ){
    //return true if this file(type) is of interest in the process
    return true;
}
////////////////////////////////////////////////////////////////////////////////
var MainFilter = function( file ){
    //return true to run evaluation on this specific file(type)
    if (file.ext!=".jpg") return true;
}
////////////////////////////////////////////////////////////////////////////////
var Evaluate = function( file ){
    //return true to execute the foreach-operation on this file (select, delete etc.)
    var jpgVariant = this.FileExists(file.baseName.esc()+'\.jpg');
    if (!jpgVariant) return true;
}
////////////////////////////////////////////////////////////////////////////////
var ExecuteForEach = function( file ){
    //execute for each file that passed evaluation
    this.cmd.RunCommand('Select "'+file.name+'" EXACT');
}
////////////////////////////////////////////////////////////////////////////////
var ExecuteForAll = function(){
    //execute finally
    //this.cmd.RunCommand('SelectEx MAKEVISIBLE');
}
////////////////////////////////////////////////////////////////////////////////
function EasyFilter(filesIn,cmd,preFilter,mainFilter,evaluate,executeForEach,executeForAll){
    this.version        = 0.1;
    this.files          = [];
    this.filesFiltered  = [];
    this.filesEvaluated = [];
    this.cmd            = cmd;
    this.PreFilter      = preFilter;
    this.MainFilter     = mainFilter;
    this.Evaluate       = evaluate;
    this.ExecuteForEach = executeForEach;
    this.ExecuteForAll  = executeForAll;
    ////////////////////////////////////////////////////////////////////////////
    String.prototype.esc = function(str){
        return this.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
    }
    ////////////////////////////////////////////////////////////////////////////
    this.FileExists = function( fileNameRegex ){
        fileNameRegex = new RegExp(fileNameRegex);
        for(var i=0;i<this.files.length;i++){
            if (this.files[i].name.search(fileNameRegex)!=-1)
                return this.files[i];
        }
        return null;
    }
    ////////////////////////////////////////////////////////////////////////////
    DOpus.Output("PreFiltering ["+filesIn.length+"] files..");
    for(var i=0;i<filesIn.length;i++){
        if (this.PreFilter(filesIn[i])===true){
            this.files[this.files.length] = filesIn[i];
            DOpus.Output("    PreFilter passed ["+filesIn[i].name+"]");
        } else {
            //DOpus.Output("    PreFilter ignore ["+filesIn[i].name+"]");
        }
    }
    DOpus.Output("");
    ////////////////////////////////////////////////////////////////////////////
    DOpus.Output("MainFiltering ["+this.files.length+"] files..");
    for(var i=0;i<this.files.length;i++){
        if (this.MainFilter(this.files[i])===true){
            DOpus.Output("    MainFilter passed ["+this.files[i].name+"]");
            this.filesFiltered[this.filesFiltered.length] = this.files[i];
        } else {
            //DOpus.Output("    MainFilter ignore ["+filesIn[i].name+"]");
        }
    }
    DOpus.Output("");
    ////////////////////////////////////////////////////////////////////////////
    DOpus.Output("Evaluating ["+this.filesFiltered.length+"] files..");
    for(var i=0;i<this.filesFiltered.length;i++){
        if (this.Evaluate(this.filesFiltered[i])===true){
            DOpus.Output("    Evaluation passed ["+this.filesFiltered[i].name+"]");
            this.filesEvaluated[this.filesEvaluated.length] = this.filesFiltered[i];
        } else {
            //DOpus.Output("    Evaluation ignore ["+this.filesFiltered[i].name+"]");
        }
    }
    DOpus.Output("");
    ////////////////////////////////////////////////////////////////////////////
    DOpus.Output("Processing ["+this.filesEvaluated.length+"] files..");
    for(var i=0;i<this.filesEvaluated.length;i++){
        DOpus.Output("    Running ForEachOp ["+this.filesEvaluated[i].name+"]");
        this.ExecuteForEach(this.filesEvaluated[i]);
    }
    DOpus.Output("");
    ////////////////////////////////////////////////////////////////////////////
    DOpus.Output("Running ForAll-Operation..");
    this.ExecuteForAll();
    DOpus.Output("");
}
////////////////////////////////////////////////////////////////////////////////
function OnClick(data){
    var filesTab = data.func.sourcetab.files, files = [];
    var cmd = data.func.command; cmd.ClearFiles();
    ////////////////////////////////////////////////////////////////////////////
    for(var i=0;i<filesTab.count;i++){
        var file = {    name        : String(filesTab(i).name).toLowerCase(),
                        baseName    : String(filesTab(i).name_stem).toLowerCase(),
                        ext         : String(filesTab(i).ext).toLowerCase() };
        files[files.length] = file;
    }
    var filter = new EasyFilter(files,cmd,PreFilter,MainFilter,Evaluate,ExecuteForEach,ExecuteForAll);
    DOpus.Output("Done.");
}

Ready to use button to download:

SelectFilesWithoutJPG.dcf (8.4 KB)

That's also true of 10 lines of script code (especially if you understand enough about scripting to be able to understand and customize 100 lines). :slight_smile:

Feel free to post your 10 lines of code solution.

I'll test it out tomorrow. Thank you.

Tested it tonight. I seems to work well. Thank you again.

Here's my simple version. Handles both the original inputs (deletes 2 files) and the revised ones (deletes 1 file).

Function OnClick(ByRef clickData)
	Set cmd = clickData.func.command
	Set fsu = DOpus.FSUtil
	AnyFiles = False
	cmd.ClearFiles
	' Use selected_files if you only want to consider what's selected.
	For Each Item In clickData.func.sourcetab.files
		' Skip .jpg files, obviously.
		If (LCase(Item.ext) <> ".jpg") Then
			Set itemPath = fsu.NewPath(item.realpath)
			' See if file exists with .jpg added to end.
			If (Not fsu.Exists(itemPath & ".jpg")) Then
				' See if file exists with ext changed to .jpg
				itemPath.ext = ".jpg"
				If (Not fsu.Exists(itemPath)) Then
					cmd.AddFile(item)
					AnyFiles = True
				End If
			End If
		End If
	Next
	If (AnyFiles) Then
		' Change to this if you want to select instead of delete:
		' cmd.RunCommand "Select FROMSCRIPT DESELECTNOMATCH"
		cmd.RunCommand "Delete"
	End If
End Function

The selected files are the ones it will delete:


If you only care about the revised requirements, you can simplify things to this:

Function OnClick(ByRef clickData)
	Set cmd = clickData.func.command
	Set fsu = DOpus.FSUtil
	AnyFiles = False
	cmd.ClearFiles
	For Each Item In clickData.func.sourcetab.files
		If (LCase(Item.ext) <> ".jpg") Then
			Set itemPath = fsu.NewPath(item.realpath)
			itemPath.ext = ".jpg"
			If (Not fsu.Exists(itemPath)) Then
				cmd.AddFile(item)
				AnyFiles = True
			End If
		End If
	Next
	If (AnyFiles) Then
		cmd.RunCommand "Delete"
	End If
End Function

That will delete these files:


I guess the (LCase(Item.ext) <> ".jpg") test is redundant (other than speed, perhaps), so it could be simplified even more:

Function OnClick(ByRef clickData)
	Set cmd = clickData.func.command
	Set fsu = DOpus.FSUtil
	AnyFiles = False
	cmd.ClearFiles
	For Each Item In clickData.func.sourcetab.files
		Set itemPath = fsu.NewPath(item.realpath)
		itemPath.ext = ".jpg"
		If (Not fsu.Exists(itemPath)) Then
			cmd.AddFile(item)
			AnyFiles = True
		End If
	Next
	If (AnyFiles) Then
		cmd.RunCommand "Delete"
	End If
End Function

(That deletes the same files as the previous screenshot, but may be slightly slower.)

Your script worked great for years but now does not seem to work properly. Everything that is not a *.jpg file gets selected. I'm assuming some update in opus changed something. Can you help out again?

What about the script(s) in my post? I can't see anything in them that would not still work the same way today.

(No reason tbone's script should not still work either, but it's also harder to tell at a glance.)

Leo,

You wrote a script for me to do something similar (I think). It is (shown below). I highlight all the files to be processed and click the button. Mine is set to delete files where there is not an RW2 extension with the same name.

MagicExt = ".rw2"

Function OnClick(ByRef clickData)
	Set cmd = clickData.func.command
	cmd.deselect = False

	Set setMagic = DOpus.Create.StringSetI
	
	For Each file In cmd.files
		If file.ext = MagicExt Then
			setMagic.insert(file.name_stem)
			cmd.RemoveFile(file)
		End If
	Next

	For Each file In cmd.files
		If setMagic.exists(file.name_stem) Then
			cmd.RemoveFile(file)
		End If
	Next

	' remove this loop once you're satisfied the script works correctly
	For Each file In cmd.files
		DOpus.Output "Will delete: " & file
	Next

	' uncomment this line to delete for real
	cmd.RunCommand("delete")
	
End Function

Looks like Jon wrote that one. I'm linking the thread so there is context, so people can see more what the script does and if it fits their needs:

Right, it was Jon.

I can't get this to work. Maybe I'm doing something wrong.

I copy and pasted Leo's script into a text document and saved it as specialDelete.js, and specialDelete.vbs (I noticed it says VBS in the code box). I tried to add it to Preferences > Toolbars > Scripts, but I get an error saying Failed (Parse).

I tried adding it directly to a button, too.

What am I doing wrong?

See Script Buttons here: How to use buttons and scripts from this forum

Any script that starts with OnClick on/near the first line goes into a button like that.