DRAFT: Synopsis 16: IPC / IO / Signals
Author: Largely, the authors of the related Perl 5 docs. Maintainer: Larry Wall <larry@wall.org> Contributions: Mark Stosberg <mark@summersault.com> Date: 12 Sep 2006 Last Modified: 1 May 2007 Version: 17
This is a draft document. Many of these functions will work as in Perl 5, except we're trying to rationalize everything into packages. 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.
As a starting point, you can help by finding the official Perl 5 documentation for these functions and copying it here.
$file.:X $file ~~ :X
A file test, where X is one of the letters listed below. This unary operator takes one argument, either a filename or a filehandle, and tests the associated file to see if something is true about it.
A Pair used as a pattern is treated as a file test.
:r File is readable by effective uid/gid.
:w File is writable by effective uid/gid.
:x File is executable by effective uid/gid.
:o File is owned by effective uid.
:R File is readable by real uid/gid.
:W File is writable by real uid/gid.
:X File is executable by real uid/gid.
:O File is owned by real uid.
:e File exists.
:z File has zero size (is empty).
:s File has nonzero size (returns size in bytes).
:f File is a plain file.
:d File is a directory.
:l File is a symbolic link.
:p File is a named pipe (FIFO), or Filehandle is a pipe.
:S File is a socket.
:b File is a block special file.
:c File is a character special file.
:t Filehandle is opened to a tty.
:u File has setuid bit set.
:g File has setgid bit set.
:k File has sticky bit set.
:T File is an ASCII text file (heuristic guess).
:B File is a "binary" file (opposite of :T).
:M Script start time minus file modification time, in days.
:A Same for access time.
:C Same for inode change time (Unix, may differ for other platforms)
The interpretation of the file permission operators :r, :R, :w, :W, :x, and :X is by default based solely on the mode of the file and the uids and gids of the user. There may be other reasons you can't actually read, write, or execute the file. Such reasons may be for example network filesystem access controls, ACLs (access control lists), read-only filesystems, and unrecognized executable formats.
Also note that, for the superuser on the local filesystems, the :r, :R, :w, and :W tests always return 1, and :x and :X return 1 if any execute bit is set in the mode. Scripts run by the superuser may thus need to do a stat() to determine the actual mode of the file, or temporarily set their effective uid to something else.
If you are using ACLs, there is a pragma called filetest that may produce more accurate results than the bare stat() mode bits. When under the use filetest 'access' the above-mentioned filetests will test whether the permission can (not) be granted using the access() family of system calls. Also note that the :x and :X may under this pragma return true even if there are no execute permission bits set (nor any extra execute permission ACLs). This strangeness is due to the underlying system calls' definitions. Read the documentation for the filetest pragma for more information.
The :T and :B switches work as follows. The first block or so of the file is examined for odd characters such as strange control codes or characters with the high bit set. If too many strange characters (>30%) are found, it's a :B file; otherwise it's a :T file. Also, any file containing null in the first block is considered a binary file. If :T or :B is used on a filehandle, the current IO buffer is examined rather than the first block. Both :T and :B return true on a null file, or a file at EOF when testing a filehandle. Because you have to read a file to do the :T test, on most occasions you want to use a :f against the file first, as in next unless $file ~~ :f && $file ~~ :T .
You can test multiple features using junctions:
if -$filename ~~ :r & :w & :x {...}
Or pass multiple tests together in OO style:
if $filename.TEST(:e,:x) {...}
our Int multi chown ($uid = -1, $gid = -1, *@files)
Changes the owner (and group) of a list of files. The first two elements of the list must be the numeric uid and gid, in that order. A value of -1 in either position is interpreted by most systems to leave that value unchanged. Returns the number of files successfully changed.
$count = chown $uid, $gid, ’foo’, ’bar’;
chown $uid, $gid, @filenames;
On systems that support fchown, you might pass file handles among the files. On systems that don’t support fchown, passing file handles produces a fatal error at run time.
Here’s an example that looks up nonnumeric uids in the passwd file:
$user = prompt "User: "; $pattern = prompt "Files: ";
($login,$pass,$uid,$gid) = getpwnam($user)
or die "$user not in passwd file";
@ary = glob($pattern); # expand filenames chown $uid, $gid, @ary;
On most systems, you are not allowed to change the ownership of the file unless you’re the superuser, although you should be able to change the group to any of your secondary groups. On insecure systems, these restrictions may be relaxed, but this is not a portable assumption. On POSIX systems, you can detect this condition this way:
use POSIX qw(sysconf _PC_CHOWN_RESTRICTED);
$can_chown_giveaway = not sysconf(_PC_CHOWN_RESTRICTED);
From t/spec/S16-filehandles/chmod.t lines 4–102 (no results): (skip)
| # L<S16/"Filehandles, files, and directories"/"chmod">
|
|
|
| =begin pod
|
|
|
| chmod - the unix chmod command, changing the rights on a file
|
|
|
| Proposed behaviour
|
| LIST = chmod MODE, LIST
|
| Given a list of files and directories change the rights on them.
|
| MODE should be an octet representing or a string like similar to what can be used in
|
| the same UNIX program:
|
| one or more of the letters ugoa, one of the symbols +-= and one or more of the letters rwxXstugo.
|
|
|
| return list should be the list of files that were successfully changed
|
| in scalar context it should be the number of files successfully changed
|
|
|
| While some of the modes are UNIX specific, it would be nice to find similar
|
| modes in other operating system and do the right thing there too.
|
|
|
|
|
| We really need the stat() function in order to test this.
|
|
|
| =end pod
|
|
|
| plan 20;
|
|
|
| if $*OS eq "browser" {
|
| skip_rest "Programs running in browsers don't have access to regular IO.";
|
| exit;
|
| }
|
|
|
| if $*OS eq any <MSWin32 mingw msys cygwin> {
|
| skip_rest "file tests not fully available on win32";
|
| exit;
|
| };
|
|
|
|
|
| {
|
| my $file = create_temporary_file;
|
| my @result = chmod 0o000, $file;
|
| is +@result, 1, "One file successfully changed";
|
| #?pugs todo ''
|
| is @result[0], $file, "name of the file returned";
|
| if ($*EUID) {
|
| ok $file ~~ :!r, "not readable after 0";
|
| ok $file ~~ :!w, "not writeable after 0";
|
| ok $file ~~ :!x, "not executable after 0";
|
| }
|
| else {
|
| skip 3, ":r :w :x can accidentally work with root permission";
|
| }
|
| remove_file($file);
|
| }
|
|
|
|
|
| {
|
| my $file = create_temporary_file;
|
| my @result = chmod 0o700, $file;
|
| is +@result, 1, "One file successfully changed";
|
| #?pugs todo ''
|
| is @result[0], $file, "name of the file returned";
|
|
|
| ok $file ~~ :r, "readable after 700";
|
| ok $file ~~ :w, "writabel after 700";
|
| ok $file ~~ :x, "executable after 700";
|
| remove_file($file);
|
| }
|
|
|
|
|
| {
|
| my $file = create_temporary_file;
|
| my @result = chmod 0o777, $file;
|
| is +@result, 1, "One file successfully changed";
|
| #?pugs todo ''
|
| is @result[0], $file, "name of the file returned";
|
|
|
| ok $file ~~ :r, "readable after 777";
|
| ok $file ~~ :w, "writable after 777";
|
| ok $file ~~ :x, "executable after 777";
|
| remove_file($file);
|
| }
|
|
|
| sub create_temporary_file {
|
| my $time = time;
|
| my $file = "temp_$time";
|
| my $fh = open $file, :w orelse die "Could not create $file";
|
| diag "Using file $file";
|
| return $file;
|
| }
|
| sub remove_file ($file) {
|
| unlink $file;
|
| ok($file ~~ :!e, "Test file was successfully removed");
|
| }
|
|
|
| ok(try { "nonesuch" ~~ :!e }, "~~:!e syntax works");
|
|
|
| eval q{
|
| ok(try { "nonesuch".:!e }, ".:!e syntax works");
|
| }
|
Changes the permissions of a list of files. The first element of the list must be the numerical mode, which should probably be an octal number, and which definitely should not be a string of octal digits: 0o644 is okay, 0644 is not. Returns the number of files successfully changed.
$cnt = chmod 0o755, 'foo', 'bar';
chmod 0o755, @executables;
$mode = '0644'; chmod $mode, 'foo'; # !!! sets mode to --w----r-T
$mode = '0o644'; chmod $mode, 'foo'; # this is better
$mode = 0o644; chmod $mode, 'foo'; # this is best
From t/spec/S16-filehandles/io.t lines 6–56 (no results): (skip)
| # L<S16/"Filehandles, files, and directories"/"close">
|
| # L<S16/Unfiled/IO.readline>
|
|
|
| =begin pod
|
|
|
| I/O tests
|
|
|
| =end pod
|
|
|
| plan 57;
|
|
|
| if $*OS eq "browser" {
|
| skip_rest "Programs running in browsers don't have access to regular IO.";
|
| exit;
|
| }
|
|
|
|
|
| sub nonce () { return (".$*PID." ~ int rand 1000) }
|
| my $filename = 'tempfile' ~ nonce();
|
|
|
| # create and write a file
|
|
|
| my $out = open($filename, :w);
|
| isa_ok($out, 'IO');
|
| $out.say("Hello World");
|
| say($out: "Foo Bar Baz");
|
| $out.say("The End");
|
| ok($out.close, 'file closed okay');
|
|
|
| # read the file all possible ways
|
|
|
| my $in1 = open($filename);
|
| isa_ok($in1, 'IO');
|
| my $line1a = readline($in1);
|
| is($line1a, "Hello World", 'readline($in) worked (and autochomps)');
|
| my $line1b = readline($in1);
|
| is($line1b, "Foo Bar Baz", 'readline($in) worked (and autochomps)');
|
| my $line1c = readline($in1);
|
| is($line1c, "The End", 'readline($in) worked');
|
| ok($in1.close, 'file closed okay (1)');
|
|
|
| my $in2 = open($filename);
|
| isa_ok($in2, 'IO');
|
| my $line2a = $in2.readline();
|
| is($line2a, "Hello World", '$in.readline() worked');
|
| my $line2b = $in2.readline();
|
| is($line2b, "Foo Bar Baz", '$in.readline() worked');
|
| my $line2c = $in2.readline();
|
| is($line2c, "The End", '$in.readline() worked');
|
| ok($in2.close, 'file closed okay (2)');
|
|
|
From t/spec/S16-filehandles/io_in_for_loops.t lines 5–96 (5 √, 4 ×): (skip)
| # L<S16/"Filehandles, files, and directories"/"close"> |
| |
| plan 37; |
| |
| if $*OS eq "browser" { |
| skip_rest "Programs running in browsers don't have access to regular IO."; |
| exit; |
| } |
| |
| my $filename = 'tempfile'; |
| |
| { # write the file first |
| my $fh = open($filename, :w); |
| for (1 .. 6) -> $num { |
| $fh.print("$num\n"); |
| } |
| $fh.close(); |
| } |
| |
| { # now read it in and check |
| my $fh = open($filename); |
| for (1 .. 6) -> $num { |
| my $line = =$fh; |
√ | is($line, "$num", '... got the right line (array controlled loop)'); |
| } |
| $fh.close(); |
| } |
| |
| { # now read it in with the $fh controling the loop |
| my $fh = open($filename); |
| my $num = 1; |
| for (=$fh) -> $line { |
√ | is($line, "$num", '... got the right line ((=$fh) controlled loop)'); |
| $num++; |
| } |
| $fh.close(); |
| } |
| |
| { # now read it in with the $fh controling the loop w/out parens |
| my $fh = open($filename); |
| my $num = 1; |
| for =$fh -> $line { |
√ | is($line, "$num", '... got the right line (=$fh controlled loop)'); |
| $num++; |
| } |
| $fh.close(); |
| } |
| |
| ## more complex loops |
| |
| { # now read it in and check |
| my $fh = open($filename); |
| my $num = 1; |
| for (1 .. 3) -> $_num { |
| my $line = =$fh; |
√ | is($line, "$num", '... got the right line (array controlled loop)'); |
| $num++; |
| my $line2 = =$fh; |
√ | is($line2, "$num", '... got the right line2 (array controlled loop)'); |
| $num++; |
| } |
| $fh.close(); |
| } |
| |
| { # now read it in with the $fh controling the loop but call |
| # the =$fh inside the loop inside parens (is this list context??) |
| my $fh = open($filename); |
| my $num = 1; |
| for =$fh -> $line { |
× | is($line, "$num", '... got the right line ((=$fh) controlled loop)'); |
| $num++; |
| my $line2 = =$fh; |
× | is($line2, "$num", '... got the right line2 ((=$fh) controlled loop)'); |
| $num++; |
| } |
| $fh.close(); |
| } |
| |
| { # now read it in with the $fh controling the loop but call |
| # the =$fh inside the loop w/out parens (is this scalar context??) |
| my $fh = open($filename); |
| my $num = 1; |
| for =$fh -> $line { |
× | is($line, "$num", '... got the right line (=$fh controlled loop)'); |
| $num++; |
| my $line2 = =$fh; |
× | is($line2, "$num", '... got the right line2 (=$fh controlled loop)'); |
| $num++; |
| } |
| $fh.close(); |
| } |
| |
Closes the file or pipe associated with the file handle, returning true only if IO buffers are successfully flushed and closes the system file descriptor. Closes the currently selected filehandle if the argument is omitted.
You don't have to close IO if you are immediately going to do another open on it, because open will close it for you. (See open.) However, an explicit close on an input file resets the line counter ($.), while the implicit close done by open does not.
From t/spec/S16-filehandles/io_in_while_loops.t lines 4–46 (3 √, 0 ×): (skip)
| # L<S16/"Filehandles, files, and directories"/"open"> |
| |
| plan 13; |
| |
| if $*OS eq "browser" { |
| skip_rest "Programs running in browsers don't have access to regular IO."; |
| exit; |
| } |
| |
| |
| my $filename = 'tempfile'; |
| |
| { # write the file first |
| my $fh = open($filename, :w); |
| for (1 .. 6) -> $num { |
| $fh.print("$num\n"); |
| } |
| $fh.close(); |
| } |
| |
| { # now read it in and check |
| my $fh = open($filename); |
| my $num = 1; |
| while ($num <= 6) { |
| my $line = =$fh; |
√ | is($line, "$num", '... got the right line (array controlled loop)'); |
| $num++; |
| } |
| $fh.close(); |
| } |
| |
| { # now read it in with the $fh controling the loop |
| my $fh = open($filename); |
| my $num = 1; |
| my $line; |
| while ($line = =$fh) { |
√ | is($line, "$num", '... got the right line (=$fh controlled loop)'); |
| $num++; |
| } |
| $fh.close(); |
| } |
| |
√ | is(unlink($filename), 1, 'file has been removed'); |
From t/spec/S16-filehandles/io.t lines 5–56 (no results): (skip)
| # L<S16/"Filehandles, files, and directories"/"open">
|
| # L<S16/"Filehandles, files, and directories"/"close">
|
| # L<S16/Unfiled/IO.readline>
|
|
|
| =begin pod
|
|
|
| I/O tests
|
|
|
| =end pod
|
|
|
| plan 57;
|
|
|
| if $*OS eq "browser" {
|
| skip_rest "Programs running in browsers don't have access to regular IO.";
|
| exit;
|
| }
|
|
|
|
|
| sub nonce () { return (".$*PID." ~ int rand 1000) }
|
| my $filename = 'tempfile' ~ nonce();
|
|
|
| # create and write a file
|
|
|
| my $out = open($filename, :w);
|
| isa_ok($out, 'IO');
|
| $out.say("Hello World");
|
| say($out: "Foo Bar Baz");
|
| $out.say("The End");
|
| ok($out.close, 'file closed okay');
|
|
|
| # read the file all possible ways
|
|
|
| my $in1 = open($filename);
|
| isa_ok($in1, 'IO');
|
| my $line1a = readline($in1);
|
| is($line1a, "Hello World", 'readline($in) worked (and autochomps)');
|
| my $line1b = readline($in1);
|
| is($line1b, "Foo Bar Baz", 'readline($in) worked (and autochomps)');
|
| my $line1c = readline($in1);
|
| is($line1c, "The End", 'readline($in) worked');
|
| ok($in1.close, 'file closed okay (1)');
|
|
|
| my $in2 = open($filename);
|
| isa_ok($in2, 'IO');
|
| my $line2a = $in2.readline();
|
| is($line2a, "Hello World", '$in.readline() worked');
|
| my $line2b = $in2.readline();
|
| is($line2b, "Foo Bar Baz", '$in.readline() worked');
|
| my $line2c = $in2.readline();
|
| is($line2c, "The End", '$in.readline() worked');
|
| ok($in2.close, 'file closed okay (2)');
|
|
|
From t/spec/S16-filehandles/io_in_for_loops.t lines 4–96 (5 √, 4 ×): (skip)
| # L<S16/"Filehandles, files, and directories"/"open"> |
| # L<S16/"Filehandles, files, and directories"/"close"> |
| |
| plan 37; |
| |
| if $*OS eq "browser" { |
| skip_rest "Programs running in browsers don't have access to regular IO."; |
| exit; |
| } |
| |
| my $filename = 'tempfile'; |
| |
| { # write the file first |
| my $fh = open($filename, :w); |
| for (1 .. 6) -> $num { |
| $fh.print("$num\n"); |
| } |
| $fh.close(); |
| } |
| |
| { # now read it in and check |
| my $fh = open($filename); |
| for (1 .. 6) -> $num { |
| my $line = =$fh; |
√ | is($line, "$num", '... got the right line (array controlled loop)'); |
| } |
| $fh.close(); |
| } |
| |
| { # now read it in with the $fh controling the loop |
| my $fh = open($filename); |
| my $num = 1; |
| for (=$fh) -> $line { |
√ | is($line, "$num", '... got the right line ((=$fh) controlled loop)'); |
| $num++; |
| } |
| $fh.close(); |
| } |
| |
| { # now read it in with the $fh controling the loop w/out parens |
| my $fh = open($filename); |
| my $num = 1; |
| for =$fh -> $line { |
√ | is($line, "$num", '... got the right line (=$fh controlled loop)'); |
| $num++; |
| } |
| $fh.close(); |
| } |
| |
| ## more complex loops |
| |
| { # now read it in and check |
| my $fh = open($filename); |
| my $num = 1; |
| for (1 .. 3) -> $_num { |
| my $line = =$fh; |
√ | is($line, "$num", '... got the right line (array controlled loop)'); |
| $num++; |
| my $line2 = =$fh; |
√ | is($line2, "$num", '... got the right line2 (array controlled loop)'); |
| $num++; |
| } |
| $fh.close(); |
| } |
| |
| { # now read it in with the $fh controling the loop but call |
| # the =$fh inside the loop inside parens (is this list context??) |
| my $fh = open($filename); |
| my $num = 1; |
| for =$fh -> $line { |
× | is($line, "$num", '... got the right line ((=$fh) controlled loop)'); |
| $num++; |
| my $line2 = =$fh; |
× | is($line2, "$num", '... got the right line2 ((=$fh) controlled loop)'); |
| $num++; |
| } |
| $fh.close(); |
| } |
| |
| { # now read it in with the $fh controling the loop but call |
| # the =$fh inside the loop w/out parens (is this scalar context??) |
| my $fh = open($filename); |
| my $num = 1; |
| for =$fh -> $line { |
× | is($line, "$num", '... got the right line (=$fh controlled loop)'); |
| $num++; |
| my $line2 = =$fh; |
× | is($line2, "$num", '... got the right line2 (=$fh controlled loop)'); |
| $num++; |
| } |
| $fh.close(); |
| } |
| |
From t/spec/S16-filehandles/open.t lines 5–50 (no results): (skip)
| # L<S16/"Filehandles, files, and directories"/"open">
|
|
|
| =begin pod
|
|
|
| Some edge and error cases for open()
|
|
|
| =end pod
|
|
|
|
|
| if $*OS eq "browser" {
|
| skip_rest "Programs running in browsers don't have access to regular IO.";
|
| exit;
|
| }
|
|
|
| # deal with non-existent files
|
| {
|
| skip(1, "open('nonexisting') => undef is waiting on 'use fatal'");
|
|
|
| if 0 {
|
| ok(!defined(open("file_which_does_not_exist")), 'open() on non-existent file returns undef');
|
| }
|
|
|
| open("create_this_file", :w);
|
| ok('create_this_file' ~~ :e, 'writing to a non-existent file creates it');
|
| unlink('create_this_file');
|
|
|
| open("create_this_file2", :w);
|
| ok('create_this_file2' ~~ :e, 'appending to a non-existent file creates it');
|
| unlink('create_this_file2');
|
| }
|
|
|
|
|
| =begin pod
|
|
|
| I/O Redirection to scalar tests
|
|
|
| =end pod
|
|
|
| skip_rest("needs speccing"); exit;
|
|
|
| my $scalar;
|
|
|
| # XXX: gaal: dunno how this should be, but this isn't it.
|
| #?pugs 2 todo ''
|
| ok(try { open $*OUT,">",\$scalar },'Direct STDOUT to a scalar');
|
| ok(try { open $*ERR,">",\$scalar },'Direct STDERR to a scalar');
|
If the file handle came from a piped open, close will additionally return false if one of the other system calls involved fails, or if the program exits with non-zero status. (If the only problem was that the program exited non-zero, $! will be set to 0.) Closing a pipe also waits for the process executing on the pipe to complete, in case you want to look at the output of the pipe afterwards, and implicitly puts the exit status value of that command into $!.
From t/spec/S16-filehandles/connect.t lines 5–48 (no results): (skip)
| # L<S16/"Filehandles, files, and directories"/"connect"> |
| |
| =begin pod |
| |
| Tests for IO connect() builtin |
| |
| =end pod |
| |
| plan 4; |
| |
| if $*OS eq "browser" { |
| skip_rest "Programs running in browsers don't have access to regular IO."; |
| exit; |
| } |
| |
| unless %*ENV<PUGS_TESTS_ALLOW_NETWORK> { |
| skip_rest "Won't test &connect as environment variable \"PUGS_TESTS_ALLOW_NETWORK\" is not true."; |
| exit; |
| } |
| |
| { |
| my $fh = connect "google.com", 80; |
| |
| my $nl = chr(13) ~ chr(10); |
| $fh.print("GET / HTTP/1.0{$nl}Host: google.de{$nl}User-Agent: pugs/connect.t{$nl}Connection: close$nl$nl"); |
| $fh.flush(); |
| |
| ok index($fh.readline, "HTTP/") > -1, "connect('google.de', 80) works"; |
| } |
| |
| { |
| dies_ok { connect "localhost", 70000 }, "&connect fails when it can't connect"; |
| } |
| |
| skip_rest("waiting on 'use fatal'"); exit; |
| |
| { |
| # no fatal; |
| lives_ok { connect "localhost", 70000 }, |
| "&connect does not die when it can't connect"; |
| |
| ok !connect("localhost", 70000), |
| "&connect returns a false value when it can't connect"; |
| } |
my $fh = connect($hostname, 80);
Attempts to connect to a remote host and returns an IO handle if successful. The call fails with an exception if it cannot connect.
Available only as a handle method.
Available only as a handle method.
Available only as a handle method.
Returns a stat buffer. If the lstat succeeds, the stat buffer evaluates to true, and additional file tests may be performed on the value. If the stat fails, all subsequent tests on the stat buffer also evaluate to false.
The .name method returns the name of the file/socket/uri the handle was opened with, if known. Returns undef otherwise. There is no corresponding name() function.
From t/var/default_scalar.t lines 18–45 (3 √, 0 ×): (skip)
| # L<S16/"Filehandles, files, and directories"/"=item open"> |
| # L<S16/"Input and Output"/"=item say"> |
| |
| # work around missing capabilities |
| # to get the output of 'say' into a test; |
| my $out = open("tmpfile", :w); |
| $out.say(3); |
| close $out; |
| my$in = open "tmpfile"; |
| my $s = =$in; close $in; |
| unlink "tmpfile"; |
| |
√ | is $s,"3", 'and is the default argument for "say"'; |
| |
| #pugs> for .. { say }; |
| |
| my $out = open("tmpfile", :w); |
| for 1 { $out.say() }; |
| close $out; |
| my$in = open "tmpfile"; |
| my $s = =$in; close $in; |
| unlink "tmpfile"; |
| |
√ | isnt $s,"3", 'and global $_ should not be the default topic of "for"'; |
| my @mutable_array = 1..3; |
√ | lives_ok { for @mutable_array { $_++ } }, 'default topic is rw by default'; |
| # #*** Error: cannot modify constant item at 1 |
| |
# Read
my $fh = open($filename);
# Write
my $fh = open($filename, :w);
our IO method fdopen(Int $fd)
Associate an IO object with an already-open file descriptor, presumably passed in from the parent process.
From t/spec/S16-filehandles/dir.t lines 6–7 (no results): (skip)
| # L<S16/"Filehandles, files, and directories"/"IO::Dir::open"> |
| # XXX closedir is not defined rigth now, should it be IO::Dir::close"? |
From t/spec/S16-filehandles/dir.t lines 8–9 (no results): (skip)
| # L<S16/"Filehandles, files, and directories"/"IO::Dir::open"> |
| # XXX readdir is not defined rigth now, should be it IO::Dir::read"? |
From t/spec/S16-filehandles/dir.t lines 10–11 (no results): (skip)
| # L<S16/"Filehandles, files, and directories"/"IO::Dir::open"> |
| # XXX rewinddir is not defined rigth now, should it be IO::Dir::rewind"? |
From t/spec/S16-filehandles/dir.t lines 12–158 (1 √, 0 ×): (skip)
| # L<S16/"Filehandles, files, and directories"/"IO::Dir::open"> |
| |
| if ($*OS eq any <browser>) { |
| skip_rest "not supported on this platform"; |
| exit; |
| } |
| |
| =begin pod |
| |
| opendir/readdir support |
| |
| =end pod |
| |
| my $dir = opendir($FindBin::Bin); |
√ | isa_ok($dir, IO::Dir, "opendir worked on $FindBin::Bin"); |
| |
| my @files = readdir($dir); |
| ok(@files, "seems readdir worked too"); |
| |
| my @more_files = readdir($dir); |
| is(+@more_files, 0, "No more things to read"); |
| |
| my $row = readdir($dir); |
| ok(!defined($row), "in scalar context it returns undef"); |
| |
| my $rew_1 = rewinddir($dir); |
| is($rew_1, 1, "success of rewinddir 1 returns 1"); |
| |
| my @files_again = readdir($dir); |
| |
| is_deeply(\@files_again, @files, "same list of files retrieved after rewind"); |
| |
| my $rew_2 = rewinddir($dir); |
| is($rew_2, 1, "success of rewinddir 2 returns 1"); |
| |
| my @files_scalar; |
| loop { |
| my $f = readdir($dir) orelse last; |
| @files_scalar.push($f); |
| } |
| is_deeply(\@files_scalar, @files, "same list of files retrieved after rewind, using scalar context"); |
| |
| my $rew_3 = $dir.rewinddir; |
| is($rew_3, 1, 'success of rewinddir 3 using $dir.rewinddir returns 1'); |
| my @files_dot = $dir.readdir; |
| is_deeply(\@files_dot, @files, 'same list of files retrieved using $dir.readdir'); |
| |
| my $rew_4 = $dir.rewinddir; |
| is($rew_4, 1, 'success of rewinddir 4 using $dir.rewinddir returns 1'); |
| |
| my @files_scalar_dot; |
| for $dir.readdir -> $f { |
| @files_scalar_dot.push($f); |
| } |
| is_deeply(\@files_scalar_dot, @files, 'same list of files, using $dir.readdir in scalar context'); |
| |
| my @more_files_2 = $dir.readdir; |
| is(+@more_files_2, 0, "No more things to read"); |
| |
| my $row_2 = $dir.readdir; |
| ok(!defined($row_2), "in scalar context it returns undef"); |
| |
| |
| ok(closedir($dir), "as does closedir"); |
| |
| # on closed directory handler these calls should throw an exception |
| #my $undef = readdir($dir); |
| #my @empty = readdir($dir); |
| # rewinddir($dir); |
| # closedir |
| |
| |
| my $dh = opendir($FindBin::Bin); |
| isa_ok($dh, 'IO::Dir', "opendir worked"); |
| my @files_once_more = $dh.readdir; |
| is_deeply(@files_once_more.sort, @files.sort, 'same list of files,after reopen'); |
| ok($dir.closedir, 'closedir using $dir.closedir format'); |
| |
| |
| # short version. read close etc... |
| # copied from above just shortent he methods. and append _s to every variable. |
| diag "Start testing for short version."; |
| my $dir_s = opendir($FindBin::Bin); |
| isa_ok($dir_s, IO::Dir, "opendir worked on $FindBin::Bin"); |
| |
| my @files_s = read($dir_s); |
| ok(@files_s, "seems read worked too"); |
| |
| my @more_files_s = read($dir); |
| is(+@more_files_s, 0, "No more things to read"); |
| |
| my $row_s = read($dir_s); |
| ok(!defined($row_s), "in scalar context it returns undef"); |
| |
| my $rew_1_s = rewind($dir_s); |
| is($rew_1_s, 1, "success of rewind 1 returns 1"); |
| |
| my @files_again_s = read($dir_s); |
| |
| is_deeply(\@files_again_s, @files_s, "same list of files retrieved after rewind"); |
| |
| my $rew_2_s = rewind($dir_s); |
| is($rew_2_s, 1, "success of rewind 2 returns 1"); |
| |
| my @files_scalar_s; |
| loop { |
| my $f = read($dir_s) orelse last; |
| @files_scalar_s.push($f); |
| } |
| is_deeply(\@files_scalar_s, @files_s, "same list of files retrieved after rewind, using scalar context"); |
| |
| my $rew_3_s = $dir_s.rewind; |
| is($rew_3_s, 1, 'success of rewind 3 using $dir.rewind returns 1'); |
| my @files_dot_s = $dir_s.read; |
| is_deeply(\@files_dot_s, @files_s, 'same list of files retrieved using $dir.read'); |
| |
| my $rew_4_s = $dir_s.rewind; |
| is($rew_4_s, 1, 'success of rewind 4 using $dir.rewind returns 1'); |
| |
| my @files_scalar_dot_s; |
| for $dir_s.read -> $f { |
| @files_scalar_dot_s.push($f); |
| } |
| is_deeply(\@files_scalar_dot_s, @files, 'same list of files, using $dir.read in scalar context'); |
| |
| my @more_files_2_s = $dir_s.read; |
| is(+@more_files_2_s, 0, "No more things to read"); |
| |
| my $row_2_s = $dir_s.read; |
| ok(!defined($row_2_s), "in scalar context it returns undef"); |
| |
| |
| ok(close($dir_s), "as does close"); |
| |
| # on closed directory handler these calls should throw an exception |
| #my $undef = readdir($dir); |
| #my @empty = readdir($dir); |
| # rewinddir($dir); |
| # closedir |
| |
| my $dh_s = opendir($FindBin::Bin); |
| isa_ok($dh_s, 'IO::Dir', "opendir worked"); |
| my @files_once_more_s = $dh_s.read; |
| is_deeply(@files_once_more_s.sort, @files_s.sort, 'same list of files,after reopen'); |
| ok($dir_s.close, 'close using $dir.close format'); |
| |
| |
my $dir = IO::Dir::open('.');
Opens a directory named EXPR for processing. Makes the directory looks like a list of autochomped lines, so just use ordinary IO operators after the open.
Deletes the directory specified by FILENAME if that directory is empty. If it succeeds it returns true, otherwise it returns false and sets $! (errno). If FILENAME is omitted, uses $_.
Returns a stat buffer. If the lstat succeeds, the stat buffer evaluates to true, and additional file tests may be performed on the value. If the stat fails, all subsequent tests on the stat buffer also evaluate to false.
From t/spec/S16-filehandles/io_in_for_loops.t lines 97–99 (1 √, 0 ×): (skip)
| # L<S16/"Filehandles, files, and directories"/"unlink"> |
| |
√ | is(unlink($filename), 1, 'file has been removed'); |
From t/spec/S16-filehandles/unlink.t lines 5–35 (6 √, 0 ×): (skip)
| # L<S16/"Filehandles, files, and directories"/"unlink"> |
| |
| sub nonce() { ".$*PID." ~ int rand 1000 } |
| |
| if $*OS eq "browser" { |
| skip_rest "Programs running in browsers don't have access to regular IO."; |
| exit; |
| } |
| |
| my $fn = "unlink-test-file" ~ nonce; |
| |
| my $iswin32 = ?($*OS eq any <MSWin32 mingw msys cygwin>) ?? "Timely closing of file handles does not yet work" !! undef; |
| |
| # open, explicit close, unlink, test |
| { |
| my $fh = open($fn, :w); |
| close $fh; |
| |
√ | ok $fn ~~ :e, "open() created a tempfile"; |
√ | is(unlink($fn), 1, "unlink() returned true"); |
√ | ok $fn ~~ :!e, "unlink() actually deleted the tempfile"; |
| } |
| |
| # open, implicit close because of scope exit, unlink, test |
| { |
| { my $fh = open($fn, :w) } |
| |
√ | ok $fn ~~ :e, "open() created a tempfile"; |
√ | is(unlink($fn), 1, "unlink() returned true", todo => $iswin32); |
√ | ok $fn ~~ :!e, "unlink() actually deleted the tempfile", todo => $iswin32; |
| } |
Deletes a list of files. Returns the number of files successfully deleted.
$cnt = unlink 'a', 'b', 'c';
Be warned that unlinking a directory can inflict damage on your filesystem. Finally, using unlink on directories is not supported on many operating systems. Use rmdir instead.
It is an error to use bare unlink without arguments.
From t/spec/S16-io/getc.t lines 5–30 (1 √, 0 ×): (skip)
| # L<S16/"Input and Output"/"getc"> |
| |
| sub nonce () { return (".$*PID." ~ int rand 1000) } |
| |
| if $*OS eq "browser" { |
| skip_rest "Programs running in browsers don't have access to regular IO."; |
| exit; |
| } |
| |
| my $tmpfile = "temp-test" ~ nonce(); |
| { |
| my $fh = open($tmpfile, :w) orelse die "Couldn't open \"$tmpfile\" for writing: $!\n"; |
| print $fh: "TestÄÖÜ\n\n0"; |
| close $fh orelse die "Couldn't close \"$tmpfile\": $!\n"; |
| } |
| |
| { |
| my $fh = open $tmpfile orelse die "Couldn't open \"$tmpfile\" for reading: $!\n"; |
| my @chars; |
| push @chars, $_ while defined($_ = getc $fh); |
| close $fh orelse die "Couldn't close \"$tmpfile\": $!\n"; |
| |
√ | is ~@chars, "T e s t Ä Ö Ü \n \n 0", "getc() works even for utf-8 input"; |
| } |
| |
| END { unlink $tmpfile } |
our Bool method getc (IO $self: *@LIST)
Returns the next character from the input stream attached to IO, or the undefined value at end of file, or if there was an error (in the latter case $! is set).
From t/spec/S16-io/print.t lines 3–58 (no results): (skip)
| # L<S16/"Input and Output"/=item print> |
| |
| # Tests for print |
| { |
| print "ok 1 - basic form of print\n"; |
| } |
| |
| { |
| print "o", "k 2 - print with multiple parame", "ters (1)\n"; |
| |
| my @array = ("o", "k 3 - print with multiple parameters (2)\n"); |
| print @array; |
| } |
| |
| { |
| my $arrayref = (<ok 4 - print stringifies its args>, "\n"); |
| print $arrayref; |
| } |
| |
| { |
| "ok 5 - method form of print\n".print; |
| } |
| |
| { |
| print "o"; |
| print "k 6 - print doesn't add newlines\n"; |
| } |
| |
| # Perl6::Spec::IO mentions |
| # print FILEHANDLE: LIST |
| # FILEHANDLE.print(LIST) |
| # FILEHANDLE.print: LIST |
| # same holds for say, even though it is not (yet?) explicitly mentioned |
| |
| { |
| print $*DEFOUT: 'ok 7 - print with $*DEFOUT: as filehandle' ~ "\n"; |
| say $*DEFOUT: 'ok 8 - say with $*DEFOUT: as filehandle'; |
| } |
| |
| { |
| $*DEFOUT.print: 'ok 9 - $*DEFOUT.print: list' ~ "\n"; |
| $*DEFOUT.say: 'ok 10 - $DEFOUT.say: list'; |
| |
| } |
| |
| { |
| my @array = 'ok', ' ', '11 - $*DEFOUT.print(LIST)', "\n"; |
| $*DEFOUT.print(@array); |
| } |
| |
| { |
| my @array = 'ok', ' ', '12 - $*DEFOUT.say(LIST)'; |
| $*DEFOUT.say(@array); |
| } |
| |
| |
our Bool method print (IO $self: *@LIST)
our Bool multi print (*@LIST)
our Bool method print (Str $self: IO $io)
Prints a string or a list of strings. Returns Bool::True if successful, Failure otherwise. The IO handle, if supplied, must be an object that supports I/O. Indirect objects in Perl 6 must always be followed by a colon, and any indirect object more complicated than a variable should be put into parentheses.
If IO is omitted, prints to $*DEFOUT, which is aliased to $*OUT when the program starts but may be temporarily or permanently rebound to some other file handle. The form with leading dot prints $_ to $*DEFOUT unless an explicit filehandle is supplied.
It is a compiler error to use a bare print without arguments. (However, it's fine if you have an explicit argument list that evaluates to the empty list at runtime.)
There is are no variables corresponding to Perl 5's $, and $\ variables. Use join to interpose separators; use filehandle properties to change line endings.
From t/var/default_scalar.t lines 19–45 (3 √, 0 ×): (skip)
| # L<S16/"Input and Output"/"=item say"> |
| |
| # work around missing capabilities |
| # to get the output of 'say' into a test; |
| my $out = open("tmpfile", :w); |
| $out.say(3); |
| close $out; |
| my$in = open "tmpfile"; |
| my $s = =$in; close $in; |
| unlink "tmpfile"; |
| |
√ | is $s,"3", 'and is the default argument for "say"'; |
| |
| #pugs> for .. { say }; |
| |
| my $out = open("tmpfile", :w); |
| for 1 { $out.say() }; |
| close $out; |
| my$in = open "tmpfile"; |
| my $s = =$in; close $in; |
| unlink "tmpfile"; |
| |
√ | isnt $s,"3", 'and global $_ should not be the default topic of "for"'; |
| my @mutable_array = 1..3; |
√ | lives_ok { for @mutable_array { $_++ } }, 'default topic is rw by default'; |
| # #*** Error: cannot modify constant item at 1 |
| |
From t/spec/S16-io/say.t lines 3–27 (no results): (skip)
| # L<S16/"Input and Output"/=item say> |
| |
| say "1..5"; |
| |
| # Tests for say |
| { |
| say "ok 1 - basic form of say"; |
| } |
| |
| { |
| say "o", "k 2 - say with multiple parame", "ters (1)"; |
| |
| my @array = ("o", "k 3 - say with multiple parameters (2)"); |
| say @array; |
| } |
| |
| { |
| my $arrayref = <ok 4 - say stringifies its args>; |
| say $arrayref; |
| } |
| |
| { |
| "ok 5 - method form of say".say; |
| } |
| |
our Bool method say (IO $self: *@LIST)
our Bool multi say (*@LIST)
our Bool method say (Str $self: IO $io)
This is identical to print() except that it auto-appends a newline after the final argument.
Was: print "Hello, world!\n";
Now: say "Hello, world!";
As with print, it is a compiler error to use a bare say without arguments.
our Bool method printf (IO $self: Str $fmt, *@LIST)
our Bool multi printf (Str $fmt, *@LIST)
The function form works as in Perl 5 and always prints to $*DEFOUT. The method form uses IO handles as objects, not formats.
From t/spec/S16-unfiled/getpeername.t lines 4–20 (0 √, 1 ×): (skip)
| # L<S16/"Unfiled"/"IO.getpeername"> |
| |
| =begin pod |
| |
| IO.getpeername test |
| |
| =end pod |
| |
| plan 1; |
| |
| if $*OS eq "browser" { |
| skip_rest "Programs running in browsers don't have access to regular IO."; |
| exit; |
| } |
| |
| my $sock = connect('google.com', 80); |
× | is (eval('is $sock.getpeername')), not undef, "IO.getpeer works"; |
our List multi method lines (IO $handle:) is export;
our List multi lines (Str $filename);
Returns all the lines of a file as a (lazy) List regardless of context. See also slurp.
Gone, see Pipe.pair
our Str prompt (Str $prompt)
From t/spec/S16-filehandles/io.t lines 7–56 (no results): (skip)
| # L<S16/Unfiled/IO.readline>
|
|
|
| =begin pod
|
|
|
| I/O tests
|
|
|
| =end pod
|
|
|
| plan 57;
|
|
|
| if $*OS eq "browser" {
|
| skip_rest "Programs running in browsers don't have access to regular IO.";
|
| exit;
|
| }
|
|
|
|
|
| sub nonce () { return (".$*PID." ~ int rand 1000) }
|
| my $filename = 'tempfile' ~ nonce();
|
|
|
| # create and write a file
|
|
|
| my $out = open($filename, :w);
|
| isa_ok($out, 'IO');
|
| $out.say("Hello World");
|
| say($out: "Foo Bar Baz");
|
| $out.say("The End");
|
| ok($out.close, 'file closed okay');
|
|
|
| # read the file all possible ways
|
|
|
| my $in1 = open($filename);
|
| isa_ok($in1, 'IO');
|
| my $line1a = readline($in1);
|
| is($line1a, "Hello World", 'readline($in) worked (and autochomps)');
|
| my $line1b = readline($in1);
|
| is($line1b, "Foo Bar Baz", 'readline($in) worked (and autochomps)');
|
| my $line1c = readline($in1);
|
| is($line1c, "The End", 'readline($in) worked');
|
| ok($in1.close, 'file closed okay (1)');
|
|
|
| my $in2 = open($filename);
|
| isa_ok($in2, 'IO');
|
| my $line2a = $in2.readline();
|
| is($line2a, "Hello World", '$in.readline() worked');
|
| my $line2b = $in2.readline();
|
| is($line2b, "Foo Bar Baz", '$in.readline() worked');
|
| my $line2c = $in2.readline();
|
| is($line2c, "The End", '$in.readline() worked');
|
| ok($in2.close, 'file closed okay (2)');
|
|
|
From t/spec/S16-unfiled/readline.t lines 4–25 (no results): (skip)
| # L<S16/"Unfiled"/"=item IO.readline">
|
|
|
| plan 3;
|
|
|
| #if $*OS eq "browser" {
|
| # skip_rest "Programs running in browsers don't have access to regular IO.";
|
| # exit;
|
| #}
|
|
|
| my $fh = open $*PROGRAM_NAME;
|
| ok($fh, "could open self");
|
| isa_ok($fh, 'IO');
|
|
|
| my $line;
|
| eval '
|
| $fh is chomped;
|
| $line = =$fh;
|
| ';
|
|
|
| #?pugs todo 'feature, depends on "is chomped"'
|
| is($line, "use v6-alpha;", "first line was chomped");
|
|
|
Gone. (Note: for subsecond sleep, just use sleep with a fractional argument.)
From t/spec/S16-unfiled/slurp.t lines 8–60 (no results): (skip)
| # L<S16/"Unfiled"/"=item IO.slurp">
|
|
|
| if $*OS eq "browser" {
|
| skip_rest "Programs running in browsers don't have access to regular IO.";
|
| exit;
|
| }
|
|
|
| {
|
| my $contents = slurp "README";
|
| ok index($contents, "Pugs") != -1, "slurp() worked";
|
| }
|
|
|
| #?rakudo skip 'dies_ok() not implemented'
|
| {
|
| dies_ok { slurp "does-not-exist" }, "slurp() on not-existant files fails";
|
| }
|
|
|
| #?rakudo skip 'dies_ok() not implemented'
|
| {
|
| dies_ok { slurp "t/" }, "slurp() on directories fails";
|
| }
|
|
|
| # slurp in list context
|
| {
|
| my @slurped_lines = slurp "README";
|
| ok +@slurped_lines > 50, "more than 50 lines in README file ?";
|
|
|
| my $fh = open "README" orelse die;
|
| my @expected_lines = =$fh;
|
| $fh.close;
|
|
|
| is +@slurped_lines, +@expected_lines, "same number of lines read";
|
| my $diff = 0;
|
| for 0..@slurped_lines-1 -> $i {
|
| $diff += @slurped_lines[$i] eq @expected_lines[$i] ?? 1 !! 0;
|
| }
|
| #?pugs todo ''
|
| is $diff, 0, "all the lines are the same";
|
|
|
| # chomp does not work on arrays yet
|
| my @chomped_lines;
|
| for @slurped_lines -> $line {
|
| push @chomped_lines, chomp $line;
|
| }
|
| is_deeply @expected_lines, @chomped_lines, "same lines read with both slurp and via open";
|
|
|
| my @dot_lines = "README".slurp;
|
| is +@dot_lines, +@expected_lines, "same number of lines read";
|
|
|
| my $filename = "README";
|
| my @var_dot_lines = $filename.slurp;
|
| is +@var_dot_lines, +@expected_lines, "same number of lines read";
|
| }
|
our Item multi method slurp (IO $handle: *%opts) is export;
our Item multi slurp (Str $filename, *%opts);
Slurps the entire file into a Str or Buf regardless of context. (See also lines.) Whether a Str or Buf is returned depends on the options.
Gone, see Socket.pair
Prints a warning just like Perl 5, except that it is always sent to the object in $*DEFERR, which is just standard error ($*ERR).
our IO method to(Str $command, *%opts)
Opens a one-way pipe writing to $command. IO redirection for stderr is specified with :err(IO) or :err<Str>. Other IO redirection is done with feed operators. XXX how to specify "2>&1"?
our IO method from(Str $comm