Skip to content

Latest commit

 

History

History
469 lines (353 loc) · 17.1 KB

S16-io.pod

File metadata and controls

469 lines (353 loc) · 17.1 KB

TITLE

Synopsis 16: I/O

VERSION

Created: 12 Sep 2006

Last Modified: 5 Nov 2014
Version: 28

Many of these functions will work as in Perl 5, except we're trying to rationalize everything into roles. For now you can assume most of the important functions will automatically be in the * namespace. However, with IO operations in particular, many of them are really methods on an IO handle, and if there is a corresponding global function, it's merely an exported version of the method.

IO

Overridable IO handles

In Perl 6, there are the standard IO handles, and any number of overriding inner filehandles for the same symbol.

The standard handles are our old familiar friends (with new names). Standard input changed from STDIN to $*IN, standard output changed from STDOUT to $*OUT, and standard error changed from STDERR to $*ERR. In Perl 6 these symbols represent more of a concept than a given filehandle, since the meaning is contextually determined. The process's version of these handles live in the PROCESS:: namespace, which is more global than the per-interpreter GLOBAL:: namespace.

When no explicit filehandle is used, the standard IO operators are defined in terms of the dynamic variables. So the say function prints to $*OUT, while note prints to $*ERR. The lines() term inputs from $*ARGFILES which defaults to $*IN in the absence of any filenames. So any given dynamic scope (interpreter, thread, function or method call) may redefine the current meaning of any of those filehandles within the dynamic scope of itself and of its called routines.

So to put it another way, when you write something like

say "Howdy, world!"

the say function looks for the current meaning of $*OUT, and takes the closest definition it can find in its callers. If none of the callers have overridden the definition, it looks in the interpreter's GLOBAL namespace. If the interpreter hasn't overridden the meaning, it takes the meaning from PROCESS. In essence, any dynamic scope in Perl 6 is allowed to do IO redirection much like a Unix shell does with its subprocesses, albeit with a different syntax:

{
    my $*OUT will leave *.close = open $newfile, :w;
    say "Written to $newfile";
}
# stdout reverts to outer scope's definition, and closed the file

In short:

default handle
    routine   for sub form    purpose
    =======   ===========     =======
    print     $*OUT           string-based writing
    say       $*OUT           string-based writing
    get       $*ARGFILES      read a line (Str)
    lines     $*ARGFILES      read all lines (Str)
    words     $*ARGFILES      read all words (Str)
    read                      binary reading (Buf)
    write                     binary writing (Buf)

Path Names and the .IO coercer

Path names are just strings (Str). Methods that return path names, will just return strings. As soon as you need to do manipulation on the path name (e.g. to find out its basename or extension), you can create an IO::Path object out of the string by applying the .IO coercer:

my $path = $filename.IO;

Then you can use any of the IO::Path methods, such as open:

my $handle = $newfile.IO.open(:w);

Note that the open() sub, is just really syntactic sugar for the above:

my $handle = open $newfile, :w;

$*SPEC

The current system's path semantics are encapsulated in $*SPEC dynamic variable. It adheres to the IO::Spec interface, and is automatically initialized for the current environment. But like any dynamic variable, can be overridden in a scope:

{ # Win32 path semantics in here
    my $*SPEC = IO::Spec::Win32;
    ...  # your code
}
# original path semantics here again

Please note that it does not need to be an instantiated object: the standard IO::Spec subclasses only provide class methods, and therefore do not need an instantiated object. But that could be different for a very specific third-party implementation of an IO::Spec class.

$*CWD and chdir()

The dynamic variable $*CWD is an IO::Dir object representing the current working directory. It is normally set with the chdir() function, which will check whether the specified path exists as a directory and is accessible (-x).

chdir($dir);  # sets $*CWD of scope, usually PROCESS::<$CWD>

The chdir() function returns a X::IO::Chdir Failure if the path does not exist, or is not a directory, or is not accessible. Otherwise returns a newly created IO::Dir object (which will be True).

Please note that the path in $*CWD does not have any bearing on what the underlying operating system's concept of a "current directory". It is simply the path that will prepended before any implicit or explicit relative paths, and the default path that will be used when executing a sub-process.

To be changing $*CWD just for a given scope, you can use indir():

indir $dir, {
    ... your code in $dir ...
};
... your code in $*CWD again ...

or you can use chdir() with a temporary $*CWD:

{
    temp $*CWD = chddir($dir);
    ... your code in $dir ...
}
... your code in $*CWD again ...

$*TMPDIR and tmpdir()

The dynamic variable $*TMPDIR is an IO::Dir object which points to the system's directory for temporary files. It can be set with the tmpdir() function which will check whether the specified path exists as a directory and has complete access (+rwx).

tmpdir($dir);  # sets $*TMPDIR of scope, usually PROCESS::<$TMPDIR>

