Main Navigation

Content

Files and Directories Handling in Perl

Learn Perl Now!
And get a job doing Perl.

Perl has strong support for handling files and directories, which are the bread-and-butter of dealing with the disk and the filesystem. Many modules on CPAN (= the Comprehensive Perl Archive Network) aim to facilitate the task even further.

Table of Contents

The Basics

For the basics, read about the open function (on perlopentut or on recommended books or tutorials) as well as other built-ins such as opendir, readdir, closedir, mkdir. There are also many UNIX-oriented file-system-builtins listed on perlfunc which may prove of utility.

Modules

Useful modules for files and directories handling are:

  • Path-Tiny - a module that provides a “fast utility for working with file paths” and which despite its name, provides a comprehensive and rich API.
  • File-Slurper - a module for fast and easy input and output from files and directories. The interface is procedural and quite Spartan, but is still useful. (Note: Using File-Slurp, and File-Slurp-Tiny which were found to be broken, is no longer recommended.)
  • File::Spec - a core module to handle file and directory paths portably.
  • File::Basename - a core module to portably extract the basename, the dirname, the suffix and other file paths parsing.
  • String-ShellQuote - quote strings for passing through the shell. Also see the list forms of system.
  • File::Path - a core module to create or remove directory trees (portably).
  • File::Copy - a core module to copy files.
  • IO-All - a no longer recommended all-in-one IO package with a lot of syntactic sugar, but quite a few quirks and bugs. Non-core.

Directory Traversal

The built-in module for traversing a directory tree in Perl is File::Find, but it has some severe limitations in interface and use. Some better alternatives are:

  • File-Find-Object - an object-oriented replacement for File::Find that: 1) can be instantiated 2) has an iterative interface 3) can be interrupted in the middle and 4) can return result objects instead of path names.

  • File-Next - an alternative with an iterative interface, but incapable of being instantiated.

  • File-Find-Rule, which is still based on File::Find, and File-Find-Object-Rule provide a more convenient and succinct interface for finding what you want.

  • Path-Iterator-Rule provides an object oriented and iterative file finder, and there's also Path-Class-Rule and IO-All-Rule. Also of interest is its “See Also” section, which contains links to other implementations, and a comparison of them.

Examples

These are a set of examples for manipulating files and directories using Perl. Each will be shown in several versions including ones using IO-All, ones using core modules, and if relevant ones from the command line.

Reading a file line by line

#!/usr/bin/env perl

use strict;
use warnings;

use Path::Tiny qw/ path /;

my $filename_to_read = 'my-file.txt';

my $fh = path($filename_to_read)->openr_utf8;
while (my $line = <$fh>)
{
    chomp($line);
    # Do something with $line.
}


#!/usr/bin/env perl

use strict;
use warnings;

use autodie;

my $filename_to_read = 'my-file.txt';

open my $fh, '<', $filename_to_read;
while (my $line = <$fh>)
{
    chomp($line);

    # Do something with $line.
}
close($fh);


Copying a file

#!/usr/bin/env perl

use strict;
use warnings;

use Path::Tiny qw/ path /;

my ($source_filename, $dest_filename) = @ARGV;
path($source_filename)->copy($dest_filename);


#!/usr/bin/env perl

use strict;
use warnings;

use File::Copy qw(copy);

my ($source_filename, $dest_filename) = @ARGV;

copy($source_filename, $dest_filename);


Overwriting a file with text

#!/usr/bin/env perl

use strict;
use warnings;

use Path::Tiny qw/ path /;

path("output.txt")->spew_utf8("Hello World!\n");


#!/usr/bin/env perl

use strict;
use warnings;

use autodie;

open my $out, '>:encoding(utf8)', "output.txt";
print {$out} "Hello World!\n";
close($out);


Processing the Lines of a File

#!/usr/bin/env perl

use strict;
use warnings;

use Path::Tiny qw/ path /;

my @filenames = @ARGV;

foreach my $fn (@filenames)
{
    path($fn)->edit_lines_utf8(sub { s/\bFrom\b/To/g; });
}


#!/usr/bin/env perl

use strict;
use warnings;

use autodie;
use File::Temp ( qw(tempfile) );

my @filenames = @ARGV;

