--- name: unlink signature: 'unlink LIST' since: 5.0 status: documented categories: ["Filehandles, files, directories"] --- ```{index} single: unlink; Perl built-in ``` *[Filehandles, files, directories](../perlfunc-by-category)* # 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 ```perl 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. [`$!`](../perlvar) is set to the error from the **last** failing call — it does **not** tell you which name failed. ```perl my $removed = unlink 'a', 'b', 'c'; unlink @goners; unlink glob "*.bak"; ``` If `LIST` is omitted, `unlink` operates on the single filename in [`$_`](../perlvar). ## Global state it touches - [`$_`](../perlvar) — used as the operand when `unlink` is called with no arguments. - [`$!`](../perlvar) — set to `errno` from the last `unlink(2)` that failed. Because it reflects only the final failure in the list, it is not a reliable way to diagnose which name could not be removed. ## Finding out which file failed `unlink` returns a count, not a per-file status, and overwrites [`$!`](../perlvar) on every iteration. To attribute errors to individual names, call `unlink` once per file: ```perl 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 [`$!`](../perlvar) 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`](rmdir) instead; it calls `rmdir(2)`, which is the correct syscall for removing an empty directory entry. ```perl 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: ```perl 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 `unlink`ed 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`](readlink) first if you specifically need to act on the target. ## Examples Remove a single file, check the result: ```perl unlink $path or die "cannot remove $path: $!"; ``` Remove several and report the tally: ```perl 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): ```perl my @failed; for my $f (@goners) { unlink $f or push @failed, "$f: $!"; } die "unlink failures:\n", join("\n", @failed), "\n" if @failed; ``` Rely on [`$_`](../perlvar) in a chain of file tests: ```perl 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): ```perl 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 ()` returns `0` and does not touch [`$!`](../perlvar). Passing an empty array behaves the same way. - **Non-existent name**: returns `0` for that entry and sets [`$!`](../perlvar) to `ENOENT`. 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 be `EACCES` or `EPERM`. - **Sticky directories** (`/tmp`, mode `1777`): non-root users may only unlink names they own. Failures surface as `EPERM`. - **Return value in boolean context**: `unlink @files or die` fires only when **every** name failed (count is `0`). To die on **any** failure, compare against `scalar @files` or use the per-file loop. - **Busy file on Linux**: Linux does not prevent `unlink` of a file that is mmap'ed, executing, or open — the name is removed and the POSIX semantics above apply. Other operating systems may return `ETXTBSY` or `EBUSY`; this page covers Linux. - **Taint mode**: names coming from tainted data are allowed as operands to `unlink`; see `perlsec` for the usual caveats about accepting untrusted paths. ## Differences from upstream Fully compatible with upstream Perl 5.42. ## See also - [`rmdir`](rmdir) — remove an **empty** directory; the correct tool when the name refers to a directory entry of type `DIR` - [`rename`](rename) — move or re-link a name without dropping it, often combined with `unlink` when replacing a file atomically - [`link`](link) — create an additional hard link to a file; `unlink` is its inverse for one name at a time - [`symlink`](symlink) — create a symbolic link; note that `unlink` removes the symlink itself, not its target - [`truncate`](truncate) — shrink a file in place without unlinking it when you want to reuse the name