4

Is it mainly because pwd is not a builtin, while . is builtin to the bash shell?

For example, they differ in the following example:

/tmp/ttt$ mv ../ttt ../tttt
/tmp/ttt$ pwd
/tmp/ttt
/tmp/ttt$ cd .
/tmp/tttt$
Braiam
  • 35,380
  • 25
  • 108
  • 167
Tim
  • 98,580
  • 191
  • 570
  • 977
  • `pwd` can be a builtin. `type pwd` returns `pwd is a shell builtin` under bash-4.2.47-3.fc20.x86_64. – Cristian Ciupitu Jul 31 '14 at 15:59
  • 3
    This `.` is a directory. The shell `.` builtin is a synonym of `source` command and is not what you are seeing in this question. – casey Jul 31 '14 at 17:16

3 Answers3

9

. is not a bash (or any other shell) built-in. Each and every directory has entries . and .. in it. You can verify by cd'ing to some arbitrary directory and doing ls -lia . You will see . and .. entries. I had you use the '-i' flag on ls to get inode numbers: note them for . and ... Go up a directory, but doing cd .. or cd /something/soemthingelse/whatever. Do ls -lia again. Notice that the inode number of . is identical to that of .. in your first listing. Every directory has directories in it when created, named "." and "..". The "." entry has the same inode number as the directory's name does in its parent directory. The ".." entry is always the parent directory. If you do ls -li / you will see that "." and ".." have the same value.

This convention eliminates the need for special case code to reference the current directory: it's always named ".". It eliminates the need for special case code to "go up one directory": it's always "..". The only special cases are the root directory, and network-mounted filesystems. The filesystem root has "." and ".." with the same inode number, not different. Network-mounted filesystems have a discontinuity in inode numbers at the mount point, and behavior depends on what the remote filesystem is, and what protocol is used to mount it.

As far as the change in prompt, when you invoked mv ../ttt ../tttt, you actually renamed your shell's working directory. You didn't invoke a shell command that checks working directory until you did cd ., so the intervening pwd reports the old name, /tmp/tt.

  • What are inode numbers for, why would you ever worry about listing them? – user75027 Jul 31 '14 at 17:35
  • 1
    In Unix, Linux and other filesystems, an inode number is actually a file's identifier to the operating system. Depends on the filesystem, but there's usually a very quick way to go from inode number to the disk block that the inode lives. The inode itself is the on-disk structure representing a file: ownership, permissions, which data blocks contain file's data, access times, etc. A directory is just a mapping from a textual name to an inode number, the quick lookup of disk block from inode number allows the OS to get data and metadata quickly. –  Jul 31 '14 at 18:32
  • 1
    @BruceEdiger: I agree with everything you said here, but I don't see a response to the implicit question, "Why does `cd .` cause my shell prompt to change?" – G-Man Says 'Reinstate Monica' Oct 03 '15 at 14:40
  • @G-Man - thanks for the intelligent critique. I added a paragraph to explain that, I hope it's adequate. –  Oct 03 '15 at 14:49
4

Assuming you're using Bash which has pwd as a builtin1, what happens is that the cd command triggers the shell to update the information regarding the current directory. Until you run cd, the shell will think that the current directory hasn't changed, so it doesn't try to get its new path.

By the way, to expand a bit on Gnouc's answer, the Open Group Base Specifications Issue 6 on environment variables says:

PWD [...] shall represent an absolute pathname of the current working directory. It shall not contain any filename components of dot or dot-dot. The value is set by the cd utility.

On the other hand, if you'll run the external /bin/pwd command, you'll get the new name of the current directory. Why does this happen? The pwd command from coreutils does some sanity checks of the PWD environment variable and doesn't print it if it's invalid. The details can be found in the source code of the logical_getcwd function (from coreutils version 8.23).

Another command you can use to the get the cannonical path of the current directory is readlink -f . 2.

[ciupicri@host ttt]$ pwd
/tmp/ttt
[ciupicri@host ttt]$ mv ../ttt ../tttt
[ciupicri@host ttt]$ pwd
/tmp/ttt
[ciupicri@host ttt]$ /bin/pwd
/tmp/tttt
[ciupicri@host ttt]$ readlink -f .
/tmp/tttt

1 Running type pwd can confirm this.

2 From the readlink man page: -f, --canonicalize canonicalize by following every symlink in every component of the given name recursively; all but the last component must exist.

