class IO::CatHandle

Use multiple IO handles as if they were one

class IO::CatHandle is IO::Handle { }

This class has been available in Rakudo since version 2017.06.

The IO::CatHandle|/type/IO::CatHandle class provides a means to create an IO::Handle that seamlessly gathers input from multiple IO::Handle and IO::Pipe sources.

All of the IO::Handle's methods are implemented, and while attempt to use write methods will (currently) throw and exception, an IO::CatHandle is usable anywhere a read-only IO::Handle can be used.

Methods

method new

Defined as:

method new(*@handles:&on-switch:$chomp = True,
           :$nl-in = ["\n""\r\n"], Str :$encodingBool :$bin)

Creates a new IO::CatHandle object.

The @handles positional argument indicates a source of handles for the IO::CatHandle to read from and can deal with a mixed collection of Cool, IO::Path, and IO::Handle (including IO::Pipe) objects. As input from IO::CatHandle is processed (so operations won't happen during .new call, but only when @handles' data is needed), it will walk through the @handles list, processing each argument as follows:

In short, all the @handles end up as IO::Handle objects opened in the same mode and with the same attributes as the invocant IO::CatHandle.

See .on-switch method for details on the :&on-switch named argument, which by default is not set.

The :$encoding named argument specifies the handle's encoding and accepts the same values as IO::Handle.encoding. Set :$bin named argument to True if you wish the handle to be in binary mode. Attempting to specify both a defined :$encoding and a True :$bin is a fatal error resulting in X::IO::BinaryAndEncoding exception thrown. If neither :$encoding is set nor :$bin set to a true value, the handle will default to utf8 encoding.

The :$chomp and :$nl-in arguments have the same meaning as in IO::Handle and take and default to the same values.

method chomp

Defined as:

method chomp(IO::CatHandle:D:is rw

Sets the invocant's $.chomp attribute to the assigned value. All source handles, including the active one will use the provided $.chomp value.

(my $f1 = 'foo'.IO).spurt: "A\nB\nC\n";
(my $f2 = 'bar'.IO).spurt: "D\nE\n";
with IO::CatHandle.new: $f1$f2 {
    # .chomp is True by default: 
    (.get xx 2).perl.say# OUTPUT: «("A", "B").Seq␤» 
 
    .chomp = False;
    (.get xx 3).perl.say# OUTPUT: «("C\n", "D\n", "E\n").Seq␤» 
    .close
}

method nl-in

Defined as:

method nl-in(IO::CatHandle:D:is rw

Sets the invocant's $.nl-in attribute to the assigned value, which can be a Str or a List of Str, where each Str object represents the end-of-line string. All source handles, including the active one will use the provided $.nl-in value. Note that source handle boundary is always counted as a new line break.

(my $f1 = 'foo'.IO).spurt: "A\nB\nC";
(my $f2 = 'bar'.IO).spurt: "DxEx";
with IO::CatHandle.new: $f1$f2 {
    # .nl-in is ["\n", "\r\n"] by default: 
    (.get xx 2).perl.say# OUTPUT: «("A", "B").Seq␤» 
 
    .nl-in = 'x';
    (.get xx 3).perl.say# OUTPUT: «("C", "D", "E").Seq␤» 
    .close
}

method close

Defined as:

method close(IO::CatHandle:D: --> True)

Closes the currently active source handle, as well as any already-open source handles, and empties the source handle queue. Unlike a regular IO::Handle, an explicit call to .close is often not necessary on a CatHandle, as merely exhausting all the input closes all the handles that need to be closed.

with IO::CatHandle.new: @bunch-of-handles {
    say .readchars: 42;
    .close# we are done; close all the open handles 
}

method comb

Defined as:

method comb(IO::CatHandle:D: |args --> Seq:D)

Read the handle and processes its contents the same way Str.comb does, taking the same arguments. Implementations may slurp the contents of all the source handles in their entirety when this method is called.

(my $f1 = 'foo'.IO).spurt: 'foo';
(my $f2 = 'bar'.IO).spurt: 'bar';
IO::CatHandle.new($f1$f2).comb(2).perl.say;
# OUTPUT: «("fo", "ob", "ar").Seq␤» 

method DESTROY

Defined as:

method DESTROY(IO::CatHandle:D:)

Calls .close. This method isn't to be used directly, but is something that's called during garbage collection.

method encoding

Defined as:

multi method encoding(IO::CatHandle:D:)
multi method encoding(IO::CatHandle:D: $new-encoding)

Sets the invocant's $.encoding attribute to the provided value. Valid values are the same as those accepted by IO::Handle.encoding (use value Nil to switch to binary mode). All source handles, including the active one will use the provided $.encoding value.

(my $f1 = 'foo'.IO).spurt: 'I ♥ Perl';
(my $f2 = 'bar'.IO).spurt: 'meow';
with IO::CatHandle.new: $f1$f2 {
    # .encoding is 'utf8' by default: 
    .readchars(5).say# OUTPUT: «I ♥ P␤» 
 
    .encoding: Nil# switch to binary mode 
    .slurp.say# OUTPUT: «Buf[uint8]:0x<72 6c 6d 65 6f 77>␤» 
}

method eof

Defined as:

method eof(IO::CatHandle:D: --> Bool:D)

Returns True if the read operations have exhausted the source handle queue, including the contents of the last handle. Note: calling this method may cause one or more .on-switch calls, while the source handle queue is examined, and the source handle queue may get exhausted.

(my $f1 = 'foo'.IO).spurt: 'foo';
(my $f2 = 'bar'.IO).spurt: 'bar';
with IO::CatHandle.new: :on-switch{ print 'SWITCH! ' }$f1$f2 {
                   # OUTPUT: «SWITCH! » 
    .eof.say;      # OUTPUT: «False␤» 
    .readchars(3);
    .eof.say;      # OUTPUT: «SWITCH! False␤» 
 
    .slurp;        # OUTPUT: «SWITCH! » 
    .eof.say;      # OUTPUT: «True␤» 
}

The same caveats for non-seekable handles and empty files that apply to IO::Handle.eof apply here.

method get

Defined as:

method get(IO::CatHandle:D: --> Bool:D)

Returns a single line of input from the handle, with the new line string defined by the value(s) of $.nl-in attribute, which will be removed from the line if $.chomp attribute is set to True. Returns Nil when there is no more input. It is an error to call this method when the handle is in binary mode, resulting in X::IO::BinaryMode exception being thrown.

(my $f1 = 'foo'.IO).spurt: "a\nb\nc";
(my $f2 = 'bar'.IO).spurt: "d\ne";
my $cat = IO::CatHandle.new: $f1$f2;
.say while $_ = $cat.get# OUTPUT: «a␤b␤c␤d␤e␤» 

method getc

Defined as:

method getc(IO::CatHandle:D: --> Bool:D)

Returns a single character of input from the handle. All the caveats described in IO::Handle.getc apply. Returns Nil when there is no more input. It is an error to call this method when the handle is in binary mode, resulting in X::IO::BinaryMode exception being thrown.

(my $f1 = 'foo'.IO).spurt: 'I ♥ Perl';
(my $f2 = 'bar'.IO).spurt: 'meow';
my $cat = IO::CatHandle.new: $f1$f2;
.say while $_ = $cat.getc# OUTPUT: «I␤ ␤♥␤ ␤P␤e␤r␤l␤m␤e␤o␤w␤» 

method handles

Defines as:

method handles(IO::CatHandle:D: --> Seq:D)

Returns a Seq containing the currently-active handle, as well as all the remaining source handles produced by calling next-handle. If the invocant has already been fully-consumed, returns an empty Seq.

This method is especially handy when working with IO::ArgFiles, where you want to treat each filehandle separately:

# print at most the first 2 lines of each file in $*ARGFILES: 
.say for flat $*ARGFILES.handles.map: *.lines: 2

It is acceptable to call this method multiple times; .handles.head is a valid idiom for obtaining the currently-active handle. If, between reification of the elements of the returned Seq the handles get switched by some other means, the next element produced by the Seq would be the next handle of the invocant, not the handle that would've been produced if no switching occurred:

(my $file1 := 'file1'.IO).spurt: "1a\n1b\n1c";
(my $file2 := 'file2'.IO).spurt: "2a\n2b\n2c";
(my $file3 := 'file3'.IO).spurt: "3a\n3b\n3c";
my $cat := IO::CatHandle.new: $file1$file2$file3;
for $cat.handles {
    say .lines: 2;
    $cat.next-handle;
}
# OUTPUT: «(1a 1b)␤(3a 3b)␤»

Likewise, reifying the returned Seq consumes the invocant's source handles and once it is fully reified the invocant becomes fully-consumed.

method IO

Defined as:

method IO(IO::CatHandle:D:)

Alias for .path

method lines

Defined as:

method lines(IO::CatHandle:D: $limit = Inf:$close --> Seq:D)

Same as IO::Handle.lines. Note that a boundary between source handles is considered to be a newline break.

(my $f1 = 'foo'.IO).spurt: "foo\nbar";
(my $f2 = 'bar'.IO).spurt: 'meow';
IO::CatHandle.new($f1$f2).lines.perl.say;
# OUTPUT: «("foo", "bar", "meow").Seq␤» 

Note: if :$close is False, fully-consumed handles are still going to be closed.

method lock

Defined as:

method lock(IO::CatHandle:D: Bool:D :$non-blocking = FalseBool:D :$shared = False --> True)

Same as IO::Handle.lock. Returns Nil if the source handle queue has been exhausted.

Locks only the currently active source handle. The .on-switch Callable can be used to conveniently lock/unlock the handles as they're being processed by the CatHandle.

method native-descriptor

Defined as:

method native-descriptor(IO::CatHandle:D: --> Int:D)

Returns the native-descriptor of the currently active source handle or Nil if the source handle queue has been exhausted.

Since the CatHandle closes a source handle, once it's done with it, it's possible for successive source handles to have the same native descriptor, if they were passed to .new as Cool or IO::Path objects.

(my $f1 = 'foo'.IO).spurt: 'foo';
(my $f2 = 'bar'.IO).spurt: 'bar';
with IO::CatHandle.new: $f1$f2$*IN {
    repeat { .native-descriptor.say } while .next-handle;
    # OUTPUT: «13␤13␤9␤» 
}

method next-handle

Defined as:

method next-handle(IO::CatHandle:D: --> IO::Handle:D)

Switches the active source handle to the next handle in the source handle queue, which is the sources given in @handles attribute to .new. The return value is the currently active source handle or Nil if the source handle queue has been exhausted.

Coerces Cool source "handles" to IO::Path; opens IO::Path and unopened IO::Handle source handles for reading using the invocant's $.nl-in, $.chomp, and $.encoding attributes; those same attributes of already-opened IO::Handle objects will be changed to the values of the invocant's attributes.

This method is called automatically whenever CatHandle's methods require a switch to the next source handle, triggers .on-switch Callable to be called, and is called once during .new call. The .on-switch will continue to be triggered each time this method is called, even after the source handle queue has been exhausted. Note that generally reaching the EOF of the currently active source handle does not trigger the .next-handle call, but rather further read operations that need more data do.

(my $f1 = 'foo'.IO).spurt: "a\nb";
(my $f2 = 'bar'.IO).spurt: "c\nd";
with IO::CatHandle.new: :on-switch{ say '▸ Switching' }$f1$f2 {
    say 'one';
    .next-handle.^name.say;
    say 'two';
    .next-handle.^name.say;
    say 'three';
    .next-handle.^name.say;
    # OUTPUT: 
    # ▸ Switching 
    # one 
    # ▸ Switching 
    # IO::Handle 
    # two 
    # ▸ Switching 
    # Nil 
    # three 
    # ▸ Switching 
    # Nil 
}

method on-switch

Defined as:

has &.on-switch is rw

One of the attributes that can be set during .new call and changed later by assigning to. By default is not specified. Takes a Callable with .count of 0, 1, 2, or Inf. Gets called every time .next-handle is, which happens once during .new call and then each time a source handle is switched to the next one in the queue, or when the .next-handle method is called manually.

If the .count of &.on-switch is 0, it receives no arguments; if it's 1, it receives the currently active handle, and if it's 2 or Inf, it receives the currently active handle, and the last active handle as positional arguments (in that order). On the very first &.on-switch execution, the "last active handle" argument is Nil. Upon source handle queue exhaustion the "currently active handle" argument is Nil, and all the executions made afterwards have both arguments as Nil.

(my $f1 = 'foo'.IO).spurt: "A\nB\nC";
(my $f2 = 'bar'.IO).spurt: "D\nE";
 
my $line;
my $cat = IO::CatHandle.new: :on-switch{ $line = 1 }$f1$f2;
say "{$cat.path}:{$line++} $_" for $cat.lines;
# OUTPUT: 
# foo:1 A 
# foo:2 B 
# foo:3 C 
# bar:1 D 
# bar:2 E 
my @old-stuff;
sub on-switch ($new$old{
    $new and $new.seek: 1SeekFromBeginning;
    $old and @old-stuff.push: $old.open.slurp: :close;
}
 
(my $f1 = 'foo'.IO).spurt: "A\nB\nC";
(my $f2 = 'bar'.IO).spurt: "D\nE";
my $cat = IO::CatHandle.new: :&on-switch$f1$f2;
$cat.lines.perl.say# OUTPUT: «("", "B", "C", "", "E").Seq␤» 
@old-stuff.perl.say# OUTPUT: «["A\nB\nC", "D\nE"]␤» 

method open

Defined as:

method open(IO::CatHandle:D: --> IO::CatHandle:D)

Returns the invocant. The intent of this method is to merely make CatHandle workable with things that open IO::Handle. You never have to call this method intentionally.

method opened

Defined as:

method opened(IO::CatHandle:D: --> Bool:D)

Returns True if the invocant has any source handles, False otherwise.

say IO::CatHandle.new      .opened# OUTPUT: «False␤» 
say IO::CatHandle.new($*IN).opened# OUTPUT: «True␤» 
 
(my $f1 = 'foo'.IO).spurt: "A\nB\nC";
with IO::CatHandle.new: $f1 {
    .opened.say# OUTPUT: «True␤» 
    .slurp;
    .opened.say# OUTPUT: «False␤» 
}

method path

Defined as:

method path(IO::CatHandle:D:)

Returns the value of .path attribute of the currently active source handle, or Nil if the source handle queue has been exhausted. Basically, if your CatHandle is based on files, this is the way to get the path of the file the CatHandle is currently reading from.

(my $f1 = 'foo'.IO).spurt: "A\nB\nC";
(my $f2 = 'bar'.IO).spurt: "D\nE";
 
my $line;
my $cat = IO::CatHandle.new: :on-switch{ $line = 1 }$f1$f2;
say "{$cat.path}:{$line++} $_" for $cat.lines;
# OUTPUT: 
# foo:1 A 
# foo:2 B 
# foo:3 C 
# bar:1 D 
# bar:2 E 

method read

Defined as:

method read(IO::CatHandle:D: Int(Cool:D$bytes = 65536 --> Buf:D)

Reads up to $bytes bytes from the handle and returns them in a Buf. $bytes defaults to an implementation-specific value (in Rakudo, the value of $*DEFAULT-READ-ELEMS, which by default is set to 65536). It is permitted to call this method on handles that are not in binary mode.

(my $f1 = 'foo'.IO).spurt: 'meow';
(my $f2 = 'bar'.IO).spurt: Blob.new: 456;
with IO::CatHandle.new: :bin$f1$f2 {
    say .read: 2;    # OUTPUT: «Buf[uint8]:0x<6d 65>␤» 
    say .read: 2000# OUTPUT: «Buf[uint8]:0x<6f 77 04 05 06>␤» 
}
 
# Non-binary mode is OK too: 
with IO::CatHandle.new: $f1$f2 {
    say .get;        # OUTPUT: «meow␤» 
    say .read: 2000# OUTPUT: «Buf[uint8]:0x<04 05 06>␤» 
}

method readchars

Defined as:

method readchars(IO::CatHandle:D: Int(Cool:D$chars = 65536 --> Str:D)

Returns a Str of up to $chars characters read from the handle. $chars defaults to an implementation-specific value (in Rakudo, the value of $*DEFAULT-READ-ELEMS, which by default is set to 65536). It is NOT permitted to call this method on handles opened in binary mode and doing so will result in X::IO::BinaryMode exception being thrown.

(my $f1 = 'foo'.IO).spurt: 'Perl loves to';
(my $f2 = 'bar'.IO).spurt: ' meow';
 
with IO::CatHandle.new: $f1$f2 {
    say .readchars: 11;   # OUTPUT: «Perl loves ␤» 
    say .readchars: 1000# OUTPUT: «to meow␤» 
}

method seek

Defined as:

method seek(IO::CatHandle:D: |c)

Calls .seek on the currently active source handle, forwarding it all the arguments, and returns the result. Returns Nil if the source handle queue has been exhausted. NOTE: this method does NOT perform any source handle switching, so seeking past the end of the current source handle will NOT seek to the next source handle in the queue and seeking past the beginning of the current source handle is a fatal error. Also see .next-handle, to learn the details on when source handles are switched.

(my $f1 = 'foo'.IO).spurt: 'foo';
(my $f2 = 'bar'.IO).spurt: 'bar';
 
with IO::CatHandle.new: $f1$f2 {
    .get.say;                     # OUTPUT: «foo␤» 
    .seek: -2SeekFromCurrent;
    .readchars(2).say;            # OUTPUT: «oo␤» 
    .seek: 1000SeekFromCurrent# this doesn't switch to second handle! 
    .readchars(3).say;            # OUTPUT: «bar␤» 
    try .seek: -4;                # this won't seek to previous handle! 
    say ~$!;                      # OUTPUT: «Failed to seek in filehandle: 22␤» 
}

method tell

Defined as:

method tell(IO::CatHandle:D: --> Int:D)

Calls .tell on the currently active source handle and returns the result. Returns Nil if the source handle queue has been exhausted.

(my $f1 = 'foo'.IO).spurt: 'foo';
(my $f2 = 'bar'.IO).spurt: 'bar';
 
with IO::CatHandle.new: $f1$f2 {
    .get.say;                   # OUTPUT: «foo␤» 
    .tell.say;                  # OUTPUT: «3␤» 
    .seek: -2SeekFromCurrent;
    .tell.say;                  # OUTPUT: «1␤» 
    say .readchars: 3;          # OUTPUT: «oob␤» 
    .tell.say;                  # OUTPUT: «2␤» 
    }

method slurp

Defined as:

method slurp(IO::CatHandle:D:)

Reads all of the available input from all the source handles and returns it as a Buf if the handle is in binary mode or as a Str otherwise. Returns Nil if the source handle queue has been exhausted.

(my $f1 = 'foo'.IO).spurt: 'foo';
(my $f2 = 'bar'.IO).spurt: 'bar';
 
IO::CatHandle.new(      $f1$f2).slurp.say# OUTPUT: «foobar␤» 
IO::CatHandle.new(:bin$f1$f2).slurp.say# OUTPUT: «Buf[uint8]:0x<66 6f 6f 62 61 72>␤» 
IO::CatHandle.new                .slurp.say# OUTPUT: «Nil␤» 

method split

Defined as:

method split(IO::CatHandle:D: |args --> Seq:D)

Read the handle and processes its contents the same way Str.split does, taking the same arguments. Implementations may slurp the contents of all the source handles in their entirety when this method is called.

(my $f1 = 'foo'.IO).spurt: 'foo';
(my $f2 = 'bar'.IO).spurt: 'bar';
IO::CatHandle.new($f1$f2).split(/o+/).perl.say;
# OUTPUT: «("f", "bar").Seq␤» 

method Str

Defined as:

method Str(IO::CatHandle:D: --> Str:D)

Calls .Str on the currently active source handle and returns the result. If the source handle queue has been exhausted, returns an implementation-defined string ('<closed IO::CatHandle>' in Rakudo).

method Supply

Defined as:

method Supply(IO::CatHandle:D: :$size = 65536 --> Supply:D)

Returns a Supply fed with either .read, if the handle is in binary mode, or with .readchars, if it isn't, with reads of :$size bytes or characters. :$size defaults to an implementation-specific value (in Rakudo, the value of $*DEFAULT-READ-ELEMS, which by default is set to 65536).

(my $f1 = 'foo'.IO).spurt: 'foo';
(my $f2 = 'bar'.IO).spurt: 'bar';
react whenever IO::CatHandle.new($f1$f2).Supply: :2size {.say}
# OUTPUT: «fo␤ob␤ar␤» 
 
react whenever IO::CatHandle.new(:bin$f1$f2).Supply: :2size {.say}
# OUTPUT: «Buf[uint8]:0x<66 6f>␤Buf[uint8]:0x<6f 62>␤Buf[uint8]:0x<61 72>␤» 

method t

Defined as:

method t(IO::CatHandle:D: --> Bool:D)

Calls .t, which tells if the handle is a TTY, on the currently active source handle and returns the result. If the source handle queue has been exhausted, returns False.

(my $f1 = 'foo'.IO).spurt: 'foo';
with IO::CatHandle.new: $f1$*IN {
    repeat { .t.say } while .next-handle# OUTPUT: «False␤True␤» 
}

method unlock

Defined as:

method unlock(IO::CatHandle:D:)

Same as IO::Handle.unlock. Returns Nil if the source handle queue has been exhausted.

Unlocks only the currently active source handle. The .on-switch Callable can be used to conveniently lock/unlock the handles as they're being processed by the CatHandle.

method words

Defined as:

method words(IO::CatHandle:D: $limit = Inf:$close --> Seq:D)

Same as IO::Handle.words (including the caveat about more data read than needed to make some number of words). Note that a boundary between source handles is considered to be word boundary.

(my $f1 = 'foo'.IO).spurt: 'foo bar';
(my $f2 = 'bar'.IO).spurt: 'meow';
IO::CatHandle.new($f1$f2).words.perl.say;
# OUTPUT: «("foo", "bar", "meow").Seq␤» 

Note: if :$close is False, fully-consumed handles are still going to be closed.

NYI Methods

The IO::CatHandle type overrides these methods to throw a X::NYI exception.

method flush

Defined as:

multi method flush(|)

The IO::CatHandle type overrides this method to throw a X::NYI exception. If you have a good idea for how this method should behave, tell Rakudo developers about it!

method nl-out

Defined as:

multi method nl-out(|)

The IO::CatHandle type overrides this method to throw a X::NYI exception. If you have a good idea for how this method should behave, tell Rakudo developers about it!

method out-buffer

Defined as:

multi method out-buffer(|)

The IO::CatHandle type overrides this method to throw a X::NYI exception. If you have a good idea for how this method should behave, tell Rakudo developers about it!

method print

Defined as:

multi method print(|)

The IO::CatHandle type overrides this method to throw a X::NYI exception. If you have a good idea for how this method should behave, tell Rakudo developers about it!

method printf

Defined as:

multi method printf(|)

The IO::CatHandle type overrides this method to throw a X::NYI exception. If you have a good idea for how this method should behave, tell Rakudo developers about it!

method print-nl

Defined as:

multi method print-nl(|)

The IO::CatHandle type overrides this method to throw a X::NYI exception. If you have a good idea for how this method should behave, tell Rakudo developers about it!

method put

Defined as:

multi method put(|)

The IO::CatHandle type overrides this method to throw a X::NYI exception. If you have a good idea for how this method should behave, tell Rakudo developers about it!

method say

Defined as:

multi method say(|)

The IO::CatHandle type overrides this method to throw a X::NYI exception. If you have a good idea for how this method should behave, tell Rakudo developers about it!

method write

Defined as:

multi method write(|)

The IO::CatHandle type overrides this method to throw a X::NYI exception. If you have a good idea for how this method should behave, tell Rakudo developers about it!

method WRITE

Defined as:

multi method WRITE(|)

The IO::CatHandle type overrides this method to throw a X::NYI exception. If you have a good idea for how this method should behave, tell Rakudo developers about it!

method READ

Defined as:

multi method EOF(|)

The IO::CatHandle type overrides this method to throw a X::NYI exception. If you have a good idea for how this method should behave, tell Rakudo developers about it!

method EOF

Defined as:

multi method EOF(|)

The IO::CatHandle type overrides this method to throw a X::NYI exception. If you have a good idea for how this method should behave, tell Rakudo developers about it!

Type Graph

Type relations for IO::CatHandle
perl6-type-graph IO::CatHandle IO::CatHandle IO::Handle IO::Handle IO::CatHandle->IO::Handle Mu Mu Any Any Any->Mu IO::Handle->Any IO::ArgFiles IO::ArgFiles IO::ArgFiles->IO::CatHandle

Expand above chart

Routines supplied by class IO::Handle

IO::CatHandle inherits from class IO::Handle, which provides the following routines:

(IO::Handle) method open

Defined as:

method open(IO::Handle:D:
    :$bin:$enc:$chomp:$nl-inStr:D :$nl-out,
    Str :$mode,
    :$r:$w:$a:$x:$update:$rw:$rx:$ra,
    :$create:$append:$truncate:$exclusive,
    :$out-buffer,
    --> IO::Handle:D
)

Opens the handle in one of the modes. Fails with appropriate exception if the open fails.

See description of individual methods for the accepted values and behavior of :$chomp, :$nl-in, :$nl-out, and :$enc. The values for parameters default to the invocant's attributes and if any of them are provided, the attributes will be updated to the new values. Specify :$bin set to True instead of :$enc to indicate the handle should be opened in binary mode. Specifying undefined value as :$enc is equivalent to not specifying :$enc at all. Specifying both a defined encoding as :$enc and :$bin set to true will cause X::IO::BinaryAndEncoding exception to be thrown.

The open mode defaults to non-exclusive, read only (same as specifying :r) and can be controlled by a mix of the following arguments:

:r      same as specifying   :mode<ro>  same as specifying nothing
 
:w      same as specifying   :mode<wo>:create:truncate
:a      same as specifying   :mode<wo>:create:append
:x      same as specifying   :mode<wo>:create:exclusive
 
:update same as specifying   :mode<rw>
:rw     same as specifying   :mode<rw>:create
:ra     same as specifying   :mode<rw>:create:append
:rx     same as specifying   :mode<rw>:create:exclusive

Support for combinations of modes other than what is listed above is implementation-dependent and should be assumed unsupported. That is, specifying, for example, .open(:r :create) or .open(:mode<wo> :append :truncate) might work or might cause the Universe to implode, depending on a particular implementation. This applies to reads/writes to a handle opened in such unsupported modes as well.

The mode details are:

:mode<ro>  means "read only"
:mode<wo>  means "write only"
:mode<rw>  means "read and write"
 
:create    means the file will be createdif it does not exist
:truncate  means the file will be emptiedif it exists
:exclusive means .open will fail if the file already exists
:append    means writes will be done at the end of file's current contents

Attempts to open a directory, write to a handle opened in read-only mode or read from a handle opened in write-only mode, or using text-reading methods on a handle opened in binary mode will fail or throw.

In 6.c language, it's possible to open path '-', which will cause open to open (if closed) the $*IN handle if opening in read-only mode or to open the $*OUT handle if opening in write-only mode. All other modes in this case will result in exception being thrown.

As of 6.d language version, use path '-' is deprecated and it will be removed in future language versions entirely.

The :out-buffer controls output buffering and by default behaves as if it were Nil. See method out-buffer for details.

Note (Rakudo versions before 2017.09): Filehandles are NOT flushed or closed when they go out of scope. While they will get closed when garbage collected, garbage collection isn't guaranteed to get run. This means you should use an explicit close on handles opened for writing, to avoid data loss, and an explicit close is recommended on handles opened for reading as well, so that your program does not open too many files at the same time, triggering exceptions on further open calls.

Note (Rakudo versions 2017.09 and after): Open filehandles are automatically closed on program exit, but it is still highly recommended that you close opened handles explicitly.

(IO::Handle) method comb

Defined as:

method comb(IO::Handle:D: Bool :$close|args --> Seq:D)

Read the handle and processes its contents the same way Str.comb does, taking the same arguments, closing the handle when done if $close is set to a true value. Implementations may slurp the file in its entirety when this method is called.

Attempting to call this method when the handle is in binary mode will result in X::IO::BinaryMode exception being thrown.

my $fh = 'path/to/file'.IO.open;
say "The file has {+$fh.comb: '':close} ♥s in it";

(IO::Handle) method chomp

Defined as:

has $.chomp is rw = True

One of the attributes that can be set via .new or open. Defaults to True. Takes a Bool specifying whether the line separators (as defined by .nl-in) should be removed from content when using .get or .lines methods.

(IO::Handle) routine get

Defined as:

method get(IO::Handle:D: --> Str:D)
multi sub get (IO::Handle $fh = $*ARGFILES --> Str:D)

Reads a single line of input from the handle, removing the trailing newline characters (as set by .nl-in) if the handle's .chomp attribute is set to True. Returns Nil, if no more input is available. The subroutine form defaults to $*ARGFILES if no handle is given.

Attempting to call this method when the handle is in binary mode will result in X::IO::BinaryMode exception being thrown.

$*IN.get.say;              # Read one line from the standard input 
 
my $fh = open 'filename';
$fh.get.say;               # Read one line from a file 
$fh.close;
 
say get;                   # Read one line from $*ARGFILES 

(IO::Handle) routine getc

Defined as:

method getc(IO::Handle:D: --> Str:D)
multi sub getc (IO::Handle $fh = $*ARGFILES --> Str:D)

Reads a single character from the input stream. Attempting to call this method when the handle is in binary mode will result in X::IO::BinaryMode exception being thrown. The subroutine form defaults to $*ARGFILES if no handle is given. Returns Nil, if no more input is available, otherwise operation will block, waiting for at least one character to be available; these caveats apply:

Buffering terminals

Using getc to get a single keypress from a terminal will only work properly if you've set the terminal to "unbuffered". Otherwise the terminal will wait for the return key to be struck or the buffer to be filled up before perl6 gets even a single byte of data.

Waiting for potential combiners

If your handle's encoding allows combining characters to be read, perl6 will wait for more data to be available before it provides a character. This means that inputting an "e" followed by a combining acute will give you an e with an acute rather than giving an "e" and letting the next reading function give you a dangling combiner. However, it also means that when the user inputs just an "e" and has no intention to also input a combining acute, your program will be waiting for another keypress before the initial "e" is returned.

(IO::Handle) submethod DESTROY

Defined as:

submethod DESTROY(IO::Handle:D:)

Closes the filehandle, unless its native-descriptor is 2 or lower. This ensures the standard filehandles do not get inadvertently closed.

Note that garbage collection is not guaranteed to happen, so you must NOT rely on DESTROY for closing the handles you write to and instead close them yourself. Programs that open a lot of files should close the handles explicitly as well, regardless of whether they were open for writing, since too many files might get opened before garbage collection happens and the no longer used handles get closed.

(IO::Handle) method gist

Defined as:

method gist(IO::Handle:D: --> Str:D)

Returns a string containing information which .path, if any, the handle is created for and whether it is .opened.

say IO::Handle.new# IO::Handle<(Any)>(closed) 
say "foo".IO.open;  # IO::Handle<"foo".IO>(opened) 

(IO::Handle) method eof

Defined as:

method eof(IO::Handle:D: --> Bool:D)

Non-blocking. Returns True if the read operations have exhausted the contents of the handle. For seekable handles, this means current position is at or beyond the end of file and seeking an exhausted handle back into the file's contents will result in eof returning False again.

On non-seekable handles and handles opened to zero-size files (including special files in /proc/), EOF won't be set until a read operation fails to read any bytes. For example, in this code, the first read consumes all of the data, but it's only until the second read that reads nothing would the EOF on a TTY handle be set:

$ echo "x" | perl6 -e 'with $*IN { .read: 10000; .eof.say; .read: 10; .eof.say }'
False
True

(IO::Handle) method encoding

Defined as:

multi method encoding(IO::Handle:D: --> Str:D)
multi method encoding(IO::Handle:D: $enc --> Str:D)

Returns a Str representing the encoding currently used by the handle, defaulting to "utf8". Nil indicates the filehandle is currently in binary mode. Specifying an optional positional $enc argument switches the encoding used by the handle; specify Nil as encoding to put the handle into binary mode.

The accepted values for encoding are case-insensitive. The available encodings vary by implementation and backend. On Rakudo MoarVM the following are supported:

utf8
utf16
utf16le
utf16be
utf8-c8
iso-8859-1
windows-1251
windows-1252
windows-932
ascii

The default encoding is utf8, which undergoes normalization into Unicode NFC (normalization form canonical). In some cases you may want to ensure no normalization is done; for this you can use utf8-c8. Before using utf8-c8 please read Unicode: Filehandles and I/O for more information on utf8-c8 and NFC.

As of Rakudo 2018.04 windows-932 is also supported which is a variant of ShiftJIS.

Implementation may choose to also provide support for aliases, e.g. Rakudo allows aliases latin-1 for iso-8859-1 encoding and dashed utf versions: utf-8 and utf-16.

utf16, utf16le and utf16be

Unlike utf8, utf16 has an endianness — either big endian or little endian. This relates to the ordering of bytes. Computer CPUs also have an endianness. Raku's utf16 format specifier will use the endianness of host system when encoding. When decoding it will look for a byte order mark and if it is there use that to set the endianness. If there is no byte order mark it will assume the file uses the same endianness as the host system. A byte order mark is the codepoint U+FEFF which is ZERO WIDTH NO-BREAK SPACE. On utf16 encoded files the standard states if it exists at the start of a file it shall be interpreted as a byte order mark, not a U+FEFF codepoint.

While writing will cause a different file to be written on different endian systems, at the release of 2018.10 the byte order mark will be written out when writing a file and files created with the utf16 encoding will be able to be read on either big or little endian systems.

When using utf16be or utf16le encodings a byte order mark is not used. The endianness used is not affected by the host cpu type and is either big endian for utf16be or little endian for utf16le.

In keeping with the standard, a 0xFEFF byte at the start of a file is interpreted as a ZERO WIDTH NO-BREAK SPACE and not as a byte order mark. No byte order mark is written to files that use the utf16be or utf16le encodings.

As of Rakudo 2018.09 on MoarVM, utf16, utf16le and utf16be are supported. In 2018.10, writing to a file with utf16 will properly add a byte order mark (BOM).

Examples

with 'foo'.IO {
    .spurt: "First line is text, then:\nBinary";
    my $fh will leave {.close} = .open;
    $fh.get.say;         # OUTPUT: «First line is text, then:␤» 
    $fh.encoding: Nil;
    $fh.slurp.say;       # OUTPUT: «Buf[uint8]:0x<42 69 6e 61 72 79>␤» 
}

(IO::Handle) routine lines

Defined as:

sub          lines$what = $*ARGFILES|c)
multi method linesIO::Handle:D: $limit:$close )
multi method linesIO::Handle:D:         :$close )

The sub form, which takes $*ARGFILES by default, will apply the lines method to the object that's the first argument, and pass it the rest of the arguments.

The method will return a Seq each element of which is a line from the handle (that is chunks delineated by .nl-in). If the handle's .chomp attribute is set to True, then characters specified by .nl-in will be stripped from each line.

Reads up to $limit lines, where $limit can be a non-negative Int, Inf, or Whatever (which is interpreted to mean Inf). If :$close is set to True, will close the handle when the file ends or $limit is reached. Subroutine form defaults to $*ARGFILES, if no handle is provided.

Attempting to call this method when the handle is in binary mode will result in X::IO::BinaryMode exception being thrown.

NOTE: the lines are read lazily, so ensure the returned Seq is either fully reified or is no longer needed when you close the handle or attempt to use any other method that changes the file position.

say "The file contains ",
  '50GB-file'.IO.open.lines.grep(*.contains: 'Perl').elems,
  " lines that mention Perl";
# OUTPUT: «The file contains 72 lines that mention Perl␤» 

You can use lines in /proc/* files (from the 6.d version):

say lines"/proc/$*PID/statm".IO ); # OUTPUT: «(58455 31863 8304 2 0 29705 0)␤» 

(IO::Handle) method lock

Defined as:

method lock(IO::Handle:D: Bool:D :$non-blocking = FalseBool:D :$shared = False --> True)

Places an advisory lock on the filehandle. If :$non-blocking is True will fail with X::IO::Lock if lock could not be obtained, otherwise will block until the lock can be placed. If :$shared is True will place a shared (read) lock, otherwise will place an exclusive (write) lock. On success, returns True; fails with X::IO::Lock if lock cannot be placed (e.g. when trying to place a shared lock on a filehandle opened in write mode or trying to place an exclusive lock on a filehandle opened in read mode).

You can use lock again to replace an existing lock with another one. To remove a lock, close the filehandle or use unlock.

# One program writes, the other reads, and thanks to locks either 
# will wait for the other to finish before proceeding to read/write 
 
# Writer 
given "foo".IO.open(:w{
    .lock;
    .spurt: "I ♥ Raku!";
    .close# closing the handle unlocks it; we could also use `unlock` method for that 
}
 
# Reader 
given "foo".IO.open {
    .lock: :shared;
    .slurp.say# OUTPUT: «I ♥ Raku!␤» 
    .close;
}

(IO::Handle) method unlock

Defined as:

method unlock(IO::Handle:D: --> True)

Removes a lock from the filehandle.

(IO::Handle) routine words

Defined as:

multi sub words(IO::Handle:D $fh = $*ARGFILES$limit = Inf:$close --> Seq:D)
multi method words(IO::Handle:D: $limit = Inf:$close --> Seq:D)

Similar to Str.words, separates the handle's stream on contiguous chunks of whitespace (as defined by Unicode) and returns a Seq of the resultant "words." Takes an optional $limit argument that can be a non-negative Int, Inf, or Whatever (which is interpreted to mean Inf), to indicate only up-to $limit words must be returned. If Bool :$close named argument is set to True, will automatically close the handle when the returned Seq is exhausted. Subroutine form defaults to $*ARGFILES, if no handle is provided.

Attempting to call this method when the handle is in binary mode will result in X::IO::BinaryMode exception being thrown.

my %dict := bag $*IN.words;
say "Most common words: "%dict.sort(-*.value).head: 5;

NOTE: implementations may read more data than necessary when a call to .words is made. That is, $handle.words(2) may read more data than two "words" worth of data and subsequent calls to read methods might not read from the place right after the two fetched words. After a call to .words, the file position should be treated as undefined.

(IO::Handle) method split

Defined as:

method split(IO::Handle:D: :$close|c)

Slurps the handle's content and calls Str.split on it, forwarding any of the given arguments. If :$close named parameter is set to True, will close the invocant after slurping.

Attempting to call this method when the handle is in binary mode will result in X::IO::BinaryMode exception being thrown.

my $fh = 'path/to/file'.IO.open;
$fh.split: '':close# Returns file content split on ♥ 

(IO::Handle) method spurt

Defined as:

multi method spurt(IO::Handle:D: Blob $data:$close = False)
multi method spurt(IO::Handle:D: Cool $data:$close = False)

Writes all of the $data into the filehandle, closing it when finished, if $close is True. For Cool $data, will use the encoding the handle is set to use (IO::Handle.open or IO::Handle.encoding).

Behavior for spurting a Cool when the handle is in binary mode or spurting a Blob when the handle is NOT in binary mode is undefined.

(IO::Handle) method print

Defined as:

multi method print(**@text --> True)
multi method print(Junction:D --> True)

Writes the given @text to the handle, coercing any non-Str objects to Str by calling .Str method on them. Junction arguments autothread and the order of printed strings is not guaranteed. See write to write bytes.

Attempting to call this method when the handle is in binary mode will result in X::IO::BinaryMode exception being thrown.

my $fh = 'path/to/file'.IO.open: :w;
$fh.print: 'some text';
$fh.close;

(IO::Handle) method print-nl

Defined as:

method print-nl(IO::Handle:D: --> True)

Writes the value of $.nl-out attribute into the handle. This attribute, by default, is , but see the page on newline for the rules it follows in different platforms and environments.

Attempting to call this method when the handle is in binary mode will result in X::IO::BinaryMode exception being thrown.

my $fh = 'path/to/file'.IO.open: :w:nl-out("\r\n");
$fh.print: "some text";
$fh.print-nl# prints \r\n 
$fh.close;

(IO::Handle) method printf

Defined as:

multi method printf(IO::Handle:D: Cool $format*@args)

Formats a string based on the given format and arguments and .prints the result into the filehandle. See sub sprintf for details on acceptable format directives.

Attempting to call this method when the handle is in binary mode will result in X::IO::BinaryMode exception being thrown.

my $fh = open 'path/to/file':w;
$fh.printf: "The value is %d\n"32;
$fh.close;

(IO::Handle) method out-buffer

Defined as:

method out-buffer(--> Int:Dis rw

Controls output buffering and can be set via an argument to open. Takes an int as the size of the buffer to use (zero is acceptable). Can take a Bool: True means to use default, implementation-defined buffer size; False means to disable buffering (equivalent to using 0 as buffer size).

Lastly, can take a Nil to enable TTY-based buffering control: if the handle is a TTY, the buffering is disabled, otherwise, default, implementation-defined buffer size is used.

See flush to write out data currently in the buffer. Changing buffer size flushes the filehandle.

given 'foo'.IO.open: :w:1000out-buffer {
    .say: 'Hello world!'# buffered 
    .out-buffer = 42;       # buffer resized; previous print flushed 
    .say: 'And goodbye';
    .close# closing the handle flushes the buffer 
}

(IO::Handle) method put

Defined as:

multi method put(**@text --> True)
multi method put(Junction:D --> True)

Writes the given @text to the handle, coercing any non-Str objects to Str by calling .Str method on them, and appending the value of .nl-out at the end. Junction arguments autothread and the order of printed strings is not guaranteed.

Attempting to call this method when the handle is in binary mode will result in X::IO::BinaryMode exception being thrown.

my $fh = 'path/to/file'.IO.open: :w;
$fh.put: 'some text';
$fh.close;

(IO::Handle) method say

Defined as:

multi method say(IO::Handle:D: **@text --> True)

This method is identical to put except that it stringifies its arguments by calling .gist instead of .Str.

Attempting to call this method when the handle is in binary mode will result in X::IO::BinaryMode exception being thrown.

my $fh = open 'path/to/file':w;
$fh.say(Complex.new(34));        # RESULT: «3+4i\n» 
$fh.close;

(IO::Handle) method read

Defined as:

method read(IO::Handle:D: Int(Cool:D$bytes = 65536 --> Buf:D)

Binary reading; reads and returns up to $bytes bytes from the filehandle. $bytes defaults to an implementation-specific value (in Rakudo, the value of $*DEFAULT-READ-ELEMS, which by default is set to 65536). This method can be called even when the handle is not in binary mode.

(my $file = 'foo'.IO).spurt: 'I ♥ Perl';
given $file.open {
    say .read: 6# OUTPUT: «Buf[uint8]:0x<49 20 e2 99 a5 20>␤» 
    .close;
}

(IO::Handle) method readchars

Defined as:

method readchars(IO::Handle:D: Int(Cool:D$chars = 65536 --> Str:D)

Reading chars; reads and returns up to $chars chars (graphemes) from the filehandle. $chars defaults to an implementation-specific value (in Rakudo, the value of $*DEFAULT-READ-ELEMS, which by default is set to 65536). Attempting to call this method when the handle is in binary mode will result in X::IO::BinaryMode exception being thrown.

(my $file = 'foo'.IO).spurt: 'I ♥ Perl';
given $file.open {
    say .readchars: 5# OUTPUT: «I ♥ P␤» 
    .close;
}

(IO::Handle) method write

Defined as:

method write(IO::Handle:D: Blob:D $buf --> True)

Writes $buf to the filehandle. This method can be called even when the handle is not in binary mode.

(IO::Handle) method seek

Defined as:

method seek(IO::Handle:D: Int:D $offsetSeekType:D $whence --> True)

Move the file pointer (that is, the position at which any subsequent read or write operations will begin) to the byte position specified by $offset relative to the location specified by $whence which may be one of:

  • SeekFromBeginning: The beginning of the file.

  • SeekFromCurrent: The current position in the file.

  • SeekFromEnd: The end of the file. Please note that you need to specify a negative offset if you want to position before the end of the file.

  • (IO::Handle) method tell

    Defined as:

    method tell(IO::Handle:D: --> Int:D)

    Return the current position of the file pointer in bytes.

    (IO::Handle) method slurp-rest

    Defined as:

    multi method slurp-rest(IO::Handle:D: :$bin! --> Buf)
    multi method slurp-rest(IO::Handle:D: :$enc --> Str)

    DEPRECATION NOTICE: this method is deprecated in the 6.d version. Do not use it for new code, use .slurp method instead.

    Returns the remaining content of the file from the current file position (which may have been set by previous reads or by seek.) If the adverb :bin is provided a Buf will be returned; otherwise the return will be a Str with the optional encoding :enc.

    (IO::Handle) method slurp

    Defined as:

    method slurp(IO::Handle:D: :$close:$bin)

    Returns all the content from the current file pointer to the end. If the invocant is in binary mode or if $bin is set to True, will return a Buf, otherwise will decode the content using invocant's current .encoding and return a Str.

    If :$close is set to True, will close the handle when finished reading.

    Note: On Rakudo this method was introduced with release 2017.04; $bin arg was added in 2017.10.

    (IO::Handle) method Supply

    Defined as:

    multi method Supply(IO::Handle:D: :$size = 65536)

    Returns a Supply that will emit the contents of the handle in chunks. The chunks will be Buf if the handle is in binary mode or, if it isn't, Str decoded using same encoding as IO::Handle.encoding.

    The size of the chunks is determined by the optional :size named parameter and 65536 bytes in binary mode or 65536 characters in non-binary mode.

    "foo".IO.open(:bin).Supply(:size<10>).tap: *.perl.say;
    # OUTPUT: 
    # Buf[uint8].new(73,32,226,153,165,32,80,101,114,108) 
    # Buf[uint8].new(32,54,33,10) 
     
    "foo".IO.open.Supply(:size<10>).tap: *.perl.say;
    # OUTPUT: 
    # "I ♥ Perl" 
    # " 6!\n" 

    (IO::Handle) method path

    Defined as:

    method path(IO::Handle:D:)

    For a handle opened on a file this returns the IO::Path that represents the file. For the standard I/O handles $*IN, $*OUT, and $*ERR it returns an IO::Special object.

    (IO::Handle) method IO

    Defined as:

    method IO(IO::Handle:D:)

    Alias for .path

    (IO::Handle) method Str

    Returns the value of .path, coerced to Str.

    say "foo".IO.open.Str# OUTPUT: «foo␤» 

    (IO::Handle) routine close

    Defined as:

    method close(IO::Handle:D: --> Bool:D)
    multi sub close(IO::Handle $fh)

    Closes an open filehandle, returning True on success, and not erroring if the filehandle is already closed, although if you close one of the standard filehandles (by default: $*IN, $*OUT, or $*ERR), that is any handle with native-descriptor 2 or lower, you won't be able to re-open them.

    given "foo/bar".IO.open(:w{
        .spurt: "I ♥ Raku!";
        .close;
    }

    It's a common idiom to use LEAVE phaser for closing the handles, which ensures the handle is closed regardless of how the block is left.

    do {
        my $fh = open "path-to-file";
        LEAVE close $fh;
        # ... do stuff with the file 
    }
     
    sub do-stuff-with-the-file (IO $path-to-file{
      my $fh = $path-to-file.open;
     
      # stick a `try` on it, since this will get run even when the sub is 
      # called with wrong arguments, in which case the `$fh` will be an `Any` 
      LEAVE try close $fh;
     
      # ... do stuff with the file 
    }

    Note: unlike some other languages, Raku does not use reference counting, and so the filehandles are NOT closed when they go out of scope. While they will get closed when garbage collected, garbage collection isn't guaranteed to get run. This means you must use an explicit close on handles opened for writing, to avoid data loss, and an explicit close is recommended on handles opened for reading as well, so that your program does not open too many files at the same time, triggering exceptions on further open calls.

    Note several methods allow for providing :close argument, to close the handle after the operation invoked by the method completes. As a simpler alternative, the IO::Path type provides many reading and writing methods that let you work with files without dealing with filehandles directly.

    (IO::Handle) method flush

    Defined as:

    method flush(IO::Handle:D: --> True)

    Will flush the handle, writing any of the buffered data. Returns True on success; otherwise, fails with X::IO::Flush.

    given "foo".IO.open: :w {
        LEAVE .close;
        .print: 'something';
        'foo'.IO.slurp.say# (if the data got buffered) OUTPUT: «␤» 
        .flush;             # flush the handle 
        'foo'.IO.slurp.say# OUTPUT: «something␤» 
    }

    (IO::Handle) method native-descriptor

    Defined as:

    method native-descriptor()

    This returns a value that the operating system would understand as a "file descriptor" and is suitable for passing to a native function that requires a file descriptor as an argument such as fcntl or ioctl.

    (IO::Handle) method nl-in

    Defined as:

    method nl-in(--> Str:Dis rw

    One of the attributes that can be set via .new or open. Defaults to ["\x0A", "\r\n"]. Takes either a Str or Array of Str specifying input line ending(s) for this handle. If .chomp attribute is set to True, will strip these endings in routines that chomp, such as get and lines.

    with 'test'.IO {
        .spurt: '1foo2bar3foo'# write some data into our test file 
        my $fh will leave {.close} = .open# can also set .nl-in via .open arg 
        $fh.nl-in = [<foo bar>]; # set two possible line endings to use; 
        $fh.lines.say# OUTPUT: ("1", "2", "3").Seq 
    }

    (IO::Handle) method nl-out

    Defined as:

    has Str:D $.nl-out is rw = "\n";

    One of the attributes that can be set via .new or open. Defaults to "\n". Takes a Str specifying output line ending for this handle, to be used by methods .put and .say.

    with 'test'.IO {
        given .open: :w {
            .put: 42;
            .nl-out = 'foo';
            .put: 42;
            .close;
        }
        .slurp.perl.say# OUTPUT: «"42\n42foo"» 
    }

    (IO::Handle) method opened

    Defined as:

    method opened(IO::Handle:D: --> Bool:D)

    Returns True if the handle is open, False otherwise.

    (IO::Handle) method t

    Defined as:

    method t(IO::Handle:D: --> Bool:D)

    Returns True if the handle is opened to a TTY, False otherwise.

    (IO::Handle) method WRITE

    Defined as:

    method WRITE(IO::Handle:D: Blob:D \data --> Bool:D)

    Called whenever a write operation is performed on the handle. Always receives the data as a Blob, even if a textual writing method has been called.

    class IO::Store is IO::Handle {
        has @.lines = [];
     
        submethod TWEAK {
            self.encoding: 'utf8'# set up encoder/decoder 
        }
     
        method WRITE(IO::Handle:D: Blob:D \data --> Bool:D{
            @!lines.push: data.decode();
            True;
        }
     
        method gist() {
            return @!lines.join("\n" );
        }
    }
    my $store = IO::Store.new();
    my $output = $PROCESS::OUT;
    $PROCESS::OUT = $store;
    .say for <one two three>;
    $PROCESS::OUT = $output;
    say $store.lines(); # OUTPUT «[one␤ two␤ three␤]» 

    In this example we are creating a simple WRITE redirection which stores anything written to the filehandle to an array. Se need to save the standard output first, which we do in $output, and then everything that is printed or said (through say) gets stored in the defined IO::Store class. Two things should be taken into account in this class. By default, IO::Handles are in binary mode, so we need to TWEAK the objects if we want them to work with text. Second, a WRITE operation should return True if successful. It will fail if it does not.

    (IO::Handle) method READ

    Defined as:

    method READ(IO::Handle:D: Int:D \bytes --> Buf:D)

    Called whenever a read operation is performed on the handle. Receives the number of bytes requested to read. Returns a Buf with those bytes which can be used to either fill the decoder buffer or returned from reading methods directly. The result is allowed to have fewer than the requested number of bytes, including no bytes at all.

    If you provide your own .READ, you very likely need to provide your own .EOF as well, for all the features to behave correctly.

    The compiler may call .EOF method any number of times during a read operation to ascertain whether a call to .READ should be made. More bytes than necessary to satisfy a read operation may be requested from .READ, in which case the extra data may be buffered by the IO::Handle or the decoder it's using, to fulfill any subsequent reading operations, without necessarily having to make another .READ call.

    class IO::Store is IO::Handle {
        has @.lines = [];
     
        submethod TWEAK {
          self.encoding: 'utf8'# set up encoder/decoder 
        }
     
        method WRITE(IO::Handle:D: Blob:D \data --> Bool:D{
          @!lines.push: data;
          True;
        }
     
        method whole() {
          my Buf $everything = Buf.new();
          for @!lines -> $b {
            $everything ~= $b;
          }
          return $everything;
        }
     
        method READ(IO::Handle:D: Int:D \bytes --> Buf:D{
          my Buf $everything := self.whole();
          return $everything;
        }
     
        method EOF {
          my $everything = self.whole();
          !$everything;
        }
    }
     
    my $store := IO::Store.new();
     
    $store.print$_ ) for <one two three>;
    say $store.read(3).decode# OUTPUT «one␤» 
    say $store.read(3).decode# OUTPUT «two␤» 

    In this case, we have programmed the two READ and EOF methods, as well as WRITE, which stores every line in an element in an array. The read method actually calls READ, returning 3 bytes, which correspond to the three characters in the first two elements. Please note that it's the IO::Handle base class the one that is taking care of cursor, since READ just provides a handle into the whole content of the object; the base class will READ 1024 * 1024 bytes at a time. If your object is planned to hold an amount of bytes bigger than that, you will have to handle an internal cursor yourself. That is why in this example we don't actually use the bytes argument.

    (IO::Handle) method EOF

    Defined as:

    method EOF(IO::Handle:D: --> Bool:D)

    Indicates whether "end of file" has been reached for the data source of the handle; i.e. no more data can be obtained by calling .READ method. Note that this is not the same as eof method, which will return True only if .EOF returns True and all the decoder buffers, if any were used by the handle, are also empty. See .READ for an example implementation.