Salta al contenuto principale


The commands 'cd $PWD' and 'cd .' in bash both look like useless no-ops which change into the same directory you're already in. But they're not, and both can be useful.

$PWD is a string variable which caches the pathname that your current directory had at the time you changed into it. It's not automatically updated in between cd commands.

So 'cd $PWD' (or 'cd "$PWD"' if you're being properly careful) changes to whatever directory _now_ lives at the pathname that your actual cwd _was_ when you changed to it.

'cd .' really _does_ just change directory to the same physical directory you're already in, but it's still not useless, because it causes bash to recalculate the value of $PWD.

For example, if one shell is in ~/test, and in another shell you rename ~/test to ~/newname and make a new directory ~/test, then in the first shell 'cd $PWD' will move to the new ~/test, whereas 'cd .' will stay in the original directory but update $PWD to reflect the fact that it now lives at ~/newname.

Lars Wirzenius reshared this.

in reply to Simon Tatham

I use "mount . ; cd ." fairly often when I find that the thing I wanted to be mounted here wasn't.
in reply to Hugo Mills

@darkling hmm, that's a special case I hadn't thought of. But that's surely one for 'cd $PWD' rather than 'cd .'? If your physical cwd is the mounted-over directory, then 'cd .' won't change that.

$ mkdir test
$ cd test
$ touch this_is_the_original_dir
$ sudo mount -t tmpfs none .
$ ls
this_is_the_original_dir
$ cd .
$ ls
this_is_the_original_dir
$ cd $PWD
$ ls
$

in reply to Simon Tatham

It works for me:

$ cd /media/pipeline
$ ls
$ mount .
$ ls
$ cd .
$ ls
audio video

in reply to Hugo Mills

@darkling well, I've given you enough information to reproduce my case where it fails, but you haven't given me enough to reproduce your case where it works, because 'mount .' will depend on some stuff in your fstab which you haven't shown me! What filesystem type is this, and does it have any interesting options?
in reply to Simon Tatham

The base filesystem is btrfs; the mounted filesystem is NFS:

/dev/mapper/mhari--vg-root / btrfs noatime,subvol=@rootfs 0 0
files6:/srv/nfs/pipeline /media/pipeline nfs user,noauto 0 0

in reply to Hugo Mills

[Sorry, I originally didn't post this as a reply]
And it works with tmpfs as well:

$ cd /mnt/test
$ ls
$ sudo touch original
$ ls
original
$ sudo mount -t tmpfs /dev/null .
$ ls
original
$ cd .
$ ls
$

in reply to Hugo Mills

@darkling aha, thanks – that saves me finding an NFS server to test your version with!

So if you get different behaviour from me even with a tmpfs, then the difference is not in the filesystem type being mounted. It's some change between the rest of your OS and mine!

I got my results on Ubuntu 24.04, Linux kernel 6.8.0-59-generic.

in reply to Simon Tatham

@darkling I guess you're using zsh? stracing reveals that both bash and dash invoke the chdir syscall with the resolved path for 'cd .' and 'cd ..'
in reply to Simon Tatham

I'm on Debian (mostly-)testing, 6.12.17 kernel. The shell is bash (5.2.37(1)-release).

I'm inclined to believe a difference in shell behaviour before anything else.

in reply to Hugo Mills

@darkling bash on my Ubuntu is 5.2.21(1)-release.

If I strace bash during the 'cd .' operation then I can see that the only chdir system call it makes is chdir("."), so it makes sense that it should end up in the same directory.

in reply to Simon Tatham

@darkling aha, I think I've worked it out!

I think the difference is that I have 'set -P' in my bash setup.

in reply to Simon Tatham

<reads man page> Ah, yes, that looks like it might do it. Well done for working it out. :)
in reply to Simon Tatham

And guess whether anyone who actually uses either of these in a script repeats your explanation as a comment.
in reply to Simon Tatham

I never used those, but I sometimes use cd $(/bin/pwd). Sounds like cd . is a shorter way of doing the same in many cases (excluding the mount case)?
in reply to Leeloo

@leeloo yes, I think so, because /bin/pwd will figure out a string representation of your current pathname by tracing .. links upwards from your physical cwd. And that's exactly what bash will do to recalculate $PWD after you run 'cd .'.

The mount case is an exception, though. If you're in a directory that's just been mounted over, /bin/pwd _can't_ return a pathname corresponding to your current physical cwd, because there is no such pathname any more – that directory is no longer reachable at all from the VFS root (unless it's bind-mounted elsewhere or some other special case). So in that case 'cd $(bin/pwd)' will change into the new mount, whereas 'cd .' will stay where you are.

in reply to Simon Tatham

cd . also invokes the chdir syscall (with the full path, at least in Bash), which is meaningful after a mount (sorry, I missed the other replies initially)

e.g. the combo for entering a namespace by hand can be

