Removing a Directory and Its Contents (Perl Cookbook, 2nd Edition)

## 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

>       #!/usr/bin/perl
>       # 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

>       #!/usr/bin/perl
>       # rmtree2 - remove whole directory trees like rm -r
>       use File::Path;
>       die "usage: $0 dir ..\n" unless @ARGV;
>       foreach $dir (@ARGV) {
>           rmtree($dir);
>       }

> WARNING:

> 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

Copyright © 2003 O'Reilly & Associates. All rights reserved.