How to delete all empty folders and sub-folders and files in a specified directory

I want to be able to delete all empty folders and sub-folders and files in a specified directory.
I am testing some code and it doesn't work as expected.

In a different discussion, Leo mentioned this:

  • The Opus Delete command has a FAILNOTEMPTY argument which lets you tell it to delete something you think is empty while it will do nothing if it turns out that isn't the case. (Must be combined with NORECYCLE). From discussion: Trouble running rmempty.exe in script - #9 by JoeBeans

I wrote a test and it didn't go well.

function start(scriptCmdData) {

	if (scriptCmdData.func.args.got_arg.dir) {
		var arg = scriptCmdData.func.args.dir;
		var cmd = scriptCmdData.func.command;
		var folderEnum = DOpus.FSUtil.ReadDir(arg);

		DOpus.ClearOutput();

		cmd.ClearFiles();
		cmd.AddFiles(folderEnum.next(-1));
		cmd.Runcommand("Delete FAILNOTEMPTY NORECYCLE");
	} else {
		DOpus.Output("No directory was provided.");
	}
	DOpus.Output("DeleteEmpty Script is done.");
}

This does the opposite of what I want. It permanently deleted everything in the folder that wasn't empty and ignored the one item that was completely empty. And ofcouse I couldn't undo because of the "norecycle".

Not sure what to do. seems like I will need to also recursively search the sub folders.

That would add all files and folders to the list of things to be deleted. You probably don't want that, especially not the files.

The default script shows how to enumerate a directory and select just the folders under it.

Thanks, it took me a few minutest to find the "-1" trick, so I could pass all of the files and folders at once.

But, back to the question, why did it delete the non-empty files and folders but not the empty ones? It was my understanding that your original comment meant that FAILNOTEMPTY combined with NORECYCLE would prevent non-empty items from being deleted.

I wish I could find a way to check if something is empty, but I assumed that failnotempty was the way to go since that was what you replied with in the other discussion

I'm not sure what it will do with files, to be honest. But if you want it to work recursively then you're going to have to loop through the folders, and sub-folders, to find the empty ones in the script. And exclude the files. What you're doing currently doesn't make sense to achieve what you're trying to do.

Okay, can you please explain why it deleted a non-empty folder but didn't delete the empty folder? Also why do I need to use norecycle?

Please explain.

How do test for isEmpty? The only discussions I can find are many years old.

Why though? I want to delete empty files.

It's not that I want to do it recursivly, but it seems like that would be the only way. if you have a better way please tell me.

It shouldn't do that, AFAIK. Your script code is incomplete so I cannot try it here, unless you provide the full script (and also tell us how you are running it).

Also why do I need to use norecycle?

Because the documentation tells you FAILNOTEMPTY only works with NORECYCLE.

How do test for isEmpty? The only discussions I can find are many years old.

You already know how to get a list of what's in a directory. You can use that to check if a directory is empty.

Why though? I want to delete empty files.

As in zero-byte files? Then FAILNOTEMPTY won't help you as it's to help with cleaning up empty directories. But, in this case, you can probably do everything with a simple delete filter that matches anything which has size = 0 bytes, which will work with both files and folders. No scripting required in that case.

https://www.gpsoft.com.au/help/opus12/index.html#!Documents/Filtered_Operations.htm

This script is run by a button with this as the function/command DeleteEmpty DIR {sourcepath}
command

function OnInit(initData)
{
	initData.name = "DeleteEmpty";
	initData.version = "1.0";
	initData.copyright = "(c) 2023 chris";
//	initData.url = "https://resource.dopus.com/c/buttons-scripts/16";
	initData.desc = "";
	initData.default_enable = true;
	initData.min_version = "12.0";

	// Create a new ScriptCommand object and initialize it to add the command to Opus
	var cmd = initData.AddCommand();
	cmd.name = initData.name;
	cmd.method = "start";
	cmd.desc = initData.desc;
	cmd.label = "Delete Empty files and folders";
	cmd.template = "DIR/K,";
}