To set a locally scoped version of $*TMPDIR, you can use tmpdir() with a temporary $*TMPDIR:

{
    temp $*TMPDIR = $tmpdir($dir);
    ... your code with $*TMPDIR being $dir ...
}
... your code in original $*TMPDIR again ...

It will return a newly created IO::Dir object (which is True) or an appropriate Failure.

The initialization of $*TMPDIR at startup is set depending on the OS you're on.

$*HOME and homedir()

The dynamic variable $*HOME is an IO::Dir object which points to the user's home directory. It can be set with the homedir() function which will check whether the specified path exists as a directory and is completely accessible (+rwx).

homedir($dir);  # sets $*HOME of scope, usually PROCESS::<$HOME>

To set a locally scoped version of $*HOME, you can use homedir() with a temporary $*HOME:

{
    temp $*HOME = homedir($dir);
    ... your code with $*HOME being $dir ...
}
... your code in original $*HOME again ...

It will return a newly created IO::Dir object (which is True) or an appropriate Failure.

The initialization of $*HOME at startup is set depending on the OS you're on.

System dependent path semantics and IO::Spec

Each time an IO::Path object is created, the current $*SPEC will be encapsulated in the object, to be used for all path related operations.

Of course, it is also possible to specify a specify a specific system's path semantics module when creating an IO::Path object with the :SPEC named parameter:

my $SPEC = IO::Spec::Win32;
my $path = $fileonNTFS.IO(:$SPEC);

or:

my $path = $fileonNTFS.IO(:SPEC<Win32>); # auto-expand to IO::Spec::Win32

Functions and Classes

The functions and classes that define most of the functionality for IO are more thoroughly defined in S32-setting-library/IO.pod. The main functions used are listed in S29 with references to S32-setting-library/IO.pod. An overview:

Functions

print(@text)                  # print text on $*OUT
say(@text)                    # print text + newline on $*OUT
note(@text)                   # print text + newline on $*ERR
dd($a,$b,$c)                  # tiny data dumper on $*ERR
$line = prompt($message)      # print message on $*OUT, obtain next line

$handle = open($path)         # open a file, return IO::Handle

@paths = dir                  # paths (as IO::Path) in $*CWD
@paths = dir($dir)            # paths (as IO::Path) in $dir

$contents = slurp($handle)    # read all that's left of an opened filehandle
$contents = slurp($filename)  # read all from given filename

spurt($handle,$contents)      # write $contents to $handle
spurt($filename,$contents)    # write $contents to $filename

mkdir($dir)                   # create a directory
rmdir($dir)                   # remove a directory
mkpath($path)                 # create directory and parents as appropriate

chdir($dir)                   # set $*CWD
temp $*CWD = chdir($dir)      # set $*CWD for the current scope

indir($dir, { ... })          # execute code with temporary $*CWD
    ...
};

tmpdir($dir)                  # set $*TMPDIR
temp $*TMPDIR = tmpdir($dir)  # set $*TMPDIR for the current scope

homedir($dir)                 # set $*HOME
temp $*HOME = homedir($dir)   # set $*HOME for the current scope

copy($from,$to)               # copy a file
rename($from,$to)             # rename (move) a file on same physical storage
move($from,$to)               # move (rename) a file to other storage
unlink(*@files)               # remove one or more files
chmod($permission,*@files)    # change permissions of one or more files

link($target,$source)         # create a hard-link to a file
symlink($target,$source)      # create a symbolic link to a file

IO::Spec Class

The IO::Spec itself only has one method: select. It takes an OS descriptive name (usually something like what $*DISTRO.name gives) and returns the type object of the appropriate IO::Spec subclass.

my $*SPEC = IO::Spec.select("MSWin32");  # gives IO::Spec::Win32

Such a subclass should provide at least the following methods (in alphabetical order):

abs2rel        convert an absolute path into a relative one
canonpath      return a canonical version of the given path
catdir         concatenate directories
catpath        create a path from volume/directories/filename
curdir         the path to the current directory (usually '.')
curupdir       test for matching curdir|updir
devnull        the path to the bit bucket (on Unixy systems '/dev/null')
extension      the extension of the path
is-absolute    whether the path is absolute
join           create a path from hash with volume/directories/filename
PATH           %ENV<PATH> interpreted as paths
rel2abs        convert a relative path into an absolute one
rootdir        the path to the root directory (on Unixy systems '/')
split          split a path into volume/directories/filename in hash
splitdir       split directories
splitpath      split a path into volume/directories/filename as list
tmpdir         path of the first writeable directory for temporary files
updir          the path to the path directory (usually '..')

IO::Path Class

class IO::Path is Cool { }

The official way to create an IO::Path object is with the new method. Apart from the path positional, it also takes optional :SPEC and CWD named parameters. The .IO coercer (which takes the same parameters as .new) is the syntactic sugar that will most likely be used most often.

