Cleanup file path

Y

Yves Martin

Hello,

I'm looking for a way to clean file paths like the following:
~/dir1/../dir2
~/dir1/../dir2/f1
~/dir1/./dir2/f1
/dir1/../dir2
/dir1/../dir2/f1
/dir1/./dir2/f1

I tested Cwd->realpath that reduce ./ and ../ but has many drawbacks:
- it does not understand ~/
- it follows symbolic links
- it checks path over the filesystem, so an unexisting file becomes
undef.

Results I expect are obviously:
~/dir1/../dir2 -> ~/dir2
~/dir1/../dir2/f1 -> ~/dir2/f1
~/dir1/./dir2/f1 -> ~/dir1/dir2/f1
/dir1/../dir2 -> /dir2
/dir1/../dir2/f1 -> /dir2/f1
/dir1/./dir2/f1 -> /dir1/dir2/f1

what ever the filesystem looks like (non existing file or symlink)
and which is multiplatform of course ;)

I'm really demanding but I just ask question before writing the code
myself from File::Spec splitpath, catpath, splitdir, catdir

If there is a simple existing way to do that, please just tell
me. Thank you in advance.

If you just want to check your ideas, here is a short test case:

my @testCase = (
"~/dir1/../dir2",
"~/dir1/../dir2/f1",
"~/dir1/./dir2/f1",
"/dir1/../dir2",
"/dir1/../dir2/f1",
"/dir1/./dir2/f1",
);
print "Result: " . join( " ", map { File::Spec->canonpath($_) } @testCase ) . "\n";

Result: ~/dir1/../dir2 ~/dir1/../dir2/f1 ~/dir1/dir2/f1 /dir1/../dir2 /dir1/../dir2/f1 /dir1/dir2/f1

Regards,
 
P

Peter J. Acklam

Yves Martin said:
I'm looking for a way to clean file paths like the following:
~/dir1/../dir2
~/dir1/../dir2/f1
~/dir1/./dir2/f1
/dir1/../dir2
/dir1/../dir2/f1
/dir1/./dir2/f1

I tested Cwd->realpath that reduce ./ and ../ but has many
drawbacks:

None of them are drawbacks. I'm not sure you understand what the
purpose of it is. :)
- it does not understand ~/

It works fine for me:

$ cd /var/tmp
$ mkdir -p '~/dir1'
$ perl -MCwd -wle 'print Cwd::realpath "~/dir1"'
/var/tmp/~/dir1

You can't expect it to expand "~" into your home directory since
"~" might very well be an existing directory.

If you want tilde expansion, do it yourself. It is mentioned in
the FAQ how to do this.
- it follows symbolic links

Of course it does. How else would it find the absolute path?
- it checks path over the filesystem, so an unexisting file
becomes undef.

That makes sense, since a non-existing file has no real pathname.
Results I expect are obviously:
~/dir1/../dir2 -> ~/dir2
~/dir1/../dir2/f1 -> ~/dir2/f1
~/dir1/./dir2/f1 -> ~/dir1/dir2/f1
/dir1/../dir2 -> /dir2
/dir1/../dir2/f1 -> /dir2/f1
/dir1/./dir2/f1 -> /dir1/dir2/f1

what ever the filesystem looks like (non existing file or
symlink) and which is multiplatform of course ;)

You can't simplify '~/dir1/../dir2' into '~/dir2' unless you make
sure that 'dir1' is a directory. If 'dir1' is a symbolic link,
then '~/dir1/../dir2' and '~/dir2' might be two totally different
directories.

Peter
 
Y

Yves Martin

Sorry for the misunderstanding, just forget what I said about
'realpath'.

My aim is not to get the real path of a file BUT to clean up file
paths according to the following examples:

~/dir1/../dir2 -> ~/dir2
~/dir1/../dir2/f1 -> ~/dir2/f1
~/dir1/./dir2/f1 -> ~/dir1/dir2/f1
/dir1/../dir2 -> /dir2
/dir1/../dir2/f1 -> /dir2/f1
/dir1/./dir2/f1 -> /dir1/dir2/f1

I have tried many things: File::Spec->canonpath, Cwd->realpath
... and realpath was the closest I found with the drawbacks I
enumerated.

So I'm just looking for a function/package than may achieve the
clean up I described in a multi-platform way without checking the
filesystem.

Sorry again.
 
P

Peter J. Acklam

Yves Martin said:
My aim is not to get the real path of a file BUT to clean up
file paths according to the following examples:

~/dir1/../dir2 -> ~/dir2
~/dir1/../dir2/f1 -> ~/dir2/f1
~/dir1/./dir2/f1 -> ~/dir1/dir2/f1
/dir1/../dir2 -> /dir2
/dir1/../dir2/f1 -> /dir2/f1
/dir1/./dir2/f1 -> /dir1/dir2/f1

I have tried many things: File::Spec->canonpath, Cwd->realpath
... and realpath was the closest I found with the drawbacks I
enumerated.

I see, so it's ok that '~/dir1/../dir2' becomes '~/dir2' even if
they are actually two different directories?

Oh, well, try this:

sub trimpath {
local $_ = shift;

require File::Spec::Functions;
$_ = File::Spec::Functions::canonpath($_);

# /foo/bar/../baz -> /foo/baz
1 while s{/(?!\.\.?/)[^/]+/\.\.(/|$)}{$1}gx;

# /../../foo -> /foo
s|^/(\.\./)+|/|;

return $_;
}

Peter
 
J

Joe Smith

Yves said:
(e-mail address removed) (Peter J. Acklam) writes:

My aim is not to get the real path of a file BUT to clean up file
paths according to the following examples:

~/dir1/../dir2 -> ~/dir2

On my system, ~/src = /home/jms/src but
~/src/../dir2 = /home/common/sources/dir2, not /home/jms/dir2.

Why would you want to use an algorithm that is guarenteed to
return wrong data when symlinks are use? The only way to get
completely accurate results is check the filesystem on the
server (something that cannot be done on a proxy).

-Joe
 
P

Peter J. Acklam

Joe Smith said:
On my system, ~/src = /home/jms/src but
~/src/../dir2 = /home/common/sources/dir2, not /home/jms/dir2.

Why would you want to use an algorithm that is guarenteed to
return wrong data when symlinks are use?

It's not guaranteed to be wrong. For instance, it won't be wrong
if the symlink is to a directory within the same directory as the
symlink.

So the problem isn't that it is guaranteed to fail, but that it
isn't guaranteed to succeed. Of course, that is hardly any
better.

Peter
 
Y

Yves Martin

It's not guaranteed to be wrong. For instance, it won't be wrong
if the symlink is to a directory within the same directory as the
symlink.

So the problem isn't that it is guaranteed to fail, but that it
isn't guaranteed to succeed. Of course, that is hardly any
better.

Thank you for your help.

In fact, my perl script is supposed to read build system
configuration files (generated and read in Java) and to provide
Emacs JDEE project files (prj.el)

The source configuration files have known constraints (relative path
and no symbolic links at that level - but perhaps out of the build
system) and the prj.el supports "~/" but not ../ or ./ in path. Here
is the complete story.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,990
Messages
2,570,211
Members
46,796
Latest member
SteveBreed

Latest Threads

Top