function start(scriptCmdData) {

	if (scriptCmdData.func.args.got_arg.dir) {//Check if parameter dir was passed an argument
		var arg = scriptCmdData.func.args.dir;
		var cmd = scriptCmdData.func.command;
		var folderEnum = DOpus.FSUtil.ReadDir(arg);

		DOpus.ClearOutput();
		DOpus.Output("Checking Path: " + arg + " for empty dir/files.");

		
		DOpus.Output(DOpus.TypeOf(folderEnum) );
		cmd.ClearFiles();
		cmd.AddFiles(folderEnum.next(-1));
		cmd.Runcommand("Delete FAILNOTEMPTY NORECYCLE");
		// while (!folderEnum.complete) {
		// 	var item = folderEnum.Next();
		// 	cmd.ClearFiles;
		// 	
		// 	// if (item.is_dir) {
		// 	// 	cmd.Runcommand("Delete FAILNOTEMPTY NORECYCLE");
		// 	// } else {
		// 	// 	
		// 	// }
		// }
	} else {
		DOpus.Output("No directory was provided.");
	}
	DOpus.Output("DeleteEmpty Script is done.");
}

I suggest using this: Remove Empty Directories (aka RED) (jonasjohn.de). It's been around for a while (so it's mature and presumably largely bug-free). I use it regularly (from a button in DO) and it works great.

I just checked your link. I am using a different program that was posted by a user here on the forum. I will try yours.

I can't get the script to delete a non-empty folder.

The Delete FAILNOTEMPTY NORECYCLE command should fail as soon as it encounters a non-empty folder, after which it won't try to delete any further empty folders. So that probably explains why it didn't delete all your empty folders, if there was a non-empty one in the mix.

The purpose of FAILNOTEMPTY is to avoid accidents, where you think a folder is empty but it actually isn't. (For example, it was made for common "move everything up a level" buttons people made that often failed to consider nested directories with the same names as their parents, where moving everything up still left the original folder with things inside it that shouldn't be deleted.)

