1

I wonder how it would be possible to use a regular expression to simplify double dots from a file path (the path may not actually exist) ?

For example change /my/path/to/.././my/./../../file.txt into /my/file.txt or path/./to/../../../file.txt into ../file.txt.

Is it possible to do this in one command in bash ? (using sed for example, not a complicated python or perl script)

edit: I came across this question but realpath isn't available on the computer I use.

edit: From F.J 's solution, I ended up building the following regex which works in more general cases (does not work if some folder of the path is named ....):

sed -e 's|/\./|/|g' -e ':a' -e 's|\.\./\.\./|../..../|g' -e 's|^[^/]*/\.\.\/||' -e 't a' -e 's|/[^/]*/\.\.\/|/|' -e 't a' -e 's|\.\.\.\./|../|g' -e 't a'
Community
  • 1
  • 1
Mat
  • 1,022
  • 2
  • 9
  • 24
  • 2
    Is the `realpath` command unavailable, or the [`realpath()`](http://pubs.opengroup.org/onlinepubs/9699919799/functions/realpath.html) function? If you have the function, an adequate program is: `#define _XOPEN_SOURCE 600` `#include ` `#include ` `#include ` `int main(int argc, char **argv) { int rc = 0; for (int i = 1; i < argc; i++) { char realname[_POSIX_PATH_MAX]; if (realpath(argv[i], realname) != 0) printf("%s\n", realname); else { fprintf(stderr, "Failed to evaluate realpath for %s\n", argv[i]); rc = 1; } } return(rc); }` – Jonathan Leffler Apr 20 '13 at 00:01
  • 1
    Only the `realpath` command is unavailable. However, I would like to use this inside a redistributable bash script, which makes this solution not very convenient. Thanks anyway. – Mat Apr 20 '13 at 12:02

1 Answers1

4

Try the following:

sed -e 's|/\./|/|g' -e ':a' -e 's|/[^/]*/\.\./|/|' -e 't a'

Example:

$ echo '/my/path/to/.././my/./../../file.txt' |
  sed -e 's|/\./|/|g' -e ':a' -e 's|/[^/]*/\.\./|/|' -e 't a'
/my/file.txt

Here is a description of the approach:

read line
replace all '/\./' in line with '/'
while there is a match of '/[^/]*/\.\./' {
    replace first occurrence of '/[^/]*/\.\./' in line with '/'
}
output line
Andrew Clark
  • 202,379
  • 35
  • 273
  • 306
  • I endeed up using the following command which also works if the path should start with `../` : `sed -e 's|/\./|/|g' -e ':a' -e 's|/[^/]*/\.\./|/|' -e 't a' -e 's|^[^/]*/\.\./|../|'`. Thank you. – Mat Apr 19 '13 at 17:10
  • why doesn't this: `sed -e 's|/\./|/|g' -e ':a' -e 's|[^/]+/\.\./|/|' -e 't a'` work ? – Mat Apr 19 '13 at 17:14
  • @Mat It could be the `+`, try changing it to `\+`. – Andrew Clark Apr 19 '13 at 17:58
  • 1
    @Mat: The `+` notation is not understood by Standard (POSIX) `sed`. Some (possibly many) versions of `sed` recognize an option `-r` to use extended regular expressions where `+` and `|` and so on are recognized. Check the man page on your system. – Jonathan Leffler Apr 19 '13 at 18:33
  • The problem with you approach is that is ends up reducing `../../` to nothing. I proposed a solution in my question that prevents that. – Mat Apr 22 '13 at 11:46