Website hosting service by Active-Venture.com
  

 Back to Index

Other I/O Issues

These topics aren't really arguments related to open or sysopen, but they do affect what you do with your open files.

Opening Non-File Files

When is a file not a file? Well, you could say when it exists but isn't a plain file. We'll check whether it's a symbolic link first, just in case.

 
    if (-l $file || ! -f _) {
        print "$file is not a plain file\n";
    }   

What other kinds of files are there than, well, files? Directories, symbolic links, named pipes, Unix-domain sockets, and block and character devices. Those are all files, too--just not plain files. This isn't the same issue as being a text file. Not all text files are plain files. Not all plain files are textfiles. That's why there are separate -f and -T file tests.

To open a directory, you should use the opendir function, then process it with readdir, carefully restoring the directory name if necessary:

 
    opendir(DIR, $dirname) or die "can't opendir $dirname: $!";
    while (defined($file = readdir(DIR))) {
        # do something with "$dirname/$file"
    }
    closedir(DIR);  

If you want to process directories recursively, it's better to use the File::Find module. For example, this prints out all files recursively, add adds a slash to their names if the file is a directory.

 
    @ARGV = qw(.) unless @ARGV;
    use File::Find;
    find sub { print $File::Find::name, -d && '/', "\n" }, @ARGV;  

This finds all bogus symbolic links beneath a particular directory:

 
    find sub { print "$File::Find::name\n" if -l && !-e }, $dir;  

As you see, with symbolic links, you can just pretend that it is what it points to. Or, if you want to know what it points to, then readlink is called for:

 
    if (-l $file) {
        if (defined($whither = readlink($file))) {
            print "$file points to $whither\n";
        } else {
            print "$file points nowhere: $!\n";
        } 
    }   

Named pipes are a different matter. You pretend they're regular files, but their opens will normally block until there is both a reader and a writer. You can read more about them in perlipc/"Named Pipes". Unix-domain sockets are rather different beasts as well; they're described in perlipc/"Unix-Domain TCP Clients and Servers".

When it comes to opening devices, it can be easy and it can tricky. We'll assume that if you're opening up a block device, you know what you're doing. The character devices are more interesting. These are typically used for modems, mice, and some kinds of printers. This is described in perlfaq8/"How do I read and write the serial port?" It's often enough to open them carefully:

 
    sysopen(TTYIN, "/dev/ttyS1", O_RDWR | O_NDELAY | O_NOCTTY)
		# (O_NOCTTY no longer needed on POSIX systems)
        or die "can't open /dev/ttyS1: $!";
    open(TTYOUT, "+>&TTYIN")
        or die "can't dup TTYIN: $!";

    $ofh = select(TTYOUT); $| = 1; select($ofh);

    print TTYOUT "+++at\015";
    $answer = <TTYIN>;  

With descriptors that you haven't opened using sysopen, such as a socket, you can set them to be non-blocking using fcntl:

 
    use Fcntl;
    fcntl(Connection, F_SETFL, O_NONBLOCK) 
        or die "can't set non blocking: $!";  

Rather than losing yourself in a morass of twisting, turning ioctls, all dissimilar, if you're going to manipulate ttys, it's best to make calls out to the stty(1) program if you have it, or else use the portable POSIX interface. To figure this all out, you'll need to read the termios(3) manpage, which describes the POSIX interface to tty devices, and then POSIX, which describes Perl's interface to POSIX. There are also some high-level modules on CPAN that can help you with these games. Check out Term::ReadKey and Term::ReadLine.

What else can you open? To open a connection using sockets, you won't use one of Perl's two open functions. See perlipc/"Sockets: Client/Server Communication" for that. Here's an example. Once you have it, you can use FH as a bidirectional filehandle.

 
    use IO::Socket;
    local *FH = IO::Socket::INET->new("www.perl.com:80");  

For opening up a URL, the LWP modules from CPAN are just what the doctor ordered. There's no filehandle interface, but it's still easy to get the contents of a document:

 
    use LWP::Simple;
    $doc = get('http://www.linpro.no/lwp/');  

Binary Files

On certain legacy systems with what could charitably be called terminally convoluted (some would say broken) I/O models, a file isn't a file--at least, not with respect to the C standard I/O library. On these old systems whose libraries (but not kernels) distinguish between text and binary streams, to get files to behave properly you'll have to bend over backwards to avoid nasty problems. On such infelicitous systems, sockets and pipes are already opened in binary mode, and there is currently no way to turn that off. With files, you have more options.

Another option is to use the binmode function on the appropriate handles before doing regular I/O on them:

 
    binmode(STDIN);
    binmode(STDOUT);
    while (<STDIN>) { print }   

Passing sysopen a non-standard flag option will also open the file in binary mode on those systems that support it. This is the equivalent of opening the file normally, then calling binmodeing on the handle.

 
    sysopen(BINDAT, "records.data", O_RDWR | O_BINARY)
        || die "can't open records.data: $!";  

Now you can use read and print on that handle without worrying about the system non-standard I/O library breaking your data. It's not a pretty picture, but then, legacy systems seldom are. CP/M will be with us until the end of days, and after.