You could feed it a single folder at a time, and run the command more than once. You'd still need to make your script recursive in order to delete empty subdirectories. And you'd still need to make your script skip over files (or non-empty files, if that's what you want) as FAILNOTEMPTY is only about folders and doesn't change what the delete command does with files.

(Although I might change that, as it should probably skip all files entirely. The aim is to avoid accidents when "cleaning up" something that is expected to be an empty directory but isn't. Something being a file would be unexpected in that case, so the command should treat that as an error the same as it does a non-empty directory.)

Edit: There is also a SKIPNOTEMPTY which is similar but will skip non-empty folders instead of stopping entirely when it encounters one. But you'd still need to make your script recursive in order to do all the things you want.

Leo, I really appreciate all the help you have given me since I bought Directory Opus. The program is amazing, and you are very helpful and knowledgeable.

That said, I feel like this discussion has become a mess. I am not sure if that line in your post is referring to me.

That is not what I have been talking about, and I can't find where I might have made a typo and said that by mistake.

Here are some of the things, that I said to explain the problem.

I don't know why you think I want the script to delete a non-empty folder. If I said that somewhere it was a typo.

I provided the code and an Image of how I set up the button to run it. That was just some test code to understand how stuff worked. It would have been nice to know why it did the opposite of what I expected. It deleted folders that were not empty and failed to delete empty folders. At this point, it doesn't matter anymore to me. That code was never going to be what I used so the bug probably wouldn't apply to my actual project anyway.

I really appreciate your detailed explanation of FAILNOTEMPTY. I now understand some of the other things that happened.

I am going to let go of this topic for now. I am just going to use a 3rd party program for now.

You said you had a problem with the script deleting a non-empty folder.

I tried the script and could not get that to happen, and said so. In other words, I tried but could not reproduce what you saw.

It might depend on the exact files/folders/names/sizes of items you are testing with, perhaps.

But it's really a side-issue, because the way you're doing things in the script is not going to work in any case. To do everything you want, your script will need to go through the individual items and delete (only) the ones it wants to delete. You won't be able to pass the list of everything below the current folder to the delete command to achieve what you're aiming for, because there isn't a mode where the delete command deletes "zero-byte files and empty folders and nothing else". (At least, not without using a filter. A filter would make the script more complex rather than less, so I wouldn't worry about that.)

I am sorry. I completely misunderstood. For some reason, I thought you were saying I said that. My only excuse is a "brain fart". I can't explain it any other way.

One last question on this topic (I hope). Is there a way to test a folder to see if it is empty before trying to delete it or is the correct way to simply try to delete it with Delete FAILNOTEMPTY NORECYCLE? If I use item.size on a folder with things in it, DOopus.output prints 0.

(Here is the code that gives me 0 for the size of a folder with stuff in it if you need a code example. The following code lacks recursion so I know it won't do exactly what I want.

function OnInit(initData) {
	initData.name = "DeleteEmpty";
	initData.version = "1.0";
	initData.copyright = "(c) 2023 chris";
	//	initData.url = "https://resource.dopus.com/c/buttons-scripts/16";
	initData.desc = "";
	initData.default_enable = true;
	initData.min_version = "12.0";

	// Create a new ScriptCommand object and initialize it to add the command to Opus
	var cmd = initData.AddCommand();
	cmd.name = initData.name;
	cmd.method = "start";
	cmd.desc = initData.desc;
	cmd.label = "Delete Empty files and folders";
	cmd.template = "DIR/K,";
}

function start(scriptCmdData) {

	if (scriptCmdData.func.args.got_arg.dir) {//Check if parameter was passed an argument
		var arg = scriptCmdData.func.args.dir;
		var cmd = scriptCmdData.func.command;
		var folderEnum = DOpus.FSUtil.ReadDir(arg, "r");

		DOpus.ClearOutput();
		DOpus.Output("Checking Path: " + arg + " for empty dir/files.");

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

			if (item.is_dir) {
				DOpus.Output(item.name);
				**DOpus.Output(item.size);**
				cmd.ClearFiles();
				cmd.AddFile(item);
				cmd.Runcommand("Delete FAILNOTEMPTY NORECYCLE QUIET");
			}
		}
	} else {
		DOpus.Output("No directory was provided.");
	}
	DOpus.Output("DeleteEmpty Script is done.");
}

Yes:

This would have been my suggestion as well, but it doesn't seem to work :frowning:

This filter will return zero-byte files, but no folders:

The only setting I found to get folders with size equal to zero was this:

Unfortunately, this doesn't return any files. Additionally, it can only be used in the panel, not in a button or script.

You need an explicit "Type = Folders" for it to consider empty folders as well.

A filter like this should match both 0 byte files and empty folders (since folder size isn't usually considered, and you wouldn't usually want it to be):

  • Size Match 0 bytes
  • OR
  • SubClause
    • Type Match "Folders Only"
    • AND
    • Size Match 0 bytes
1 Like

I frequently delete empty folders and zero size files when consolidating and organizing my client's photo and video collections, and this is how:

Removing Empty Folders:

  1. I open a directory using Grouped layout.
    2: I choose display the 'Files (total)' column (my User Default has these columns).
    3: I sort by the 'Files (total)' column.
    4: I select and delete folders with zero size files.

Removing Zero Size Files:

  1. I open a directory in 'Flat View (Mixed) No folders'.
  2. I sort by file size.
    3: I select and delete zero size files.

Yes, it does :+1:


Want to save some typing? Save SizeZero.ofi.txt to

%appdata%\GPSoftware\Directory Opus\Filters

and remove .txt.

How though? This is my best guess based on your clues. It seems to work, but I only did two tests. Is this the way you are suggesting?

		//check if folder is empty
		var path = scriptCmdData.func.sourcetab.path;
		DOpus.Output("Checking if " + path + " is empty");
		var folderEnum = DOpus.FSUtil.ReadDir(path);

		if(folderEnum.complete){
			DOpus.Output("empty");
			scriptCmdData.func.command.ClearFiles();
			scriptCmdData.func.command.AddFile(path);
		        scriptCmdData.func.command.Runcommand("Delete FAILNOTEMPTY NORECYCLE QUIET");
		} else {
			DOpus.Output("not empty");
                        //Skip
		}