Recipe 9.8 Removing a Directory and Its Contents

9.8.1 Problem

You want to remove a directory tree recursively without using rm -r.

9.8.2 Solution

Use the finddepth function from File::Find, shown in Example 9-3.

Example 9-3. rmtree1
  # rmtree1 - remove whole directory trees like rm -r
  use File::Find;
  die "usage: $0 dir ..\n" unless @ARGV;
  find {
      bydepth   => 1,
      no_chdir  => 1,
      wanted    => sub { 
          if (!-l && -d _) {
              rmdir     or warn "couldn't rmdir directory $_: $!";
          } else {
              unlink    or warn "couldn't unlink file $_: $!";
  } => @ARGV;

Or use rmtree from File::Path, as shown in Example 9-4.

Example 9-4. rmtree2
  # rmtree2 - remove whole directory trees like rm -r
  use File::Path;
  die "usage: $0 dir ..\n" unless @ARGV;
  foreach $dir (@ARGV) {

These programs remove an entire directory tree. Use with extreme caution!

9.8.3 Discussion

The File::Find module supports an alternate interface in which find's first argument is a hash reference containing options and their settings. The bydepth option is the same as calling finddepth instead of find. This is guaranteed to visit all files beneath a directory before the directory itself, just what we need to remove a directory and its contents. The no_chdir option stops find from descending into directories during processing; under this option, $_ is the same as $File::Find::name. Finally, the wanted option takes a code reference, our old wanted( ) function.

We use two different functions, rmdir and unlink; both default to $_ if no argument is provided. The unlink function deletes only files, and rmdir deletes only empty directories. We need to use finddepth or the bydepth option to make sure we've first removed the directory's contents before we rmdir the directory itself.

We first check that the file isn't a symbolic link before determining whether it's a directory, because -d returns true for both a real directory and a symbolic link to a directory. stat, lstat, and file test operators like -d all use the syscall stat(2), which returns the file meta-information stored in the file's inode. These functions and operators cache that information in the special underscore (_) filehandle. This permits tests on the same file while avoiding redundant syscalls that would return the same information, slowly.

According to POSIX, if the directory is either the root directory (the mount point for the filesystems or the result of a chroot(2) syscall) or the current working directory of any process, it is unspecified whether the rmdir syscall succeeds, or whether it fails and sets errno ($! in Perl) to EBUSY ("Device busy"). Many systems tolerate the latter condition, but few the former.

9.8.4 See Also

The unlink, rmdir, lstat, and stat functions in perlfunc(1) and in Chapter 29 of Programming Perl; the documentation for the standard File::Find module (also in Chapter 32 of Programming Perl); your system's rm(1) and stat(2) manpages; the -X section of perlfunc(1), and the "Named Unary and File Test Operators" section of Chapter 3 of Programming Perl