On systems with exotic I/O systems, it turns out that, astonishingly enough, even unbuffered I/O using sysread and syswrite might do sneaky data mutilation behind your back.

 
    while (sysread(WHENCE, $buf, 1024)) {
        syswrite(WHITHER, $buf, length($buf));
    }   

Depending on the vicissitudes of your runtime system, even these calls may need binmode or O_BINARY first. Systems known to be free of such difficulties include Unix, the Mac OS, Plan 9, and Inferno.

File Locking

In a multitasking environment, you may need to be careful not to collide with other processes who want to do I/O on the same files as others are working on. You'll often need shared or exclusive locks on files for reading and writing respectively. You might just pretend that only exclusive locks exist.

Never use the existence of a file -e $file as a locking indication, because there is a race condition between the test for the existence of the file and its creation. Atomicity is critical.

Perl's most portable locking interface is via the flock function, whose simplicity is emulated on systems that don't directly support it, such as SysV or WindowsNT. The underlying semantics may affect how it all works, so you should learn how flock is implemented on your system's port of Perl.

File locking does not lock out another process that would like to do I/O. A file lock only locks out others trying to get a lock, not processes trying to do I/O. Because locks are advisory, if one process uses locking and another doesn't, all bets are off.

By default, the flock call will block until a lock is granted. A request for a shared lock will be granted as soon as there is no exclusive locker. A request for an exclusive lock will be granted as soon as there is no locker of any kind. Locks are on file descriptors, not file names. You can't lock a file until you open it, and you can't hold on to a lock once the file has been closed.

Here's how to get a blocking shared lock on a file, typically used for reading:

 
    use 5.004;
    use Fcntl qw(:DEFAULT :flock);
    open(FH, "< filename")  or die "can't open filename: $!";
    flock(FH, LOCK_SH) 	    or die "can't lock filename: $!";
    # now read from FH  

You can get a non-blocking lock by using LOCK_NB.

 
    flock(FH, LOCK_SH | LOCK_NB)
        or die "can't lock filename: $!";  

This can be useful for producing more user-friendly behaviour by warning if you're going to be blocking:

 
    use 5.004;
    use Fcntl qw(:DEFAULT :flock);
    open(FH, "< filename")  or die "can't open filename: $!";
    unless (flock(FH, LOCK_SH | LOCK_NB)) {
	$| = 1;
	print "Waiting for lock...";
	flock(FH, LOCK_SH)  or die "can't lock filename: $!";
	print "got it.\n"
    } 
    # now read from FH  

To get an exclusive lock, typically used for writing, you have to be careful. We sysopen the file so it can be locked before it gets emptied. You can get a nonblocking version using LOCK_EX | LOCK_NB.

 
    use 5.004;
    use Fcntl qw(:DEFAULT :flock);
    sysopen(FH, "filename", O_WRONLY | O_CREAT)
        or die "can't open filename: $!";
    flock(FH, LOCK_EX)
        or die "can't lock filename: $!";
    truncate(FH, 0)
        or die "can't truncate filename: $!";
    # now write to FH  

Finally, due to the uncounted millions who cannot be dissuaded from wasting cycles on useless vanity devices called hit counters, here's how to increment a number in a file safely:

 
    use Fcntl qw(:DEFAULT :flock);

    sysopen(FH, "numfile", O_RDWR | O_CREAT)
        or die "can't open numfile: $!";
    # autoflush FH
    $ofh = select(FH); $| = 1; select ($ofh);
    flock(FH, LOCK_EX)
        or die "can't write-lock numfile: $!";

    $num = <FH> || 0;
    seek(FH, 0, 0)
        or die "can't rewind numfile : $!";
    print FH $num+1, "\n"
        or die "can't write numfile: $!";

    truncate(FH, tell(FH))
        or die "can't truncate numfile: $!";
    close(FH)
        or die "can't close numfile: $!";  

IO Layers

In Perl 5.8.0 a new I/O framework called "PerlIO" was introduced. This is a new "plumbing" for all the I/O happening in Perl; for the most part everything will work just as it did, but PerlIO brought in also some new features, like the capability of think of I/O as "layers". One I/O layer may in addition to just moving the data also do transformations on the data. Such transformations may include compression and decompression, encryption and decryption, and transforming between various character encodings.

Full discussion about the features of PerlIO is out of scope for this tutorial, but here is how to recognize the layers being used:

  • The three-(or more)-argument form of open() is being used and the second argument contains something else in addition to the usual '<', '>', '>>', '|' and their variants, for example:

     
        open(my $fh, "<:utf8", $fn);  

  • The two-argument form of binmode<open() is being used, for example

     
        binmode($fh, ":encoding(utf16)");  

For more detailed discussion about PerlIO see perlio; for more detailed discussion about Unicode and I/O see perluniintro.

 

 

 

Domain name registration service & domain search - 
Register cheap domain name from $7.95 and enjoy free domain services 
 

Cheap domain name search service -
Domain name services at just
$8.95/year only
 


Buy domain name registration and cheap domain transfer at low, affordable price.

2002-2004 Active-Venture.com Web Site Hosting Service

 

[ You know you're a geek when... You try to shoo a fly away from the monitor with your cursor. That just happened to me. It was scary.   ]

 

 
 
 

Disclaimer: This documentation is provided only for the benefits of our web hosting customers.
For authoritative source of the documentation, please refer to http://www.perldoc.com