Is this the right way of checking whether a directory is empty or not in C? Is there a more efficient way to check for an empty directory, especially if it has 1000s of files if not empty?
int isDirectoryEmpty(char *dirname) {
int n = 0;
struct dirent *d;
DIR *dir = opendir(dirname);
if (dir == NULL) //Not a directory or doesn't exist
return 1;
while ((d = readdir(dir)) != NULL) {
if(++n > 2)
break;
}
closedir(dir);
if (n <= 2) //Directory Empty
return 1;
else
return 0;
}
If its an empty directory, readdir
will stop after the entries '.' and '..' and hence empty if n<=2
.
If its empty or doesn't exist, it should return 1, else return 0
Update:
@c$ time ./isDirEmpty /fs/dir_with_1_file; time ./isDirEmpty /fs/dir_with_lots_of_files
0
real 0m0.007s
user 0m0.000s
sys 0m0.开发者_开发技巧004s
0
real 0m0.016s
user 0m0.000s
sys 0m0.008s
Why does it take longer to check for a directory with lots of files as compared to one with just one file?
Is there a more efficient way to check for an empty directory, especially if it has 1000s of files if not empty
The way you wrote your code it doesn't matter how many files it has (you break
if n > 2). So your code is using a maximum of 5 calls. I don't think there's any way to (portably) make it faster.
There could be a tricky strategy referred to a command-line rmdir
which couldn't remove a non-empty directory, and this feature could be used to detect if a directory is empty. To do this, try to remove a directory by calling system("rmdir your_directory")
. If the directory isn't empty, the function fails and returns a non-zero value, and probably prompts you rmdir: failed to remove 'your_directory': Directory not empty
. The prompt could be muted by redirecting stderr
into /dev/null
, and doing the mute could increase its performance. Otherwise, the directory will be removed, then you could restore it by creating it again.
This strategy would be helpful if there are any hidden files in the directory, it's still able to detect their existence. And rmdir
returns immediately regardless of how many files are in a non-empty directory in my case.
But pay attention to the command aliases, especially in the *nix shell environment, if there are any aliases of rmdir
command which added some arguments that causes rmdir
to do recursive file deletion, the trick will fail and causes all of the directories to be actually removed. This could be solved by calling system("\rmdir your_directory")
which removes the alias.
Maybe this code could help you:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char *argv[]) {
char *cmd;
char *folder = "/tmp";
int status, exitcode;
char *format="test $(ls -AU \"%s\" 2>/dev/null | head -1 | wc -l) -ne 0";
clock_t start, stop;
int size;
if(argc == 2)
folder = argv[1];
size = strlen(format)+strlen(folder)+1;
cmd = malloc(size * sizeof(char));
snprintf(cmd, size, format, folder);
printf("executing: %s\n", cmd);
status = system(cmd);
exitcode = WEXITSTATUS(status);
printf ("exit code: %d, exit status: %d\n", exitcode, status);
if (exitcode == 1)
printf("the folder is empty\n");
else
printf("the folder is non empty\n");
free(cmd);
return 0;
}
I check if the folder is empty using ls -AU folder 2>/dev/null | head -1 | wc -l, to count the files in the folder, if it returns zero the folder is empty else the folder is non empty. The WEXITSTATUS macro, returns the exit code of the executed command. head command doesn't wait until ls finish, just until the condition is fit.
some examples using the find command to generate long file list show that it is really effective
Command without head
/usr/bin/time -p -v find / -print | wc -l
output
Command being timed: "find / -print"
User time (seconds): 0.63
System time (seconds): 1.28
Percent of CPU this job got: 98%
Elapsed (wall clock) time (h:mm:ss or m:ss): 0:01.94
Average shared text size (kbytes): 0
Average unshared data size (kbytes): 0
Average stack size (kbytes): 0
Average total size (kbytes): 0
Maximum resident set size (kbytes): 6380
Average resident set size (kbytes): 0
Major (requiring I/O) page faults: 0
Minor (reclaiming a frame) page faults: 3419
Voluntary context switches: 7
Involuntary context switches: 140
Swaps: 0
File system inputs: 0
File system outputs: 0
Socket messages sent: 0
Socket messages received: 0
Signals delivered: 0
Page size (bytes): 4096
Files counted: 1043497
Command modified using head
/usr/bin/time -p -v find / -print | head -1 | wc -l
Command terminated by signal 13
Command being timed: "find / -print"
User time (seconds): 0.00
System time (seconds): 0.00
Percent of CPU this job got: 100%
Elapsed (wall clock) time (h:mm:ss or m:ss): 0:00.00
Average shared text size (kbytes): 0
Average unshared data size (kbytes): 0
Average stack size (kbytes): 0
Average total size (kbytes): 0
Maximum resident set size (kbytes): 2864
Average resident set size (kbytes): 0
Major (requiring I/O) page faults: 0
Minor (reclaiming a frame) page faults: 136
Voluntary context switches: 1
Involuntary context switches: 0
Swaps: 0
File system inputs: 0
File system outputs: 0
Socket messages sent: 0
Socket messages received: 0
Signals delivered: 0
Page size (bytes): 4096
Files counted: 1
As you can see while the first command without "head" tooks 1.28 seconds for execution the command modified with "head" tooks 0 seconds for execution.
Moreover if we measure the execution time of the above core, with and without head we have.
Normal ls:
/usr/bin/time -p ls -A /var/lib/dpkg/info/
real 0.67
user 0.06
sys 0.06
program without head
/usr/bin/time -p ./empty.exe /var/lib/dpkg/info/
executing: test $(ls -AU "/var/lib/dpkg/info/" 2>/dev/null | wc -l) -ne 0
exit code: 0, exit status: 0
the folder is non empty
real 0.01
user 0.00
sys 0.01
program using head
/usr/bin/time -p ./empty.exe /var/lib/dpkg/info/
executing: test $(ls -AU "/var/lib/dpkg/info/" 2>/dev/null | head -1 | wc -l) -ne 0
exit code: 0, exit status: 0
the folder is non empty
real 0.00
user 0.00
sys 0.00
Note: if the folder doesn't exists, or you don't have the correct permissions to access it, this program must print "the folder is empty".
The program was built using: gcc empty.c -o empty.exe
精彩评论