Filehandles, files, directories
unlink#
Remove one or more directory entries that name files.
unlink asks the kernel to drop the link between each name in LIST
and the file it refers to. If that link was the last one and no
process still holds the file open, the file’s storage is reclaimed;
otherwise the file remains on disk, nameless but accessible through
any open descriptor, until the last handle is closed. unlink
operates on directory entries, not on file contents.
Synopsis#
unlink LIST
unlink
What you get back#
The number of files unlink actually removed, as an integer. On
total success that equals scalar @LIST; on partial or total failure
it is the count of names that were removed before and after the ones
that failed. Zero is a valid “false” return when nothing could be
removed. $! is set to the error from the last failing
call — it does not tell you which name failed.
my $removed = unlink 'a', 'b', 'c';
unlink @goners;
unlink glob "*.bak";
If LIST is omitted, unlink operates on the single filename in
$_.
Global state it touches#
Finding out which file failed#
unlink returns a count, not a per-file status, and overwrites
$! on every iteration. To attribute errors to individual
names, call unlink once per file:
foreach my $file (@goners) {
unlink $file
or warn "could not unlink $file: $!";
}
The per-file form also lets you keep going after a failure, which the list form does implicitly but silently.
Directories#
unlink does not remove directories. On Linux it returns 0 and
sets $! to EISDIR when asked to. The perl binary recognises a
-U command-line switch that, combined with running as superuser,
lifts the check — but even then unlinking a directory bypasses the
filesystem’s directory-integrity guarantees and can corrupt the
parent directory’s link count. Use rmdir instead; it calls
rmdir(2), which is the correct syscall for removing an empty
directory entry.
rmdir $dir or die "rmdir $dir: $!"; # correct
unlink $dir; # fails with EISDIR
POSIX deletion semantics#
unlink removes the name, not the file. As long as any process
holds the file open, the file’s data and inode remain valid and that
process can keep reading and writing through its descriptor. Storage
is reclaimed only when the last name is gone and the last
descriptor is closed. This is the idiomatic pattern for creating a
temporary file that cleans itself up even on a crash:
open my $fh, "+>", $path or die "open $path: $!";
unlink $path or die "unlink $path: $!";
# $fh is still usable; $path is gone from the directory.
# When $fh closes or the process exits, the storage is freed.
It is also why a long-running program can keep writing to a log file
whose name has been unlinked by an operator — the writes succeed,
but nothing on disk is visible under the old name. df will report
the space as used until the handle closes.
Symbolic links#
If a name in LIST is a symbolic link, unlink removes the link
itself, not the file it points at. The target is left untouched
even if the link is dangling or points to a directory. This matches
the unlink(2) syscall and is almost always what you want; use
readlink first if you specifically need to act on the target.
Examples#
Remove a single file, check the result:
unlink $path
or die "cannot remove $path: $!";
Remove several and report the tally:
my @files = glob "*.tmp";
my $gone = unlink @files;
warn "removed $gone of ", scalar @files, " files\n"
if $gone != @files;
Per-file diagnostics (the only reliable way to know which name failed):
my @failed;
for my $f (@goners) {
unlink $f or push @failed, "$f: $!";
}
die "unlink failures:\n", join("\n", @failed), "\n" if @failed;
Rely on $_ in a chain of file tests:
for (glob "*.bak") {
next unless -f && -M _ > 30; # older than 30 days
unlink or warn "skip $_: $!";
}
Self-cleaning scratch file (POSIX unlink-while-open idiom):
open my $scratch, "+>", "/tmp/work.$$" or die $!;
unlink "/tmp/work.$$" or die $!;
# write through $scratch, no cleanup needed on exit
Edge cases#
Empty
LIST:unlink ()returns0and does not touch$!. Passing an empty array behaves the same way.Non-existent name: returns
0for that entry and sets$!toENOENT. The rest of the list is still processed.Permission denied:
unlink(2)checks write permission on the containing directory, not on the file itself. Removing a file you cannot write is allowed as long as you can write its parent directory; removing a file you own in a directory you cannot write is not.$!will beEACCESorEPERM.Sticky directories (
/tmp, mode1777): non-root users may only unlink names they own. Failures surface asEPERM.Return value in boolean context:
unlink @files or diefires only when every name failed (count is0). To die on any failure, compare againstscalar @filesor use the per-file loop.Busy file on Linux: Linux does not prevent
unlinkof a file that is mmap’ed, executing, or open — the name is removed and the POSIX semantics above apply. Other operating systems may returnETXTBSYorEBUSY; this page covers Linux.Taint mode: names coming from tainted data are allowed as operands to
unlink; seeperlsecfor the usual caveats about accepting untrusted paths.
Differences from upstream#
Fully compatible with upstream Perl 5.42.
See also#
rmdir— remove an empty directory; the correct tool when the name refers to a directory entry of typeDIRrename— move or re-link a name without dropping it, often combined withunlinkwhen replacing a file atomicallylink— create an additional hard link to a file;unlinkis its inverse for one name at a timesymlink— create a symbolic link; note thatunlinkremoves the symlink itself, not its targettruncate— shrink a file in place without unlinking it when you want to reuse the name