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.
Hugo Mills
in reply to Simon Tatham • • •Simon Tatham
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
$
Hugo Mills
in reply to Simon Tatham • • •It works for me:
$ cd /media/pipeline
$ ls
$ mount .
$ ls
$ cd .
$ ls
audio video
Simon Tatham
in reply to Hugo Mills • • •Hugo Mills
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
Hugo Mills
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
$
Simon Tatham
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.
Alexander Monakov
in reply to Simon Tatham • • •Hugo Mills
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.
Simon Tatham
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.
Simon Tatham
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.
Hugo Mills
in reply to Simon Tatham • • •Tim Ward ⭐🇪🇺🔶 #FBPE
in reply to Simon Tatham • • •Leeloo
in reply to Simon Tatham • • •Simon Tatham
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.
Alexander Monakov
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 /
Iris Young (he/they/she) (PhD)
in reply to Simon Tatham • • •pwd
' matches 'cd $PWD' too. Is there any reason to prefer one of those two over the other?Simon Tatham
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!
Richard Hector
in reply to Simon Tatham • • •Now a whole lot of odd behaviour I've seen in the past makes more sense.
Klaus Stein
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
JayMoore
in reply to Simon Tatham • • •Ed Davies
in reply to Simon Tatham • • •schiermi
in reply to Simon Tatham • • •Jyrgen N
in reply to Simon Tatham • • •Diego Roversi
in reply to Simon Tatham • •That's remind me something i found amusing some times ago:
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?).
Simon Tatham
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.
Ian Jackson
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.
Moritz Dietz
in reply to Simon Tatham • • •