foreach my $fn (@filenames)
{
    open my $in, '<', $fn;
    my ($tempout, $temp_fn) = tempfile();

    while (my $line = <$in>)
    {
        chomp($line);
        # Perform the operation here.
        $line =~ s/\bFrom\b/To/g;

        print {$tempout} "$new_line\n";
    }

    close($in);
    close($tempout);

    rename($temp_fn, $fn);
}


$ perl -i.bak -lp -e 's/\bFrom\b/To/g' *.txt

Reading an entire UTF-8 file into a big variable

#!/usr/bin/env perl

use strict;
use warnings;

use Path::Tiny qw/ path /;

my $string = path($my_filepath)->slurp_utf8;


#!/usr/bin/env perl

use strict;
use warnings;

use autodie;

sub _utf8_slurp
{
    my $filename = shift;

    open my $in, '<:encoding(utf8)', $filename;

    local $/;
    my $contents = <$in>;

    close($in);

    return $contents;
}

my $file_contents = _utf8_slurp($my_filepath);


$ perl -i.bak -ln -0777 -C -ln -e 'Something with $_ here' "$my_utf8_filepath"
#!/usr/bin/env perl

use strict;
use warnings;

use IO::All qw/ io /;

my $string = io->file($my_filepath)->utf8->all;


Appending to a File

#!/usr/bin/env perl

use strict;
use warnings;

use Path::Tiny qw/ path /;

my $string_to_append = "My new line\n";

path($my_file_path)->append_utf8($string_to_append);


#!/usr/bin/env perl

use strict;
use warnings;

use autodie;

my $string_to_append = "My new line\n";

{
    open my $out, '>>', $my_file_path;
    print {$out} $string_to_append;
    close($out);
}


Line count

#!/usr/bin/env perl

use strict;
use warnings;

use Path::Tiny qw/ path /;

sub count_lines
{
    my ($filename) = @_;

    my $fh = path($filename)->openr_utf8;

    my $count = 0;

    while (my $l = <$fh>)
    {
        ++$count;
    }

    return $count;
}


#!/usr/bin/env perl

use strict;
use warnings;

use autodie;

sub count_lines
{
    my ($filename) = @_;

    open my $fh, '<', $filename;

    my $count = 0;
    while (my $l = <$fh>)
    {
        ++$count;
    }

    close($fh);

    return $count;
}


$ perl -lnE 'END{say "$ARGV has $. lines";}' /path/to/myfile.txt

Deleting a directory tree

#!/usr/bin/env perl

use strict;
use warnings;

use Path::Tiny qw/ path /;

path("./path-to-subdir")->remove_tree();


#!/usr/bin/env perl

use strict;
use warnings;

use File::Path qw(rmtree);

rmtree(["./path-to-subdir"], 1, 1);


Prepending to a File (While Slurping)

#!/usr/bin/env perl

use strict;
use warnings;

use Path::Tiny qw/ path /;

my $filename = 'foo.txt';
my $text_to_prepend = "[Text to Prepend]\n";

path($filename)->edit_utf8(sub { s/\A/$text_to_prepend/; return; });


#!/usr/bin/env perl

use strict;
use warnings;

my $filename = 'foo.txt';
my $text_to_prepend = "[Text to Prepend]\n";

open my $in_fh, '<', $filename
    or die "Could not open file '$filename' for reading! - $!";
my $contents = do { local $/; <$in_fh>;};
close($in_fh);

open my $out_fh, '>', $filename
    or die "Could not open file '$filename' for writing! - $!";
print {$out_fh} $text_to_prepend . $contents;
close($out_fh);


#!/usr/bin/env perl

use strict;
use warnings;

use IO::All qw/ io /;

my $filename = 'foo.txt';
my $text_to_prepend = "[Text to Prepend]\n";

my $fh = io->file($filename);

$fh->print( $text_to_prepend . $fh->all() );


Get an array of lines in a file with trailing newlines removed

#!/usr/bin/env perl

use strict;
use warnings;

use Path::Tiny qw/ path /;

my $filename = 'foo.txt';
my @lines = path($filename)->lines_utf8(
    {
        chomp => 1,
    })


#!/usr/bin/env perl

use strict;
use warnings;

use autodie;

sub _lines_utf8
{
    my $filename =shift;
    open my $fh, '<:encoding(utf-8)', $filename;
    my @ret;
    while(my $l=<$fh>)
    {
        chomp$l;
        push@ret, $l;
    }
    close $fh;
    return \@ret;
}

my $aref = _lines_utf8('/etc/resolv.conf');


Share/Bookmark

Footer