Danger when using catfile for a path ending in a directory?
directory-structure, perl
Solution
The difference of the `catdir` and `catfile` is usually very small (depends on the used OS). On the UNIX, the `catfile` does `catdir` anyway for the directory elements (e.g. minus the last (file) part) and then appends the file part. The `catdir` does some path-cleaning logic. On the windows platform the differences even less.
You can check the source of the module(s) for the different OS-es yourself.
The main thing is: these routines doesn't do any filesystem access, just doing some logical path cleaning.
Alternative:
If you don't care about the other OS-es as UNIX-like (e.g. Linux, FreeBSD, Mac OS X etc..) and Windows, me personally recommending to use the Path::Tiny module. From it's doc:
This module provides a small, fast utility for working with file paths. It is friendlier to use than File::Spec and provides easy access to functions from several other core file handling modules. It aims to be smaller and faster than many alternatives on CPAN, while helping people do many common things in consistent and less error-prone ways.
Path::Tiny does not try to work for anything except Unix-like and Win32 platforms.
IMHO - it is one of the best modules on CPAN - real treasure. For example, for the path creation you should use the `child` method - again from the docs:
$file = path("/tmp")->child("foo.txt"); # "/tmp/foo.txt"
$file = path("/tmp")->child(@parts);
Returns a new Path::Tiny object relative to the original. Works like `catfile` or `catdir` from File::Spec, but without caring about file or directories.
Except the above the Path::Tiny module provides easy access to file contents, (also with unicode handling) and every commonly needed "path" operations. Drawback: unfortunately, it isn't CORE module, e.g. you need install it from the CPAN.
EDIT
Is safe to use `catfile` for the directories? By me: yes (but maybe some experts will know something else or more). On unix,
- the `catdir` calls the `canonpath` (which does some logical path-cleaning).
- the `catfile` calls the `canonpath` twice, once for the file part and once for the directories (if they are provided), and concatenate the two results. The concatenation could result some strange looking paths, but they're generally harmless.
On Windows, the differences even less, both will call the path-cleaning routine.
Demo - the following script will generate many combinations of different parts and will use the `catdir` and `catfile` (and even two variants of the `Path::Tiny`)
use strict;
use warnings;
use File::Spec::Functions;
use Path::Tiny;
my @parts = qw(x / /x /x/ /x// // //x //x/ //x// /./ /./x /x/./ );
my $cwidth=16;
print pr(qw(first second catdir catfile path-child path-list)), "\n";
print '-' x ($cwidth*6),"\n";
for my $first (map { s/x/first/r } @parts) {
for my $second ( map { s/x/second/r } @parts) {
print pr(
$first,
$second,
catdir($first,$second),
catfile($first,$second),
path($first)->child($second),
path($first,$second),
), "\n";
}
}
sub pr {
my $str;
$str .= sprintf "%-${cwidth}s",$_ for @_;
return $str;
}
It prints an long result. As you can see bellow the all paths are safe for use for directories and files, just some paths from the `catfile` isn't "nice".
The both "Path::Tiny" results are clear and consistent. Contrary, the `catfile` and `catdir` will allow to use undefined parts, (not shown in the demo) - and even in such errornous arguments will produce some results, the Path::Tiny will die if you pass undef or null-string to its `child` method.
first second catdir catfile path-child path-list
------------------------------------------------------------------------------------------------
first second first/second first/second first/second first/second
first / first first// first first
first /second first/second first//second first/second first/second
first /second/ first/second first//second first/second first/second
first /second// first/second first//second first/second first/second
first // first first// first first
first //second first/second first//second first/second first/second
first //second/ first/second first//second first/second first/second
first //second// first/second first//second first/second first/second
first /./ first first// first first
first /./second first/second first//second first/second first/second
first /second/./ first/second first//second first/second first/second
/ second /second /second /second /second
/ / / // / /
/ /second /second //second /second /second
/ /second/ /second //second /second /second
/ /second// /second //second /second /second
/ // / // / /
/ //second /second //second /second /second
/ //second/ /second //second /second /second
/ //second// /second //second /second /second
/ /./ / // / /
/ /./second /second //second /second /second
/ /second/./ /second //second /second /second
/first second /first/second /first/second /first/second /first/second
/first / /first /first// /first /first
/first /second /first/second /first//second /first/second /first/second
/first /second/ /first/second /first//second /first/second /first/second
/first /second// /first/second /first//second /first/second /first/second
/first // /first /first// /first /first
/first //second /first/second /first//second /first/second /first/second
/first //second/ /first/second /first//second /first/second /first/second
/first //second// /first/second /first//second /first/second /first/second
/first /./ /first /first// /first /first
/first /./second /first/second /first//second /first/second /first/second
/first /second/./ /first/second /first//second /first/second /first/second
/first/ second /first/second /first/second /first/second /first/second
/first/ / /first /first// /first /first
/first/ /second /first/second /first//second /first/second /first/second
/first/ /second/ /first/second /first//second /first/second /first/second
/first/ /second// /first/second /first//second /first/second /first/second
/first/ // /first /first// /first /first
/first/ //second /first/second /first//second /first/second /first/second
/first/ //second/ /first/second /first//second /first/second /first/second
/first/ //second// /first/second /first//second /first/second /first/second
/first/ /./ /first /first// /first /first
/first/ /./second /first/second /first//second /first/second /first/second
/first/ /second/./ /first/second /first//second /first/second /first/second
/first// second /first/second /first/second /first/second /first/second
/first// / /first /first// /first /first
/first// /second /first/second /first//second /first/second /first/second
/first// /second/ /first/second /first//second /first/second /first/second
/first// /second// /first/second /first//second /first/second /first/second
/first// // /first /first// /first /first
/first// //second /first/second /first//second /first/second /first/second
/first// //second/ /first/second /first//second /first/second /first/second
/first// //second// /first/second /first//second /first/second /first/second
/first// /./ /first /first// /first /first
/first// /./second /first/second /first//second /first/second /first/second
/first// /second/./ /first/second /first//second /first/second /first/second
// second /second /second /second /second
// / / // / /
// /second /second //second /second /second
// /second/ /second //second /second /second
// /second// /second //second /second /second
// // / // / /
// //second /second //second /second /second
// //second/ /second //second /second /second
// //second// /second //second /second /second
// /./ / // / /
// /./second /second //second /second /second
// /second/./ /second //second /second /second
//first second /first/second /first/second /first/second /first/second
//first / /first /first// /first /first
//first /second /first/second /first//second /first/second /first/second
//first /second/ /first/second /first//second /first/second /first/second
//first /second// /first/second /first//second /first/second /first/second
//first // /first /first// /first /first
//first //second /first/second /first//second /first/second /first/second
//first //second/ /first/second /first//second /first/second /first/second
//first //second// /first/second /first//second /first/second /first/second
//first /./ /first /first// /first /first
//first /./second /first/second /first//second /first/second /first/second
//first /second/./ /first/second /first//second /first/second /first/second
//first/ second /first/second /first/second /first/second /first/second
//first/ / /first /first// /first /first
//first/ /second /first/second /first//second /first/second /first/second
//first/ /second/ /first/second /first//second /first/second /first/second
//first/ /second// /first/second /first//second /first/second /first/second
//first/ // /first /first// /first /first
//first/ //second /first/second /first//second /first/second /first/second
//first/ //second/ /first/second /first//second /first/second /first/second
//first/ //second// /first/second /first//second /first/second /first/second
//first/ /./ /first /first// /first /first
//first/ /./second /first/second /first//second /first/second /first/second
//first/ /second/./ /first/second /first//second /first/second /first/second
//first// second /first/second /first/second /first/second /first/second
//first// / /first /first// /first /first
//first// /second /first/second /first//second /first/second /first/second
//first// /second/ /first/second /first//second /first/second /first/second
//first// /second// /first/second /first//second /first/second /first/second
//first// // /first /first// /first /first
//first// //second /first/second /first//second /first/second /first/second
//first// //second/ /first/second /first//second /first/second /first/second
//first// //second// /first/second /first//second /first/second /first/second
//first// /./ /first /first// /first /first
//first// /./second /first/second /first//second /first/second /first/second
//first// /second/./ /first/second /first//second /first/second /first/second
/./ second /second /second /second /second
/./ / / // / /
/./ /second /second //second /second /second
/./ /second/ /second //second /second /second
/./ /second// /second //second /second /second
/./ // / // / /
/./ //second /second //second /second /second
/./ //second/ /second //second /second /second
/./ //second// /second //second /second /second
/./ /./ / // / /
/./ /./second /second //second /second /second
/./ /second/./ /second //second /second /second
/./first second /first/second /first/second /first/second /first/second
/./first / /first /first// /first /first
/./first /second /first/second /first//second /first/second /first/second
/./first /second/ /first/second /first//second /first/second /first/second
/./first /second// /first/second /first//second /first/second /first/second
/./first // /first /first// /first /first
/./first //second /first/second /first//second /first/second /first/second
/./first //second/ /first/second /first//second /first/second /first/second
/./first //second// /first/second /first//second /first/second /first/second
/./first /./ /first /first// /first /first
/./first /./second /first/second /first//second /first/second /first/second
/./first /second/./ /first/second /first//second /first/second /first/second
/first/./ second /first/second /first/second /first/second /first/second
/first/./ / /first /first// /first /first
/first/./ /second /first/second /first//second /first/second /first/second
/first/./ /second/ /first/second /first//second /first/second /first/second
/first/./ /second// /first/second /first//second /first/second /first/second
/first/./ // /first /first// /first /first
/first/./ //second /first/second /first//second /first/second /first/second
/first/./ //second/ /first/second /first//second /first/second /first/second
/first/./ //second// /first/second /first//second /first/second /first/second
/first/./ /./ /first /first// /first /first
/first/./ /./second /first/second /first//second /first/second /first/second
/first/./ /second/./ /first/second /first//second /first/second /first/second
Problem
The `File::Spec` module provides a way to create cross-OS valid paths. It works as one would expect: ``` use strict; use warnings; use File::Spec; my $file = 'ghi.xml'; my $path = File::Spec->catfile(('abc', 'def'), $file); print $path; # Windows: abc/def/ghi.xml ``` A method `catdir` is also available, which would result in a directory - not a path to a file. My issue is that I do not know in advance if `$file` is a file name or a directory name. As far as I have tested, the results are still correct when using a dirname instead, e.g.: ``` use strict; use warnings; use File::Spec; my $file = 'ghi'; my $path = File::Spec->catfile(('abc', 'def'), $file); print $path; # Windows: abc/def/ghi ``` But I wonder whether this can cause issues anyway. I am assuming yes because I don't understand why the creators of the module would build two interfaces for the same functionality. However, I cannot find any further explanation in the documentation. Is it safe to use `catfile` to create paths to files as well as to directories? If not, what are the caveats?