diff --git a/src/builtins/path.cpp b/src/builtins/path.cpp index 33ccc4ee3..82b1dd7f7 100644 --- a/src/builtins/path.cpp +++ b/src/builtins/path.cpp @@ -631,7 +631,28 @@ static int path_real(parser_t &parser, io_streams_t &streams, int argc, const wc auto real = wrealpath(*arg); if (!real) { - continue; + // The path doesn't exist, so we go up until we find + // something that does. + wcstring next = *arg; + // First add $PWD if we're relative + if (!next.empty() && next[0] != L'/') { + next = wgetcwd() + L"/" + next; + } + auto rest = wbasename(next); + while(!next.empty() && next != L"/") { + next = wdirname(next); + real = wrealpath(next); + if (real) { + next.push_back(L'/'); + next.append(rest); + real = normalize_path(next, false); + break; + } + rest = wbasename(next) + L'/' + rest; + } + if (!real) { + continue; + } } // Return 0 if we found a realpath. diff --git a/tests/checks/path.fish b/tests/checks/path.fish index f53b6c191..1a8de987b 100644 --- a/tests/checks/path.fish +++ b/tests/checks/path.fish @@ -110,3 +110,14 @@ path real bin//sh | string match -r -- 'bin/bash$' # The "//" is squashed, and the symlink is resolved. # sh here is bash # CHECK: bin/bash + +# `path real` with nonexistent paths +set -l path (path real foo/bar) +string match -rq "^"(string escape --style=regex -- $PWD)'/' -- $path +and echo It matches pwd! +# CHECK: It matches pwd! +string replace -r "^"(string escape --style=regex -- $PWD)'/' "" -- $path +# CHECK: foo/bar + +path real /banana//terracota/terracota/booooo/../pie +# CHECK: /banana/terracota/terracota/pie