9
$\begingroup$

How can I check if a filesystem path is absolute or relative in a cross-platform way?

For my purposes, I consider a path absolute if points to a location that is independent of Directory[].

I only need to check paths valid on the current operating system. I never need to check Windows paths on Unix or vice versa.


Examples of absolute paths on Unix:

  • /foo/bar
  • ~/baz (This is sometimes considered relative as it is user $UserName-dependent. However, it is not Directory[]-dependent.)

On Windows:

  • F:\foo\bar
  • F:/foo/bar

Relative paths on Unix:

  • foo/bar
  • ., ./foo, ..

Relative paths on Windows:

  • foo\bar
  • ., .\bar, .., etc.
  • \foo\bar (May be either of C:\ or D:\, etc.)
  • any of the above with a / separator instead of \

These are just a few examples. I may have missed several special cases. I am looking for something which always works and preferably doesn't depend on my own partial understanding of what forms an absolute or relative path may take on different OSs. I would rather not start matching strings, at least not without a reference document which gives an exhaustive list of all possible path syntaxes both for absolute and relative paths.

The ideal solution would use a feature that asks the operating system directly:

$\endgroup$
7
$\begingroup$

The only reliable way I found so far was by using Java through J/Link:

Needs["JLink`"]

Clear[absolutePathQ]
absolutePathQ[File[path_String]] := absolutePathQ[path]
absolutePathQ[path_String] :=
 Module[{file},
   InstallJava[];
   JavaBlock[
     file = JavaNew["java.io.File", path];
     file@isAbsolute[]
   ]
 ]

My best pure Mathematica attempt was the following:

Clear[absolutePathQ]
absolutePathQ[path_] := ExpandFileName[path] === FileNameJoin@FileNameSplit[path]

As @george2079 points out, this fails on absolute paths like /var/../bin/sh.


Neither of these two methods consider ~ absolute. That is not unreasonable and not really a problem. This case is special enough that I can always check StringStartsQ[path, "~"].

$\endgroup$
  • 1
    $\begingroup$ this will break on liunx if you have . or .. embedded in an absolute path, eg /var/../bin/sh gets cleaned up by ExpandFileName to /bin/sh $\endgroup$ – george2079 Nov 29 '16 at 15:47
  • $\begingroup$ A possible alternative was comparing the name with its AbsoluteFileName, but it seems that it has issues with symlinks that ExpandFileName does not. Also, ExpandFileName and FileNameSplit work on File, so extracting the filename from them is redundant. $\endgroup$ – rcollyer Nov 29 '16 at 20:45
  • $\begingroup$ @rcollyer originally I had ExpandFileName[path] === path, so it wasn't redundant. Now it is. $\endgroup$ – Szabolcs Nov 29 '16 at 20:52
  • $\begingroup$ Ah, I see. that would do it. :) $\endgroup$ – rcollyer Nov 29 '16 at 20:59

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.