cd /path/to/rootfs
unshare -mr
mount -B . .
cd .
for i in proc dev sys; do mount -R /$i $i; done
pivot_root . .
cd /
umount -l /

Questa voce è stata modificata (1 settimana fa)
in reply to Simon Tatham

oh, neat! Looks like 'cd pwd' matches 'cd $PWD' too. Is there any reason to prefer one of those two over the other?
in reply to Simon Tatham

Addendum: the effect of 'cd .' depends on whether you have bash in 'set -P' mode or not.

By default, bash's 'cd' builtin interprets the directory in a clever way, so that if you change directory into a thing that's a symlink to a directory rather than a physical directory itself, then 'cd ..' goes to the parent dir of the _symlink_ instead of the parent of the directory.

I hate this behaviour with a passion and turn it off, because it makes bash's interpretation of '..' inconsistent with subprocesses spawned by bash, so you mispredict what a command will do. I want '..' to have its physical meaning everywhere, so that it's the _same_ everywhere.

And apparently this setting affects 'cd .' as well as 'cd ..', which I suppose makes sense.

But of course I set this up 20 years ago and forgot it wasn't the default!

in reply to Simon Tatham

Ok, I only just discovered that this is bash being clever. I'd always assumed that this was normal - the parent of a directory is always in the context of the process. I guess it makes sense that most processes probably don't have that context ...
Now a whole lot of odd behaviour I've seen in the past makes more sense.
in reply to Simon Tatham

Well, in many filesystems¹ “.” and “..” are real directory entries (directories by themselves) so as far as I understand this most processes just don't have any special handling but just take them as directories with odd names, while shell cd has special code. Is this right?

__
¹this is not longer fully true for e.g. btrfs that just simulates them but does not have them on disk, or fat that AFAIK never had them but the linux driver simulates them

in reply to Simon Tatham

thanks for highlighting this! I usually find the cd .. thing handy, as I then end up where I was before I went cd <linky place> but it's good to know more details about all this, and that ther is another way.
in reply to Simon Tatham

Another reason why they're not no-ops is that they'll update $OLDPWD. Not sure what use that could be but it is at least an op.
in reply to Simon Tatham

I also like 'cd -P .' in case I'm deep in some symlink maze
in reply to Simon Tatham

Interesting, that thing about "cd ." and $PWD. I did not know that. I think I have also not needed that yet, but still, good to know. OTOH I have used "cd $PWD".
in reply to Simon Tatham

That's remind me something i found amusing some times ago:

~/tmp/tmp$ rmdir ../tmp
~/tmp/tmp$ # where am i now?
~/tmp/tmp$ pwd
/mnt/vg00/diego/tmp/tmp
~/tmp/tmp$ echo $PWD
/mnt/vg00/diego/tmp/tmp
~/tmp/tmp$ # everything is fine, right?
~/tmp/tmp$ ls
~/tmp/tmp$ ls .
~/tmp/tmp$ cat /dev/null > x
bash: x: No such file or directory
~/tmp/tmp$ # ah!
~/tmp/tmp$ mkdir ../tmp
~/tmp/tmp$ # so does it works now?
~/tmp/tmp$ cat /dev/null > x
bash: x: No such file or directory
~/tmp/tmp$ #still no luck
~/tmp/tmp$ cd .
~/tmp/tmp$ cat /dev/null > x
~/tmp/tmp$ # now it works!

While the error in creating the file looks legit, why can I list a directory that doesn't exist? And I think that a new directory with the same name, it's still a new directory, but looks like "cd ." somehow fix the cwd of the shell.

Just for reference tests were made on linux on a ext4 filesystem (but does it matters?).

in reply to Diego Roversi

@diegor 'why can I list a directory that doesn't exist?' – it does exist, it just isn't linked from anywhere. On Unix, directories as well as files have the property that the inode (containing the actual contents) persists even after there's no path to it from the filesystem root, if a process has it open. So your cwd can still be in a directory that's been deleted.

'looks like "cd ." somehow fix the cwd of the shell' – assuming the shell is bash, yes, by default "cd ." will do that. It _doesn't_ do what I claimed in the head of the thread, unless you have the non-default 'set -P' option, which I set long ago for myself and had forgotten was relevant. If _I_ had done "cd ." in that situation, I'd have stayed in the deleted directory.

in reply to Simon Tatham

I often use cd `pwd` which (in bash at least) is pretty close to the effect of cd $PWD, but easier to type.

This is useful with tests that blow away and recreate a temporary workspace. You run the test, and in another shell cd there and inspect its files etc. The next iteration will delete your inspection shell's cwd - but cd `pwd` puts you in the same place in the new run's output.

For this, the test case must reuse the same temp path. But you wanted to avoid /tmp, anyway.

Questa voce è stata modificata (1 settimana fa)

Questo sito utilizza cookie per riconosce gli utenti loggati e quelli che tornano a visitare. Proseguendo la navigazione su questo sito, accetti l'utilizzo di questi cookie.