开发者

Check if a directory is empty using C on Linux

开发者 https://www.devze.com 2023-03-13 10:54 出处:网络
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?

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

0

精彩评论

暂无评论...
验证码 换一张
取 消