I/O

tell#

Return the current byte position of a filehandle.

tell reports where the next read or write on FILEHANDLE will happen, measured in bytes from the start of the file. Pair it with seek to save a position now and return to it later. If FILEHANDLE is omitted, tell reports the position of the handle last read from — including the implicit handle inside while (<FH>) loops.

Synopsis#

tell FILEHANDLE
tell

What you get back#

A non-negative integer byte offset on success, -1 on error. The value is suitable as the POSITION argument to seek with WHENCE set to 0 (absolute seek). On pipes, FIFOs, sockets, and sometimes on the standard streams, the underlying OS has no seekable position and tell returns -1; $! is not necessarily set in that case, so treat -1 as the authoritative failure signal.

my $pos = tell $fh;
die "not a seekable handle" if $pos < 0;
# ... read or write ...
seek $fh, $pos, 0;                  # back to where we were

Global state it touches#

  • Last-read filehandle: the no-argument form tell uses the handle most recently read from in the current scope. Every readline, <FH>, or <> updates this. Mixing tell with no argument and multiple active handles is a source of surprising results — name the handle explicitly when in doubt.

  • $! is not a reliable error channel here. Check for -1 directly rather than testing $! after the call.

Bytes, not characters#

tell always returns a byte offset, even when the handle has a character-oriented PerlIO layer such as :encoding(UTF-8) or :utf8. This is deliberate: translating a character count into a byte offset in a variable-width encoding would require rereading the file from the beginning. The same rule applies to seek and sysseek — they all speak bytes.

Consequence: on a UTF-8 text file you cannot in general use tell to count characters. Use it only to mark a position you intend to feed back into seek.

Examples#

Remember a position, read ahead, return:

open my $fh, "<", "log.txt" or die $!;
my $mark = tell $fh;                # 0
my $first = <$fh>;                  # read one line
seek $fh, $mark, 0;                 # rewind to the mark
my $again = <$fh>;                  # same line again

Record the end-of-header offset so later code can rescan the body without reparsing the header:

while (my $line = <$fh>) {
    last if $line =~ /^\r?\n\z/;    # blank line ends headers
}
my $body_start = tell $fh;
# ... process body ...
seek $fh, $body_start, 0;           # second pass over the body

The no-argument form uses the handle last read from. Inside a while (<>) loop that handle is the implicit ARGV:

while (<>) {
    print "at ", tell, ": $_";      # offset within the current file
}

tell on a pipe returns -1 because pipes are not seekable:

open my $ph, "-|", "ls" or die $!;
my $pos = tell $ph;                 # -1

Filehandles inside complex expressions need the same block form as with printtell $handles[0] is a syntax error. Use:

my @handles = (\*STDIN, $fh);
my $pos = tell { $handles[1] };

Edge cases#

  • Closed filehandle: returns -1. Under use warnings a tell() on closed filehandle warning is emitted.

  • No-argument form, nothing read yet: returns -1. The “last-read filehandle” is only meaningful after an actual read.

  • After sysread / syswrite / sysseek: do not use tell on a handle you have manipulated with the sys* family. Those functions bypass PerlIO buffering; tell reports the buffered position, which will be wrong by the buffer contents. Use sysseek with a whence of 1 and an offset of 0 as the sys*-safe position query.

  • Standard streams: tell STDIN, tell STDOUT, tell STDERR may return -1 or a meaningful offset depending on whether the stream is backed by a regular file (shell redirection) or a terminal, pipe, or socket.

  • No systell: there is no separate function for buffered-bypassing position. The idiom is:

    my $pos = sysseek $fh, 0, 1;
    
  • Binary-mode vs text-mode on non-Linux platforms: pperl targets Linux only, so tell offsets always match raw file bytes. CRLF translation layers that complicate tell on Windows-family perls do not apply here.

  • Append mode (>>): after open my $fh, ">>", ..., the initial tell $fh may report 0 until the first write, because some platforms position the handle at end-of-file only at each write. Don’t rely on the initial position; write once, then tell.

Differences from upstream#

Fully compatible with upstream Perl 5.42.

See also#

  • seek — jump to an absolute byte offset previously returned by tell

  • sysseek — position query and jump for handles used with sysread / syswrite, bypassing PerlIO buffering

  • telldir — the directory-handle analogue; pairs with seekdir

  • eof — test for end-of-file rather than absolute position

  • open — produce the filehandle in the first place; mode and PerlIO layers determine whether the handle is seekable