Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

cd follows symlinks #3350

Closed
2 tasks done
kozec opened this issue Sep 2, 2016 · 39 comments
Closed
2 tasks done

cd follows symlinks #3350

kozec opened this issue Sep 2, 2016 · 39 comments
Assignees
Milestone

Comments

@kozec
Copy link

kozec commented Sep 2, 2016

  • Have you checked if problem occurs with fish 2.3.1?
  • Tried fish without third-party customizations (check sh -c 'env HOME=$(mktemp -d) fish')?

fish version installed (fish --version): fish, version 2.3.1

OS/terminal used: Manjaro Linux, XFCE4-Terminal

Hello. I'm trying to launch application using Wine, what means I need path without non-ascii characters. Since application is installed to directory with Japanese name, I've created symlink and now I can't cd into it - for whatever crazy reason, cd follows that link and ends in original directory.

tom@silver /d/t/.l/s/S/S/common> ls -l Gods\ One\ Day\ World
lrwxrwxrwx 1 tom tom 89 Sep  2 18:52 'Gods One Day World' -> ' 神明的一天世界(God'\''s One Day World)'/
tom@silver /d/t/.l/s/S/S/common> cd Gods\ One\ Day\ World/
tom@silver /d/t/.l/s/S/S/c/ 神明的一天世界(God's One Day World)> pwd
/data/tom/.local/share/Steam/SteamApps/common/ 神明的一天世界(God's One Day World)

Reproduction steps

  1. Launch fish
  2. create any directory (mkdir /tmp/any)
  3. create symlink to that directory (ln -s /tmp/any /tmp/link)
  4. navigate to created link (cd /tmp/link)

Expected results

Fish should navigate to link, /tmp/link

Actual results

Cwd is set to target, /tmp/any

@faho
Copy link
Member

faho commented Sep 2, 2016

This was reported (and discussed to death) in #1957.

The thing is, we're not doing anything special. We're using getcwd to give us a directory, and that gives it to us resolved. Bash is "unresolving" the symlink to give the appearance that that is your current directory. That can be very confusing, because that's not actually how the system works. As an example, try (in bash), cd /tmp/link; ls ../link. ls will error out because it can't find "link", because it's not actually in /tmp/link. It's in /tmp/any.

How is wine reacting to the PWD it is given?

Would env PWD=/tmp/link wine work?

@faho faho added the question label Sep 2, 2016
@floam
Copy link
Member

floam commented Sep 2, 2016

So, this won't work. It's not possible use symlinks to offer an "aliased" current working directory in fish, because fish will always resolve the real path it points at and place you there. So for example OS X if you cd /etc/ fish will betray the the /private/etc/ path you're actually at. This is thought to be worth it on balance. Maybe a hard link can work.

@faho
Copy link
Member

faho commented Sep 2, 2016

@floam: Directory hardlinks aren't actually a thing.

What might work is cp --link. This will create a directory and fill it with symlinks.

EDIT: Or pass the full path to wine.

@floam
Copy link
Member

floam commented Sep 2, 2016

Hey man, I have directory hard links.

I'd give up and just let whatever launches the game for me be a POSIX shell script that handles PWD normally.

@kozec
Copy link
Author

kozec commented Sep 2, 2016

@faho

As an example, try (in bash), cd /tmp/link; ls ../link. ls will error out because it can't find "link", because it's not actually in /tmp/link. It's in /tmp/any.

I'm not sure if I follow

~> bash
[tom@silver ~]$ cd /tmp/
[tom@silver tmp]$ ls -l link
lrwxrwxrwx 1 tom tom 8 Sep  2 19:02 link -> /tmp/any
[tom@silver tmp]$ cd link
[tom@silver link]$ touch something
[tom@silver link]$ ls ../link
something

Anyway, setting PWD env.variable works, but it's rather PITA, especially because path to that directory is 90 chars long. Running cd .. ; bash is probably faster. And now I'm seriously scared navigating to my project directory, where practically everything is symlink :(

But considering that problem was "discussed to death" in closed issue, I'd guess this behavior is not going to be fixed.

@krader1961
Copy link
Contributor

Your experiment only worked because the symlink was to a directory with the same parent as the link. It doesn't work in the general case. Try this example:

$ mkdir ~/tmp/any
$ ln -s ~/tmp/any link
$ ls -l link
lrwxr-x--- 1 krader wheel 21 Sep  2 10:50 link -> /Users/krader/tmp/any/
$ cd link
/tmp/link
$ ls ../link
ls: cannot access '../link': No such file or directory

I'd guess this behavior is not going to be fixed.

There's nothing to fix since the behavior isn't broken. It's merely different than what some other shells do. Both behaviors can be justified. The fish community simply believes that exposing the truth results in fewer problems.

@faho
Copy link
Member

faho commented Sep 2, 2016

I'm not sure if I follow

Sorry, I mixed that up.

Do:

mkdir -p /tmp/a/b
ln -s /tmp/a/b /tmp/link
cd /tmp/link # bash's PWD is now /tmp/link, fish would show /tmp/a/b
echo ../* # shows b
ls ../a # with bash's logic, this should show the contents of /tmp/link/../a (which is /tmp/a) - which would be "b"

Really the point is that bash and basically everything else disagree on what "../" means.

And now I'm seriously scared navigating to my project directory, where practically everything is symlink :(

Be less scared. When you do anything with relative paths, you can be confident that what fish shows you is what will happen. When you use "../SOMETHING", you can be sure that you could "cd .." and use "SOMETHING" instead.

@kozec
Copy link
Author

kozec commented Sep 2, 2016

OK, I probably have a lot to say about this not being broken, but after reading through that linked issue I'm fairly sure I'be just repeating what others said.

I ended up "fixing" it using chsh. Thank you for your assistance :)

@kozec kozec closed this as completed Sep 2, 2016
@floam
Copy link
Member

floam commented Sep 2, 2016

I'd guess this behavior is not going to be fixed.

It can be just as soon as folks are convinced. It's happened before.

@kozec I actually share your preference. There is not really a unified front here holding discussion back. Somehow they allow me (for now) to commit to the repository!

For me, anyhow, what fish does can be just as counterintuitive as bash, but more annoying, particularly when fish fails to let me "back out" out of a symlink the same way I came in and forces me to navigate out of whatever directory maze the symlinks dropped me into or remember directory history. Others have good reasons to not want my preference too, though. I can only bide my time.

I hate to see threads like this close up - obviously I think it's useful to get the data points in 😄

@ridiculousfish
Copy link
Member

I'd be open to changing this at some point in the future too - AFAIK nobody really loves the current behavior. That doesn't mean we should take bash's behavior wholesale, but it's worth thinking if we can make the UE better here. The "back out is annoying" stuff that floam mentions is real.

@faho
Copy link
Member

faho commented Sep 2, 2016

but it's worth thinking if we can make the UE better here.

Well, HOW? To me it seems like we either display and use the resolved path, or we display and use the unresolved one.

Like I said, if you're gonna use relative paths with external commands, then it's nicer if you can count on the cwd matching - if I see the path is "SOMETHING/foo/bar/baz", and I want to remove the "spam" file next to foo, then I want to know that "../../../spam" will refer to it, without checking if foo, bar or baz are symlinks.

To me, that strongly points towards resolving.

The "annoying back out" is an issue, but it can be mitigated with e.g. cd - or popd. It might be possible to see if the last cd (or even the last cd through that particular dir) was through a symlink and then offer that as well, but that seems a bit magicky to me and I have no idea how it would work in scripts.

@floam
Copy link
Member

floam commented Sep 2, 2016

Well, HOW? To me it seems like we either display and use the resolved path, or we display and use the unresolved one.

There's got to be a lot of things we could do aside from these two options. You could change how you display it - to make it less jarring, when you are taken through the wormhole through the filesystem like this.

There must be somehow with arrows or animation or something to show the user in their prompt how /private/tmp/a/b occurred, first by /tmp/ being a symlink to /private/tmp, and then /tmp->{private/tmp}/link->{a/b}].

> cd /tmp/link
> pwd /private/tmp/a/b

Maybe the interaction? With something that felt like a completion, one could somehow select the different universes where it "expands" one way versus the other as you're cd'ing. I dunno!

Could have it show the real directories but cd .. could skips back through the logical parents instead of the absolute resolved ones?

@floam
Copy link
Member

floam commented Sep 2, 2016

The "annoying back out" is an issue, but it can be mitigated with e.g. cd - or popd

Meh - that's really lousy. That still makes you deal with becoming lost and so disoriented you had to pull out the CSI tools. I never use directory history, except for when something bad happens to me.

To me, having the ground sort of ripped out from underneath you in a way that is going to assuredly not not permit you to get home without cd / or teleporting with directory history... it's a way worse problem than this "I need to be sure that ../../../x is the same path I predict it is - the truth!` problem. I want a step backwards to be where I just came form, and in front of me any symlink hallucinationsremain consistent after I step through them, and for as long as possible after that. Anything but the truth!

@krader1961
Copy link
Contributor

The "back out is annoying" stuff that floam mentions is real.

AFAICT that's the only complaint of all I've read that I can take seriously. Most of the other arguments seem to involve profound misunderstandings about how symlinks work. Consider how many people ask that this be fixed rather than changed. They truly think fish is broken. And those misunderstandings are part of why I'm reluctant to emulate, say, bash's behavior. If I could be convinced that most people do in fact understand what is going on and know that using relative paths after cd'ing through a symlink won't work as they expect (in the general case) I'd be more comfortable making fish emulate bash in this regard.

I wouldn't mind making cd magic with respect to symlinks. But I feel pretty strongly that $PWD should continue to be set to the absolute, canonical, path as returned by realpath(3). If for no other reason than changing it is a backward incompatible change that could break a large number of scripts. Perhaps we need to introduce a $CD_PWD var or some such.

I know I'm atypical since I've been using UNIX since the early 1980s and am thus used to living with shortcomings far more profound than this one. But like @faho I guess I don't see what's so hard about using cd - or something like my mcd function that I've posted in another issue (personally I loath pushd/popd). For that matter, I generally know when I've cd'd through a symlink and what relative paths I can safely use at that point. For example, I have a ~/projects symlink that points to a directory in a different file system. If I cd projects/fish-shell I know I can safely do diff ../old-fish/somefile somefile. I suspect all of the core devs on this project have similar situational awareness with respect to the directory symlinks they've created. I wouldn't be surprised if most of the people asking that fish act like bash lack that awareness.

@ridiculousfish
Copy link
Member

Consider how many people ask that this be fixed rather than changed. They truly think fish is broken. And those misunderstandings are part of why I'm reluctant to emulate, say, bash's behavior.

That's a great point. Users think that it is fish that resolves symlinks, but of course the OS resolves symlinks whether you like it or not. Getting to bash's behavior does not mean taking anything out, it would mean adding new stuff in.

$PWD should continue to be set to the absolute, canonical, path as returned by realpath()

Yep.

@floam
Copy link
Member

floam commented Sep 3, 2016

That's a great point. Users think that it is fish that resolves symlinks, but of course the OS resolves symlinks whether you like it or not.

Yeah - where /logically_here is a symlink to some mess we don't want to be concerned with:

int main(int argc, char **argv) {
    char cwd[1024];
    chdir("/logically_here");
    if (getcwd(cwd, 1024) == 0);
        printf("%s", cwd);
}
> sudo ln -s (mktemp -d) /logically_here
> ./a.out
/private/var/folders/d6/t4zb8t9s4hb6835nktgtt4jc0000gp/T/tmp.VRDDSQfc⏎

... is going to give us the unpleasant truth with these symlinks resolved without us doing anything extra. If we want to discern a physical working directory path from the logical one we need a little help from the shell here by recording something when we cd. $PWD can be used to store the absolute logical path when you cd.

The problem on fish is that $PWD is not actually the logical working directory. The last directory history entry is also problematic, it has been realthpath'd and you cannot see the path you cd'd to by the path name you used there. You can't overwrite $PWD it if you sit down and try to fix our cd wrapper yourself.

Getting to bash's behavior does not mean taking anything out, it would mean adding new stuff in.

Fish is undeniably adding something when $PWD gets realpath'd instead of just recorded during cd.

$PWD should continue to be set to the absolute, canonical, path as returned by realpath()

Why?

$PWD exists explicitly for this purpose, no? It should be set to the logical working directory path - it's only necessary that we make sure it is absolute and try to reduce ..'s inside.

Why do we even need $PWD otherwise? Why do we not want it to take into account the context as to how the current directory was accessed?

@faho
Copy link
Member

faho commented Sep 3, 2016

The problem on fish is that $PWD is not actually the same as the last directory history entry. You can't even overwrite it if you sit down and try to fix our cd wrapper yourself. To me, our cd is undeniably adding something here.

Do you mean $dirstack? Then that's actually wrong. pushd uses command pwd (for whatever reason - we also have the builtin and the variable).

@floam
Copy link
Member

floam commented Sep 3, 2016

I meant whatever I see when I type dirh -- I actually assumed something incorrect about it though. I had assumed it was using the path names as I had specified them during cd's because it's always mentioned as a solution when one is annoyed by the fish behavior - but it too is always physical resolved paths. I revised my remarks:

The problem on fish is that $PWD is not actually the logical working directory. The last directory history entry is also problematic, it has been realthpath'd and you cannot see the path you cd'd to by the path name you used there. You can't overwrite $PWD it if you sit down and try to fix our cd wrapper yourself.

@faho
Copy link
Member

faho commented Sep 4, 2016

The problem on fish is that $PWD is not actually the logical working directory.

Well, we're kinda debating if and why that is problematic. If it were the "logical working directory" (is that something you came up with or used elsewhere? either way, let's use that for now), then interaction with other tools is problematic. And the way it is now is something people aren't used to, and that makes e.g. cd .. behave in unexpected (but consistent) ways.

Really, symlinks are weird. The weirdness isn't something fish has invented, it's the UNIX WAY^tm. The question is if there's something we can do to alleviate that weirdness. (Personally, it seems like it would be much more understandable if paths weren't canonicalized all the time in the system. But that's not the way it is and I may very well be missing something)

But whatever that thing is, I don't think it's what bash is doing. It might be a better interface. An addition to cd, a different way of changing directories. I don't know.

@floam
Copy link
Member

floam commented Sep 4, 2016

If it were the "logical working directory" (is that something you came up with or used elsewhere? either way, let's use that for now),

I think that's POSIX/UNIX.

@floam
Copy link
Member

floam commented Sep 4, 2016

"logical working directory" I guess I invented, they never actually put the terms together like that. I'm referring to -L on cd, where it logically resolves dot-dot. A logical current working directory, to me, is what we'd first need to know to hope to figure out the logical parent of our current working directory. It requires a $PWD that hasn't been canonicalized after our last cd. I think fish could set $PWD to the logical one but keep the current pwd -P-alike behavior if you folks prefer that, without really breaking stuff, right?
Not asking for the the actual -P/-L options but here's how they put it:

cd:

-L Handle the operand dot-dot logically; symbolic link components shall not be resolved before dot-dot components are processed
-P Handle the operand dot-dot physically; symbolic link components shall be resolved before dot-dot components are processed

pwd:

-L If the PWD environment variable contains an absolute pathname of the current directory that does not contain the filenames dot or dot-dot, pwd shall write this pathname to standard output. Otherwise, the -L option shall behave as the -P option.
-P The absolute pathname written shall not contain filenames that, in the context of the pathname, refer to files of type symbolic link.

My actual goals aren't specifically -P or -L to even be options, just for this to work:

sudo ln -s (mktemp -d) /logically_here
cd /logically_here
# $PWD is "/logically_here". 
cd ..
# I'm back in / now

It makes sense. Shouldn't need to dig into history or use pushd first. It'd (probably?) be easier if $PWD wasn't canonicalized. Why does fish have PWD as a read-only electric? We can't set it ourselves in cd.fish which is unfortunate.

@krader1961
Copy link
Contributor

"logical working directory" I guess I invented

In the three decades I've been using UNIX I've never seen the phrase before now. There is no such thing. There is only the "current working directory". In fact man bash on my system says as the first sentence of the pwd command description:

Print the absolute pathname of the current working directory.

That is the default behavior. Getting the behavior of bash's pwd -L does not require that the $PWD variable be the "logical path" (to invent another term). It only requires that the shell track the path used to reach the cwd via cd commands. We would expose that through a new env var so as not to break any existing use of $PWD.

The fish cd command could then be modified to use the "logical path" by default when evaluating a relative path or we could decide to add a flag to get that behavior if there is reason to think changing the default behavior would cause problems. I can't think of any problems such a change would introduce but before we make the "logical" cd behavior the default we need to think long and hard about the ramifications. Too, given that a user could simply abbr cd 'cd -L' to get the bash like behavior it may be prudent to leave the default behavior unchanged (i.e., interpret relative paths as relative to the canonical cwd).

I have to say I'm also confused by the bash documentation for the cd command. It doesn't explicitly say whether -P or -L is the default behavior. The description of the -L option also seems to imply that it doesn't simply check for leading ../ sequences. It seems to imply that even a /../ in the middle of the path is handled by bash as if the preceding path component was not a symbolic link even if it was. Which would mean it isn't simply passing the value to the chdir(2) syscall. If true that is behavior I definitely think we do not want to emulate. We should only be handling leading sequences of ../.

@floam
Copy link
Member

floam commented Sep 4, 2016

There is only the "current working directory". In the three decades I've been using UNIX I've never seen the phrase before now. There is no such thing. In fact man bash on my system says as the first sentence of the pwd command description:

Print the absolute pathname of the current working directory.

Which is wrong, don't you think? Seems to me like it should say "Print an absolute pathname of the current working directory" - because there's not just one.

That is the default behavior. Getting the behavior of bash's pwd -L

Uh, in bash pwd -L is the default, isn't it? It is in POSIX.

We would expose that through a new env var so as not to break any existing use of $PWD.

How can it break anything? It's longstanding standard behavior in UNIX for $PWD to be able to contain symlinks. If you wrote software that crashed when $PWD was correct and has path components which are symbolic links, it's going to crash in every shell I know of other than fish.

Getting the behavior of bash's pwd -L does not require that the $PWD variable be the "logical path" (to invent another term). It only requires that the shell track the path used to reach the cwd via cd commands.

Yes, all I meant by "logical" PWD is that the PWD is not canonicalized but allowed to contain symlinks for the current working directory as tracked by cd or set by anything. This is what I'd like to do too!

@krader1961
Copy link
Contributor

Seems to me like it should say "Print an absolute pathname of the current working directory" - because there's not just one.

You just made my earlier point that people don't understand how UNIX file systems, and symlinks specifically, work. There is indeed "just one" canonical path to a given object in the classic UNIX file system model. I'm obviously ignoring esoteric extensions and things like chroot(2) which alter the idea of the file system root because they are not relevant to this discussion. But ignoring those unusual situations there is indeed a single path from the file system root to a given object that does not include symlinks.

Uh, in bash pwd -L is the default, isn't it?

I can't tell from reading the man page but given that so many bash refugees are asking us to emulate it I would bet that is the default. So what? As for it being POSIX standard behavior a) I haven't seen any references to the relevant standards doc, and b) fish explicitly states as an anti-goal blindly adhering to POSIX.

It's longstanding standard behavior in UNIX for $PWD to be able to contain symlinks.

For bash. Not for fish let alone all shells as you imply. Since fish has historically canonicalized PWD changing that behavior can in fact confuse fish users and break existing scripts. Why do you insist on changing the behavior of PWD rather than simply introducing a new var that provides the "logical" (symlink including) path semantics?

@floam
Copy link
Member

floam commented Sep 4, 2016

I can't tell from reading the man page but given that so many bash refugees are asking us to emulate it I would bet that is the default. So what?

I'm just quoting the POSIX docs and my own experience with csh and ksh. The reason I asked is you mentioned compatibility issues and have been using UNIX for 30 years.

I also can't tell from the manpages - I always get the stupid manpage for the builtins command. I had to check bash -c pwd -h after you mentioned the -L behavior in a way that made me think bash might be exceptional rather than typical here.

Can you tell me a single shell that doesn't take a PWD with symlinks? One?

Why do you insist on changing the behavior of PWD rather than simply introducing a new var that provides the "logical" (symlink including) path semantics?

Because it's the normal way to do it.

@floam
Copy link
Member

floam commented Sep 4, 2016

You just made my earlier point that people don't understand how UNIX file systems, and symlinks specifically, work.

Hey, friend... you are wrong. Maybe it's the fault of the documentation you were looking at - like I said they should change it from "the". ksh has the correct wording:

pwd writes an absolute pathname of the current working directory to standard output. An absolute pathname is a pathname that begins with / that does not contains any . or .. components.

If both -L and -P are specified, the last one specified will be used. If neither -P or -L is specified then the behavior will be determined by the getconf parameter PATH_RESOLVE. If PATH_RESOLVE is physical, then the behavior will be as if -P were specified. Otherwise, the behavior will be as if -L were specified.

The pwd command without arguments in non-fish shells can return any of the path names for the current working directory, which can be numerous depending on if there are symlinks to the current directory or parents of it, and depending on if the inherited $PWD is "canonicalized" or if it's a symlink to it thus logical. I think probably better than "logical current working directory" is simply "effective CWD" here.

pwd shouldn't come up with a differing canonicalized version of the shell's effective CWD unless $PWD is disrupted or you are using a noteworthy shell.

@krader1961
Copy link
Contributor

The pwd command without arguments in non-fish shells has have many potential outputs possible for the current working directory

Yes, it does. But only when it uses the $PWD env var to influence its output. That does not change the fact that there is only one canonical path, which does not include symlinks, to the cwd.

Can you tell me a single shell that doesn't take a PWD with symlinks?

I cannot make sense of that sentence. I would not expect any sane shell to blindly use the value of env var PWD. Heck, I wouldn't expect any shell to use that var at all when it starts running. Unless it goes to the trouble of validating the value expands to the same canonical path returned by the getcwd(3) function and perhaps not even then.

Your comments intrigued me enough to do some simple experiments on Apple's latest macOS Sierra beta release (a BSD derivative). The results were surprising and help explain why you, and others, are arguing for bash like behavior.

Let's start a ksh93 shell and create a directory and two symlinks to that directory all with the same parent directory:

$ /bin/ksh
$ cd ~/tmp
$ mkdir x
$ ln -s x y
$ ln -s x z
$ ls -l
drwxr-x--- 2 krader staff      68 Sep  3 20:38 x
lrwxr-x--- 1 krader staff       1 Sep  3 20:23 y -> x
lrwxr-x--- 1 krader staff       1 Sep  3 20:38 z -> x

Now cd into one of the symlinks:

$ cd y
$ pwd
/Users/krader/tmp/y
$ /bin/pwd
/Users/krader/tmp/y

That /bin/pwd reports the logical (symlink) path surprised me. What happens if we lie to it?

$ env PWD=/Users/krader/tmp/z /bin/pwd
/Users/krader/tmp/z
$ /usr/local/opt/coreutils/libexec/gnubin/pwd
/Users/krader/tmp/x

Wow! It blindly reports the lie. Whereas the GNU (Linux) pwd command reports the truth. Note that if we give it a different lie it reports the canonical path rather than parroting the $PWD value:

$ env PWD=/Users/krader/tmp/abc /bin/pwd
/Users/krader/tmp/x

Now remove the alternate "z" symlink and observe what happens. Note that I have not done a cd or any other command other than those above at this point:

$ rm ../z
$ env PWD=/Users/krader/tmp/z /bin/pwd
/Users/krader/tmp/x

Note that up until I removed the "z" symlink the pwd command was happy to report it as the path that the user took to reach the "x" directory. Now it no longer does so. Yet I have not done a cd command. So why is Clearly the$PWD` value is not blindly used by those commands.

Note that at this point we are still in the "y" logical (i.e., symlink) path. What happens if we remove that symlink? Note that the ksh93 builtin still reports the now invalid logical path while the external command resorts to reporting the canonical, physical, path:

$ pwd
/Users/krader/tmp/y
$ /bin/pwd
/Users/krader/tmp/y
$ rm ../y
$ pwd
/Users/krader/tmp/y
$ /bin/pwd
/Users/krader/tmp/x

I'm sorry, but that behavior is just FUBAR. We should not emulate it. It was clearly implemented by people who were trying to paper over the issues created by symlinks. They failed except in trivial cases. It would have been better if they had recognized the problems with their "solution" and chosen to only alter the behavior of interactive uses of the cd command.

@floam
Copy link
Member

floam commented Sep 4, 2016

$ rm ../y
$ pwd
/Users/krader/tmp/y

Wipes tear from eye. Oh, it's a beautiful thing!

cd $(pwd) works perfectly as well.

$ pwd
/Users/floam/tmp/y
$ ls ..
x   y   z
$ # someone just deleted it
$ pwd
/Users/floam/tmp/y
$ ls ..                    
x   z
$ cd $(pwd)
$ pwd
/Users/floam/tmp/y

Fish can't even handle the pwd-after-unlink case while it is safely cd'd into tmp/x. Obviously we don't know how bad we'll be when it's the symlink that goes away. Makes a mess of it too three times:

[I] floam@rmbp~/t/x> pwd
<E> fish: getcwd() failed with errno 2/No such file or directory
<E> fish: getcwd() failed with errno 2/No such file or directory
<E> fish: getcwd() failed with errno 2/No such file or directory
[I] floam@rmbp~/t/x> 

😞
That's a bug.

$ /bin/pwd
/Users/krader/tmp/x

I'm sorry, but that behavior is just FUBAR.

Meh - what more could it do? $PWD is /Users/krader/tmp/y (which is not a not a file, link, directory, anything to /bin/pwd). It's gone - giving up and using x, the physical path, is not wrong. It's not like ksh could just give it the good fd it has for it.

@floam
Copy link
Member

floam commented Sep 4, 2016

@krader1961 rereading your comments above, are you using ksh as an example of "FUBAR" here? Are you not liking it that when the symlink is deleted it is still showing that as the PWD? The deletion of some component of your path should not change the PWD, whether that component a symlink or a real directory that happened to have been removed. As I'm sure you're aware in Unix-like -OSes you can delete files that a process has a handle on - until that process dies that handle is good! Running pwd does not cause the pwd to change - nor does a failed cd or cd to file you have a good It works perfectly well for the symlink case and the actual-directory-died case - you can even cd to it - again, correctamundo!

We can check and see here, it's got the cwd fd open, so we know if we delete that, like normal files, we should expect nothing about the state to change due to this. He can keep using it until it's closed.

> lsof -L -c ksh -a -x +d tmp
COMMAND   PID  USER   FD   TYPE DEVICE SIZE/OFF     NODE NAME
ksh     21091 floam  cwd    DIR    1,4       68 94098140 tmp/y

I don't know how well bash fares but given that it's probably gotten a decent amount of testing I'll guess it's more or less also able to proceed in those cases.

@krader1961
Copy link
Contributor

I give up. Anyone else care to try explaining why most of the behaviors @floam likes are problematic?

I'm also exceedingly concerned, @floam, when you say things like "That's a bug" after deleting the cwd and fish correctly reports that it can no longer determine the cwd. The error message could perhaps be more useful but it accurately reflects the situation. As someone who used to earn their living debugging Linux kernel crashes I suspect I have better knowledge of how open file descriptors interact with deleted file system objects than you do.

you can even cd to it - again, correctamundo!

No, you cannot. I have no idea what you think you tested but you absolutely cannot cd /some/path after doing rmdir /some/path or, if it was a symlink, rm /some/path. Not even if your cwd is that path. Some references, such as ../ will still work by virtue of your cwd holding a reference to the inode whose name you just deleted. But since the parent directory no longer has an entry named "path" you can't cd ../path. The only reason that appears to work in ksh is that it is resolving the path wholly in user space. It is not calling chdir("../path") because it has calculated that your cd ../path is a no-op. That is something that only works for that specific shell process or child processes which similarly treat env var PWD as a mystical oracle about the truth regarding the cwd. Something that damn few programs will do.

@kozec
Copy link
Author

kozec commented Sep 5, 2016

I didn't really meant to start such discussion, but there is one thing I think is overlooked here.

While I kinda like that high horse krader1961 is sitting on, I don't think that problem is what's "right" way to do it. Problem really is what user expects to happen, especially in something called "user-friendly command line shell." And after 10+ years of using and managing many Linux and some BSD installation, after 10+ years of using bash, bash and then mainly bash, this behavior is simply new trick I'm not going to learn :)

I believe there may be much more people like me.

@floam
Copy link
Member

floam commented Sep 5, 2016

I think you should expect it to work if it is already your CWD. If a chdir fails the CWD should be guaranteed to remain what it was before.

Yeah, it impressed me. I'm probably stark raving mad but I like to say I just like stuff.

On Sep 4, 2016, at 9:57 PM, Kurtis Rader notifications@github.com wrote:

No, you cannot. I have no idea what you think you tested but you absolutely cannot cd /some/path after doing rmdir /some/path or, if it was a symlink, rm /some/path. Not even if your cwd is that path.

@pwwang
Copy link

pwwang commented May 11, 2018

Hopefully this can help somebody ran into the same situation.
https://gist.github.com/pwwang/5ad96bbfb034a91af851c411f0b12a23

@danbst
Copy link

danbst commented Jun 6, 2018

https://github.com/externl/fish-symnav

But seriously, I don't get an argument by @krader1961 that mungling with PWD makes some problems. PWD is readonly in fish, so it's impossible to mangle. pwd then can return whatever it wants, and I vote for it returning logical path.

If I'd like to resolve symlink, I could do cd $(readlink -f path/to/link), but in fish there is no alternative (cd $(preserve-logical-path path/to/link))

Currently cd jumps holy man nowhere, because on my system (NixOS, but will be a thing also on darwin system with Nix package manager) lots is stored inside read-only directory /nix/store. I don't benefit from being inside there without my EXPLICIT will.

danbst@station /n/v/n/p/p/root> ls -l
total 12
lrwxrwxrwx 1 root root 16 May 25 14:00 channels -> channels-14-link/
lrwxrwxrwx 1 root root 60 Apr  3 17:32 channels-12-link -> /nix/store/421xh7d9q37w39jasi1rzbzixhvswzk3-user-environment/
lrwxrwxrwx 1 root root 60 Apr 17 18:43 channels-13-link -> /nix/store/4kdvpchm29w0gwmm28w050wilva3ywk7-user-environment/
lrwxrwxrwx 1 root root 60 May 25 14:00 channels-14-link -> /nix/store/sfkrmr0mcwh6invprzik6w8j51b1xvg6-user-environment/
danbst@station /n/v/n/p/p/root> cd channels
danbst@station /n/s/sfkrmr0mcwh6invprzik6w8j51b1xvg6-user-environment>

(to be clear, my usecase for this cd was to create some new relative symlinks. I can do that inside /n/v/n/p/p/root/channels, but can't inside /n/s/sfkrmr0mcwh6invprzik6w8j51b1xvg6-user-environment - it's readonly)

@ridiculousfish
Copy link
Member

Yeah, I'm now in favor of a virtualized path like other shells do. It creates some problems but is better on balance.

@ridiculousfish ridiculousfish self-assigned this Jun 16, 2018
@ridiculousfish ridiculousfish added this to the fish-future milestone Jun 16, 2018
ridiculousfish added a commit to ridiculousfish/fish-shell that referenced this issue Sep 17, 2018
This switches fish to a "virtual" PWD, where it no longer uses getcwd to
discover its PWD but instead synthesizes it based on normalizing cd against
the $PWD variable.

Both pwd and $PWD contain the virtual path. pwd is taught about -P to
return the physical path, and -L the logical path (which is the default).

Fixes fish-shell#3350
ridiculousfish added a commit to ridiculousfish/fish-shell that referenced this issue Oct 6, 2018
This switches fish to a "virtual" PWD, where it no longer uses getcwd to
discover its PWD but instead synthesizes it based on normalizing cd against
the $PWD variable.

Both pwd and $PWD contain the virtual path. pwd is taught about -P to
return the physical path, and -L the logical path (which is the default).

Fixes fish-shell#3350
ridiculousfish added a commit to ridiculousfish/fish-shell that referenced this issue Oct 7, 2018
This switches fish to a "virtual" PWD, where it no longer uses getcwd to
discover its PWD but instead synthesizes it based on normalizing cd against
the $PWD variable.

Both pwd and $PWD contain the virtual path. pwd is taught about -P to
return the physical path, and -L the logical path (which is the default).

Fixes fish-shell#3350
@zanchey zanchey removed the question label Oct 7, 2018
@zanchey zanchey modified the milestones: fish-future, fish-3.0 Oct 7, 2018
ridiculousfish added a commit to ridiculousfish/fish-shell that referenced this issue Nov 24, 2018
This switches fish to a "virtual" PWD, where it no longer uses getcwd to
discover its PWD but instead synthesizes it based on normalizing cd against
the $PWD variable.

Both pwd and $PWD contain the virtual path. pwd is taught about -P to
return the physical path, and -L the logical path (which is the default).

Fixes fish-shell#3350
ridiculousfish added a commit to ridiculousfish/fish-shell that referenced this issue Nov 24, 2018
This switches fish to a "virtual" PWD, where it no longer uses getcwd to
discover its PWD but instead synthesizes it based on normalizing cd against
the $PWD variable.

Both pwd and $PWD contain the virtual path. pwd is taught about -P to
return the physical path, and -L the logical path (which is the default).

Fixes fish-shell#3350
ridiculousfish added a commit to ridiculousfish/fish-shell that referenced this issue Dec 2, 2018
This switches fish to a "virtual" PWD, where it no longer uses getcwd to
discover its PWD but instead synthesizes it based on normalizing cd against
the $PWD variable.

Both pwd and $PWD contain the virtual path. pwd is taught about -P to
return the physical path, and -L the logical path (which is the default).

Fixes fish-shell#3350
matchai pushed a commit to matchai/spacefish that referenced this issue Dec 29, 2018
@qtfkwk
Copy link

qtfkwk commented Jan 15, 2019

This issue surprised me today... setting aside all the above issues regarding virtual directory, what pwd reports, etc...

... and the possibly true statement "nobody really loves the current behavior" ("user experience" is usually shortened to "UX" not "UE" btw)

I liked the "old" behavior (pre-3.0.0) but also agree with eliminating sources of trouble. For anyone who wants a superficial "fix" with respect to how cd behaves, it is easily restored via the following addition to ~/.config/fish/config.fish:

# restore cd follows symlink (3.0.0+)
function cd
	builtin cd (realpath $argv)
end
# https://github.com/fish-shell/fish-shell/blame/3.0.0/CHANGELOG.md#L35
# https://github.com/fish-shell/fish-shell/issues/3350

awalgarg added a commit to awalgarg/dotfiles that referenced this issue Jan 20, 2019
Fun bug, took me a while to get it.

After updating to fish v3, opening new terminals in tmux won't open the
shell in the same path as the opener shell. But:

- If I run a nested fish or bash shell inside the opener shell and then
  open a new shell, it does follow the correct path
- Unsetting `default-command` from tmux would also restore the original
  intended behavior
- Same behavior wouldn't occur if default shell is bash or zsh
- Inspecting `${pane_current_path}` would suggest that the variable
  isn't even being updated at all when the shell is fish and
  default-command is set to "$SHELL"!

Turns out, in the original setup, when a new terminal is opened by tmux,
it launches the shell as `fish -c /usr/bin/fish`. So there is the parent
fish shell and then that shell launches `/usr/bin/fish`. Any directory
change done in this nested shell is not tracked by the parent fish shell
instance, which is what tmux would read for `#{pane_current_path}`. This
could be verified still by running `fish -c /usr/bin/fish`, cd'ing in
this new shell, and then opening a new terminal to verify that the new
path hasn't been followed. Running `bash -c /usr/bin/fish` however does
not exhibit this behavior.

Changing `default-command` to `exec $SHELL` eliminates this nesting, and
hence tmux can read the appropriate path. Additionally, this results in
a cleaner process tree as well regardless of which shell is being used.

I am not sure if this is a bug or intended behavior with the new release
of fish. Perhaps it is related to a change[1] in their new handling of
current directory path.

[1]: fish-shell/fish-shell#3350
@warptozero
Copy link

warptozero commented Jan 20, 2019

@qtfkwk I was surprised too, and now use the following, which prints the real path after following a symlink:

 function cd --description 'cd and print real path when following a symlink'
     builtin cd $argv
     if test (pwd -P) != (pwd -L)
         echo (pwd -P)
     end
 end

Seems like a nice middle ground. Improvements are welcome ofc.

BTW this does disable cd history (cdh, prevd, nextd), so to keep that intact it's better to modify the builtin by funced cd, inserting the if-test near the end and funcsave cd.

@phillco
Copy link

phillco commented Feb 7, 2019

I too loved the old behavior. Conceptually, it prevented me from seeing symlinked directories as distinct from their source. An option to go back (akin to set -o physical in bash) would be very helpful.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Apr 17, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests