--- name: lstat signature: 'lstat FILEHANDLE' since: 5.0 status: documented categories: ["Filehandles, files, directories"] --- ```{index} single: lstat; Perl built-in ``` *[Filehandles, files, directories](../perlfunc-by-category)* # lstat Return the 13-element status list for a path **without** following a symbolic link. `lstat` does exactly what [`stat`](stat) does — same 13-element result, same special `_` filehandle cache, same failure semantics — with one difference: if the argument names a symbolic link, `lstat` returns information about the *link itself* (its own mode, owner, size, mtime), not the target the link points to. For anything that is not a symbolic link, `lstat` and [`stat`](stat) are indistinguishable. On systems without symbolic links `lstat` silently degrades to [`stat`](stat). ## Synopsis ```perl lstat FILEHANDLE lstat EXPR lstat DIRHANDLE lstat ``` ## What you get back In list context, the same 13-element list as [`stat`](stat): ```perl my ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size, $atime,$mtime,$ctime,$blksize,$blocks) = lstat($path); ``` Field meanings are identical to [`stat`](stat) — device, inode, mode (type + permissions), link count, owner uid/gid, rdev, size, atime, mtime, ctime, preferred I/O block size, and allocated blocks. The type bits in `$mode` are what distinguish a symbolic link: on the link itself they report `S_IFLNK`, whereas [`stat`](stat) on the same name would report the type of the *target*. In scalar context `lstat` returns a true value on success and the empty string on failure. On failure the list form returns the empty list and [`$!`](../perlvar) is set to the reason (`ENOENT`, `EACCES`, etc.). If `EXPR` is omitted, `lstat` operates on [`$_`](../perlvar). ## The `_` filehandle Every successful `lstat` (and every successful [`stat`](stat) or filetest) populates the interpreter's internal "last stat buffer". Passing the bareword `_` as the argument reuses that buffer instead of making another system call: ```perl if (-l $path && (my @s = lstat(_))) { # $path was a symlink; @s describes the link, not the target } ``` Because `-l` itself does an `lstat` under the hood, the `lstat(_)` above costs nothing extra. This is the canonical way to "ask several questions about one file without re-stat'ing it". Note that a *filetest* on a bareword filename (`-l $path`) leaves the buffer describing the link; a filetest on a filehandle follows the open descriptor and thus leaves a [`stat`](stat)-shaped buffer. When you want symlink data, reach for `lstat` explicitly. ## Global state it touches - [`$_`](../perlvar) — the argument when `EXPR` is omitted. - [`$!`](../perlvar) — set on failure to the `errno` from the underlying `lstat(2)` call. - The special `_` filehandle — overwritten on every successful call. ## Examples Distinguish a symlink from its target. `stat` on a symlink silently follows it; `lstat` does not: ```perl symlink "/etc/passwd", "link.tmp"; my $via_stat = (stat "link.tmp")[7]; # size of /etc/passwd my $via_lstat = (lstat "link.tmp")[7]; # size of the link inode # (length of "/etc/passwd") ``` Walk a directory and separate links from regular files without a second syscall per entry, using the `_` buffer: ```perl use Fcntl ':mode'; for my $name (readdir $dh) { next if $name eq '.' || $name eq '..'; lstat "$dir/$name" or next; if (S_ISLNK((lstat(_))[2])) { print "link: $name\n"; } elsif (-f _) { print "file: $name\n"; } elsif (-d _) { print "dir: $name\n"; } } ``` Detect a dangling symlink — `lstat` succeeds on the link, `stat` fails because the target is gone: ```perl if (lstat $path and not stat $path) { warn "$path is a dangling symlink: $!\n"; } ``` Follow the link manually when you want both pieces of information: ```perl my @link = lstat $path or die "lstat $path: $!"; my $target = readlink $path // die "readlink $path: $!"; my @target = stat $path or die "stat $path: $!"; ``` No-argument form operating on [`$_`](../perlvar), idiomatic in `File::Find` preprocess blocks and [`grep`](grep) filters: ```perl my @symlinks = grep { lstat; -l _ } @paths; ``` ## Edge cases - **Not a symlink**: `lstat` behaves exactly like [`stat`](stat) — there is no penalty to preferring `lstat` when you are about to branch on `-l`. - **Systems without symlinks**: `lstat` silently falls back to [`stat`](stat). Code written against `lstat` stays portable; it just loses the distinguishing behaviour. - **Filehandle / dirhandle argument**: a passed FILEHANDLE or DIRHANDLE refers to an already-opened descriptor, which cannot itself be a symlink (the kernel resolved the link at [`open`](open) time). `lstat FILEHANDLE` therefore returns the same result as [`stat`](stat) on that handle. Use the EXPR form on the *path* when you need the link metadata. - **`lstat(_)` after a failed `lstat`**: the `_` buffer is left in whatever state the last *successful* stat put it in. A failed `lstat` does not clear it. Always check the return value before reading `_`. - **Relative paths**: resolved against the process's current working directory, same as [`stat`](stat). - **Permission to `lstat`**: requires search (`x`) permission on every directory component of the path, but — unlike [`stat`](stat) — does **not** require any permission on the link target. `lstat` therefore succeeds on links into directories you cannot read. - **Bareword vs expression**: `lstat $fh` where `$fh` holds a filehandle stats the handle; `lstat "$fh"` stringifies `$fh` and stats the resulting (usually meaningless) pathname. Quote intentionally. ## Differences from upstream Fully compatible with upstream Perl 5.42. ## See also - [`stat`](stat) — the link-following counterpart; identical return shape and `_` semantics - [`readlink`](readlink) — read the *target* string of a symbolic link without stat'ing it - [`symlink`](symlink) — create a symbolic link - [`-l`](../perlop) — filetest operator for "is a symbolic link"; internally performs an `lstat` and leaves the result in `_` - [`$_`](../perlvar) — default argument when `EXPR` is omitted - [`$!`](../perlvar) — reason for failure when `lstat` returns the empty list