go ahead... be a heretic | |
PerlMonks |
Testing the current directory with Cwd and File::Specby xdg (Monsignor) |
on Dec 03, 2005 at 15:58 UTC ( [id://513817]=perlmeditation: print w/replies, xml ) | Need Help?? |
This meditation addresses how to test whether the current working directory is what you expect it to be. It describes some pitfalls I encountered using two common modules, Cwd and File::Spec, in the hopes that others may learn from my mistakes and avoid similar frustrations. This meditation stems from my work on the test suite for File::pushd, which does a "local" directory change for a limited scope. The module itself works correctly on both Linux and Win32 ActiveState (the two platforms I have handy to test) -- but proving that it's working in the test suite and doing so in an easy and portable way turned out to be a greater challenge that I first anticipated. Here is a simplified example of where my testing went awry:
Result on Linux:
Result on Win32:
Simple enough -- cwd provides forward slashes, whereas File::pushd uses File::Spec, which keeps things in the canonical form for the operating system. That can be cleaned up with canonpath.
That works on both Linux and Win32, hooray! But then I tried a different directory -- the root directory.
Again, this works just fine on Linux, but not on Win32. Result on Win32:
The problem is that rootdir doesn't provide a drive volume letter, whereas cwd does. This sent me off in several directions looking for a portable workaround. Before describing what I settled on, I'll show some things that didn't work, didn't work as expected, or just turned out too cumbersome as examples of things to avoid. Option 1: see if the current directory relative to the expected new root directory ( built from rootdir ) gives a directory of "." (i.e. the relative form of the current directory).
Now things get interesting. The result on Linux reveals a bug in File::Spec -- the result is the empty string rather than a proper directory:
This may seem a minor issue, but it could lead to a very hard-to-detect (and potentially severe) error if such a blank result were ever used with catdir again, as these one-liners demonstrate:
This bug has been fixed in version 3.13 of PathTools, but of course it breaks anything that depends on the older, broken behavior. For example, SVK is one of those tools that breaks. Whether/how this fix should be rolled into maint sparked some discussion on this perl5-porters thread. The situation isn't much better on Win32. There, I don't even get the empty string, as File::Spec refuses to take a relative path if it can't be sure the base path is on the same volume.
Option 2: drop the volume using splitpath (being sure to include the "no file" option). (For brevity, I use File::Spec::Functions selectively in the following examples.)
This works on both Linux and Win32 -- until I go back to trying it not with the root directory but with the first directory I tried.
This fails on Win32 because (recalling above), both "got" and "expected" have a volume. So the volume must be stripped twice:
This "works" on both systems, but only in the sense that the resulting strings match. With the volumes dropped, "D:\Temp" is the same as "C:\Temp". For the test suite I was running, that's probably OK, but I wouldn't recommend it as a general approach. It's also quite cumbersome, even more so without File::Spec::Functions. Option 3: stick entirely to Cwd and use abs_path to convert the expected value to the same format as cwd. In hindsight, this should have been obvious much earlier, since abs_path is the algorithm underlying cwd, but I started out trying to use File::Spec to handle the absolute paths portably and got caught up in the bug in Option 1. This option is shorter, clearer and works on both Linux and Win32.
This also works as desired on both operating systems when used with the root directory:
One potential downside to abs_path is that the paths returned have forward-slashes. This might be an issue if comparing to filenames generated externally from a native OS source -- though the consistent use of abs_path on all such filenames would address that. Likewise, consistently applying canonpath after abs_path could be an approach. abs_path has some side-effects, as it also resolves symbolic links and relative-path components like "." and "..", but for most ordinary needs it should work well. Given the volume-challenged behavior in File::Spec, abs_path is going to have a lasting place in my toolbox. -xdg Code written by xdg and posted on PerlMonks is public domain. It is provided as is with no warranties, express or implied, of any kind. Posted code may not have been tested. Use of posted code is at your own risk.
Back to
Meditations
|
|