sysseek#
Reposition a filehandle at the system level, bypassing PerlIO buffering.
sysseek moves FILEHANDLE’s kernel file-descriptor offset to a new byte position by calling the underlying lseek(2) directly. It is the companion of sysread and syswrite: the three form the unbuffered I/O family, which talks to the kernel without going through Perl’s :perlio or stdio-like buffer stack. Use sysseek on handles you access with the sys-family; use seek on handles you access with read, readline, or print.
Synopsis#
sysseek FILEHANDLE, POSITION, WHENCE
sysseek $fh, 0, 0 # rewind to start
sysseek $fh, 0, 1 # report current position
sysseek $fh, -1024, 2 # 1024 bytes before EOF
What you get back#
The new absolute byte offset on success, or undef on failure (with $! set). A position of zero is returned as the dualvar string "0 but true" so that the value prints as 0 in numeric context yet remains truthy in boolean context — a check like if (sysseek ...) is safe at offset zero.
defined(sysseek $fh, $offset, 0)
or die "sysseek to $offset failed: $!";
Because the return value carries the new offset, sysseek doubles as a «where am I» primitive — the reason the idiomatic systell is one line:
use Fcntl 'SEEK_CUR';
sub systell { sysseek($_[0], 0, SEEK_CUR) }
WHENCE values#
WHENCE is an integer with three meaningful values. Prefer the symbolic constants from Fcntl — they also make the call portable to platforms that do not use 0 / 1 / 2:
0/SEEK_SET—POSITIONis measured from the start of the file.POSITIONmust be non-negative.1/SEEK_CUR—POSITIONis added to the current position. Negative values move backward, positive values forward.sysseek $fh, 0, 1returns the current offset without moving the pointer.2/SEEK_END—POSITIONis added to the end-of-file offset.POSITIONis typically zero or negative.
use Fcntl qw(SEEK_SET SEEK_CUR SEEK_END);
sysseek $fh, 0, SEEK_SET; # rewind
sysseek $fh, 0, SEEK_END; # move to EOF
sysseek $fh, -$n, SEEK_CUR; # $n bytes back from here
Bytes, not characters#
sysseek operates on byte offsets, always. Even when the handle has a character-oriented layer such as :encoding(UTF-8), the offset it accepts and returns is a raw byte count. This matches the kernel view but is irrelevant in practice: sysseek is meant for handles that do not use PerlIO layers, and mixing it with a decoding layer is one of the confusion traps listed below.
If the data is character-oriented, use seek and tell on the buffered side instead of sysseek.
Do not mix with buffered I/O#
sysseek talks directly to the file descriptor. read, readline, print, write, seek, tell, and eof talk to the PerlIO buffer sitting in front of the descriptor. The buffer’s position and the kernel’s position are independent — a sysseek moves the kernel pointer but does not flush or invalidate the buffer, so the next buffered read returns stale bytes from before the sysseek, and the next buffered write lands wherever the buffer thought it was.
The rule is simple: pick one family per handle.
Global state it touches#
Examples#
Random-access read of a fixed-length record. The file holds 128-byte records; jump straight to record 42:
use Fcntl 'SEEK_SET';
sysopen my $fh, $path, O_RDONLY or die $!;
sysseek $fh, 42 * 128, SEEK_SET or die "sysseek: $!";
sysread $fh, my $rec, 128 or die "sysread: $!";
Report the current offset without moving the pointer — the systell idiom:
use Fcntl 'SEEK_CUR';
my $pos = sysseek $fh, 0, SEEK_CUR;
defined $pos or die "sysseek: $!";
print "at byte $pos\n";
Append to a file held open for read-write, then rewind to replay from the start:
use Fcntl qw(SEEK_SET SEEK_END);
sysseek $fh, 0, SEEK_END or die $!;
syswrite $fh, $record or die $!;
sysseek $fh, 0, SEEK_SET or die $!;
Zero is still true — the dualvar lets you write the terse form without accidentally treating a successful rewind as a failure:
if (my $pos = sysseek $fh, 0, 0) {
# entered even when $pos stringifies to "0"
printf "rewound; pos=%d\n", $pos;
}
else {
die "sysseek failed: $!";
}
Seek past end-of-file to create a sparse hole, then write the tail marker:
use Fcntl 'SEEK_SET';
sysseek $fh, 1_000_000, SEEK_SET or die $!;
syswrite $fh, "END" or die $!;
Edge cases#
Unseekable handles: pipes, sockets,
TTYs, and most special devices fail with$!set toESPIPE(Illegal seek). The return value isundef; always check withdefined.Do not mix with buffered I/O on the same handle.
sysseekbypasses the PerlIO buffer;read,readline,print,write,seek,tell, andeofuse it. Interleaving the two families produces silently wrong results. Pick one family per handle and stay with it.Zero stringifies as
"0 but true". Boolean tests work at offset zero —if (sysseek ...)stays truthy — but string comparisons against the literal"0"do not:my $pos = sysseek $fh, 0, 0; $pos == 0 or die; # true $pos eq "0" and die "nope"; # false — the string is "0 but true"
Use
definedfor success/failure detection and numeric comparison for the offset value.Encoding layers:
sysseekon a handle with an:encoding(...)layer is almost always a mistake — the byte offset can land mid codepoint, and the sys-family is meant for raw bytes anyway. If the data needs decoding, useseekwith the buffered family.Negative offsets with
SEEK_SET:POSITIONmust be non-negative whenWHENCEis0. A negative value fails withEINVAL.Very large files:
sysseekreturns the kernel’s full 64-bit offset; positions beyond 2 GiB round-trip correctly through subsequentsysseekcalls on all supported 64-bit builds.Closed filehandle: returns
undefand sets$!; underuse warningsasysseek() on closed filehandlewarning is emitted.
Differences from upstream#
Fully compatible with upstream Perl 5.42.
See also#
seek— buffered counterpart; use it on handles accessed viaread,readline, orprintsysread— the unbuffered read paired withsysseek; both skip PerlIO and speak directly to the file descriptorsyswrite— the unbuffered write; the third member of the family that belongs on the same handle assysseeksysopen— opens a handle at the descriptor level, the usual way to get a handle you will later drive with the sys-familytell— buffered-side current-offset query; pair it withseek, not withsysseekFcntl— source of theSEEK_SET,SEEK_CUR,SEEK_ENDconstants