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