my $io = $filename.IO;                  # current $*SPEC/$*CWD
my $io = $filename.IO(:SPEC(*$SPEC));   # specific IO::SPEC
my $io = $filename.IO(:SPEC(*$SPEC), :CWD($*CWD));

which would be the same as:

my $io = IO::Path.new($filename);
my $io = IO::Path.new($filename, :SPEC(*$SPEC));
my $io = IO::Path.new($filename, :SPEC(*$SPEC), :CWD($*CWD));

If you only have filename components to start with, you can also create an IO::Path object with the :volume, :directory and :basename named parameters:

my $io = IO::Path.new( :$volume, :$directory, :$basename );

The following file test methods are provided:

r          is readable by effective uid/gid
w          is writable by effective uid/gid
x          is executable by effective uid/gid
o          is owned by effective uid

R          is readable by real uid/gid
W          is writable by real uid/gid
X          is executable by real uid/gid
O          is owned by real uid

e          exists
s          Size of the $!path of $io in bytes
z          has zero size (an empty file)

f          is a plain file
d          is a directory
l          is a symbolic link
L          path of symbolic link (readlink)
p          is a named pipe (FIFO)
S          is a socket
b          is a block special file
c          is a character special file

u          has setuid bit set
g          has setgid bit set
k          has sticky bit set

To allow for easy chaining of file tests, there is an .all method that can be fed the tests to be tried as a list of strings. The value returned will be the first non-True value, or the final True value.

say "rwx" if $io.all: <r w x>;

if $io.all(<f r w x s>) -> $size {
    say "plain file with rwx of $size bytes";
}

This is mostly handy when passing file tests as parameters between routines and methods. From a performance point of view, direct use of the methods, like:

if $io.f && $io.r && $io.w && $io.x && $io.s -> $size {
    say "plain file with rwx of $size bytes";
}

or the smart match method:

given $io {
    when :f :r :w :x {
        say "plain file with rwx of $_.s() bytes";
    }
}

is probably faster.

These other methods are also provided (in alphabetical order):

absolute       the absolute, canonical path
accessed       last access time (if available)
basename       the basename of the path
changed        last (metadata) changed time
chdir          change $*CWD if directory
child          append basename to path, return new object for that
chmod          change attributes of path
copy           create a copy of file
dir            files in path (if dir)
directory      the directory part of the absolute path
extension      the extension of the file
is-absolute    is the (original) path absolute
is-relative    is the (original) path relative
lines          contents of file as lines
mkdir          create directory
modified       last modified time
move           move (rename) to other storage
open           attempt to open file, return IO::Handle
parent         remove last portion of path, return new object for that
pipe           attempt to open a pipe, return IO::Pipe
pred           previous logical path, return new object for that
relative       the relative path against CWD
rename         rename (move) to other name
resolve        follow symlinks to the real path, return new object for that
rmdir          remove directory if empty directory
slurp          obtain the contents of the file
SPEC           the :SPEC at instantiation time
spurt          set the contents of the file
succ           next logical path, return new object for that
unlink         remove file
volume         the volume of the path (if any)
words          contents of file as words

IO::Handle Class

class IO::Handle does IO { }

The IO::Handle object is usually not directly instantiated, but with open() or IO::Path.open. Nonetheless, you can create an IO::Handle object with just a path:

my $handle = IO::Handle.new($filename);
my $handle = IO::Handle.new($filename, :SPEC(*$SPEC));
my $handle = IO::Handle.new($filename, :SPEC(*$SPEC), :CWD($*CWD));

This does not interact with anything at all and will appear as if the file has been .closed. The .open method does interact with the file system:

$handle.open;  # same as $handle = $filename.IO.open

It either returns True, or a Failure with additional information.

The other methods of the IO::Handle class are only valid after the .open has been called successfully:

close          close file handle, flush buffers
encoding       set/return encoding of file handle
eof            file pointer reached end of file
fileno         file descriptor (usually a native integer)
flush          flush buffers
get            get next line from file
getc           get next character from file
ins            number of lines read
IO             return new IO::Path of path of file
lines          return rest of contents of file as lines
opened         is the file open?
p              the handle is a pipe
path           the IO::Path of path of file, handles file tests
print          write characters to file
read           read bytes from file
say            write characters + newline to file
seek           move file pointer to given position
slurp          return rest of contents of file
spurt          write / append contents to file
t              is the file a TTY (as a person looking?)
tell           return position of file pointer
words          return rest of contents of file as words
write          write bytes to file

Interplay between Roles and Classes

These classes and roles may cache and share pertinent information for better performance.

AUTHORS

Largely, the authors of the related Perl 5 docs.
Larry Wall <larry@wall.org>
Mark Stosberg <mark@summersault.com>
Tim Nelson <wayland@wayland.id.au>
Daniel Ruoso <daniel@ruoso.com>
Elizabeth Mattijsen <liz@wenzperl.nl>