Cristian Ciupitu
  • 2,430
  • 1
  • 22
  • 29
  • so `mv` doen't update the current path, but `cd` will? – Tim Jul 31 '14 at 16:26
  • Is `.` always updated immediately, while `pwd` isn't? – Tim Jul 31 '14 at 16:29
  • Yes `mv` doesn't make the shell to update its current directoty info. `.` is a special subdirectory pointing to the directory itself which means that `cd .` changes to the same directory. – Cristian Ciupitu Jul 31 '14 at 17:03
  • 1
    Note that calling `pwd -P` (Physical) will report the correct directory, even if it is a shell builtin (tested inside bash). And, after `pwd -P` reports the correct value, the memory value gets updated and plain `pwd` will reort it correctly also. –  Oct 23 '15 at 19:19
3

pwd will print the value of $PWD if it contains an absolute pathname that does not have dot . or dot-dot ... From POSIX pwd definition:

-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, if the PWD 
    environment variable contains a pathname of the current directory that 
    is longer than {PATH_MAX} bytes including the terminating null, and the 
    pathname does not contain any components that are dot or dot-dot, it is 
    unspecified whether pwd writes this pathname to standard output or 
    behaves as if the -P option had been specified. Otherwise, the -L option 
    shall behave as the -P option.

By default, pwd use -L option so it will print value of current PWD, which is set when you use cd. When you mv the current directory to new path, PWD still not change, so you will get the old pathname.

You can have some ways to get the right new pathname:

  • Using -P option: pwd -P
  • Using /proc: ls -l /proc/self/cwd

Note

As POSIX definition for variable PWD, if an application set or unset value of PWD, the behaviors of the cd and pwd are unspecified.

% cuonglm at /tmp/ttt
% PWD=/home/cuonglm
% cuonglm at ~
% pwd
/tmp/ttt
% cuonglm at ~
% pwd -P
/tmp/ttt
cuonglm
  • 150,973
  • 38
  • 327
  • 406
  • You don't necessarily need to run `pwd -P`, running the external command can be good enough, i.e. `/bin/pwd` will print the new directory name. – Cristian Ciupitu Jul 31 '14 at 17:11
  • Because `/bin/pwd` use `-P` option by default if `POSIXLY_CORRECT` isn't set. This is just my habit when use `pwd -P`. – cuonglm Jul 31 '14 at 17:16
  • Running `POSIXLY_CORRECT=1 /bin/pwd` or `env POSIXLY_CORRECT=1 pwd` makes no difference. The new directory is still printed. – Cristian Ciupitu Jul 31 '14 at 17:20
  • @CristianCiupitu: It must be defined in compile time, see:http://git.savannah.gnu.org/cgit/coreutils.git/tree/src/pwd.c – cuonglm Jul 31 '14 at 17:28
  • [`pwd.c`](http://git.savannah.gnu.org/cgit/coreutils.git/tree/src/pwd.c?id=v8.23) has `bool logical = (getenv ("POSIXLY_CORRECT") != NULL);` which suggests it's looking for an environment variable. – Cristian Ciupitu Jul 31 '14 at 17:34
  • What do you mean? Can you see that it set value of boolean variable logical base on `POSIXLY_CORRECT` value? – cuonglm Jul 31 '14 at 17:35
  • 1
    It means that the `pwd` command depends on the *environment variable* named `POSIXLY_CORRECT`. – Cristian Ciupitu Jul 31 '14 at 17:39
  • @cuonglm The reason that the builtin `pwd` reports an incorrect value is that the shell keeps an idea of the PWD in memory. Which could get out of sync with reality on corner cases. Call `pwd -P` and `pwd` will report the correct value. That `/bin/pwd` reports the correct value is because it has no notion of the value kept in memory by the shell. The memory value has absolutely no relation to any POSIXLY variable. –  Oct 23 '15 at 19:35
  • @BinaryZebra: No, it had. Running `/bin/pwd` the shell need to fork child process, which inherited the shell environment variables, include `PWD`. In all case, setting `PWD` will lead to unspecified behavior. Try the last example with `/bin/pwd` instead `pwd`, you got the same result (in `bash`). – cuonglm Oct 24 '15 at 01:41
  • @cuonglm The "unspecified behavior" is pure POSIX theory. In practice all shells do set PWD when `cd .` is executed. –  Oct 24 '15 at 02:44
  • @BinaryZebra: I didn't see your point. POSIX explicit state that *the shell* and *cd* assign value to `PWD` is legal thing, other application attempt to assign to `PWD` will lead to unspecified behavior from `cd` and `pwd`. – cuonglm Oct 24 '15 at 17:02
  • @cuonglm What you mean is that `/usr/bin/env PWD=/no/real/path pwd` should fail? The command `env` changed PWD and is not `shell` or `cd`. –  Oct 24 '15 at 18:38