Frage

I would like to check if a file name has any directory information within it, preferably without using a system dependent hack like index($file_name,'/')!=-1. I know of the File::Spec module, but can't seem to find a way to use that module to make this determination. Perhaps another module would work better. Here are some examples of file names:

# This does not have directory info, so the test should fail (i.e., return false)
my $file_name1='abc.txt';

# These do have directory info, so the test should succeed (i.e., return true)
my $file_name2='dir/abc.txt';
my $file_name3='/home/me/abc.txt';
my $file_name4='~me/abc.txt';
War es hilfreich?

Lösung

splitdir will return a list. Evaluated in scalar context, it returns the number of items in the list. If there is more than 1 item in the list, then you know there is a directory separator.

use warnings;
use strict;
use File::Spec qw();

while (<DATA>) {
    chomp;
    if (File::Spec->splitdir($_) > 1) {
        print 'PASS';
    }
    else {
        print 'FAIL';
    }
    print ": $_\n";
}

__DATA__
abc.txt
dir/abc.txt
/home/me/abc.txt
~me/abc.txt

Prints:

FAIL: abc.txt
PASS: dir/abc.txt
PASS: /home/me/abc.txt
PASS: ~me/abc.txt

Andere Tipps

EXAMPLE

($volume,$directories,$file) =
                       File::Spec->splitpath( $path );

splitpath Splits a path in to volume, directory, and filename portions. On systems with no concept of volume, returns '' for volume.

You must use File::Spec to get a properly portable result.

If the complexity bothers you then you should write your own library that wraps the File::Spec functions.

It could look like this

File PathLib.pm

package PathLib;
use strict;
use warnings;

require File::Spec;
require Exporter;

our @ISA       = qw(Exporter);
our @EXPORT_OK = qw(has_path);

sub has_path {
  my ($volume, $path, $name) = File::Spec->splitpath($_[0]);
  return ($path or $volume)
}

and you could use it like this. (I am currently working on a Windows laptop and cannot test a Unix version until I get to a desk, but I am certain that this will work for you.)

use strict;
use warnings;

use PathLib qw(has_path);

my @paths = qw{
  C:\aa\bb\cc.txt
  E:ee.txt
  cc.txt
  ee\
};

for (@paths) {
  printf "%6s - %s\n", has_path($_) ? 'true' : 'false', $_;
}

output

  true - C:\aa\bb\cc.txt
  true - E:ee.txt
 false - cc.txt
  true - ee\

In Perl, the / is always reserved as a directory separator (except for the old Mac OS System [6-9] which insisted that it be :). However, in other operating systems, other symbols could be used too. For example, on Windows machines, both of these refer to the same directory:

$file = 'C:\this\that\foo.txt';
$file = "C:/this/that/foo.txt";

So, you should always suspect that / is a directory specifier unless you happen to be on an old Mac. And, you should also figure out what the current Path separator may be on that system and look for that one too.

I was hoping that File::Spec or File::Path might have some function or method that reveals the name of the preferred directory separator, but there's none documented.

Looking through the code of the various File::Spec sub-modules, I see that the actual file separator is hard coded, and the functions are more complex than I originally thought. No special hidden function. Drat.

However, the good news is that File::Spec is a standard module, so there's no reason to fear it. It was even included in Perl 5.8.8. You don't have to worry that it might not be installed.

I did a simple test:

#! /usr/bin/env perl
#
use strict;
use warnings;
use feature qw(say);

use File::Spec;

while ( my $file = <DATA> ) {
    chomp $file;
    my ( $volume, $directory, $file )  = File::Spec->splitpath( $file );
    if ( $directory ) {
        say qq('$volume' '$directory' '$file' contains directory stuff);
    }
    else {
        say qq('$volume' '$directory' '$file' is pure file);
    }
}


__DATA__
Foo/Bar.txt
/Bar.txt
Bar.txt/
Foo_Bar.txt

The results were:

'' 'Foo/' 'Bar.txt' contains directory stuff
'' '/' 'Bar.txt' contains directory stuff
'' 'Bar.txt/' '' contains directory stuff
'' '' 'Foo_Bar.txt' is pure file

It appears that anytime a directory separator appears, the string is considered a file. I guess this makes sense.

This is one of those tasks that end up being way more complex than you realize (such as determining whether or not a string is a valid email address or a number). Your best bet is to use File::Spec->split and make sure there's no volume or directory set.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top