6

I am using GNU bash, version 4.3.42(1)-release (x86_64-redhat-linux-gnu).

I created a dummy dir hello and moved there. Once in /tmp/hello, I removed the directory itself.

$ pwd
/tmp/hello
$ rmdir $PWD

I checked and I was still in that directory.

$ pwd
/tmp/hello

I then tried to move to the directory itself and of course it was not possible (even though I was already there):

$ cd $PWD
bash: cd: /tmp/hello: No such file or directory

But then I tried with cd ., as it is supposed to be the same as cd $PWD. And this worked... somehow:

$ cd .
cd: error retrieving current directory: getcwd: cannot access parent directories: No such file or directory

To my surprise, now I was in a new "something":

$ pwd
/tmp/hello/.

And if I tried to go to the previous directory, it of course fails:

$ cd -
bash: cd: /tmp/hello: No such file or directory

Funny thing also is that not even a man page works while in there:

$ man cd
man: can't change directory to '': No such file or directory
man: command exited with status 255: (cd  && LESS=-ix8RmPm Manual page cd(1) ?ltline %lt?L/%L.:byte %bB?s/%s..?e (END):?pB %pB\%.. (press h for help or q to quit)$PM Manual page cd(1) ?ltline %lt?L/%L.:byte %bB?s/%s..?e (END):?pB %pB\%.. (press h for help or q to quit)$ MAN_PN=cd(1) less -s)

Why is all of this happening?

1

3 Answers 3

3

Agreeing with @BinaryZebra, it is fair to point out that this question has been asked before (for instance, Remove a directory from inside using the command line interface [duplicate], and can be cited ad infinitum), and the observable features are well known.

The comment about behavior relating to POSIX is more interesting, since it could provide some insight about expected behavior. The relevant POSIX documents are

In the Application Usage section of cd, it is noted

Since cd affects the current shell execution environment, it is always provided as a shell regular built-in.

In the Description section of cd, item 10 gives the most pertinent information:

  1. The cd utility shall then perform actions equivalent to the chdir() function called with curpath as the path argument. If these actions fail for any reason, the cd utility shall display an appropriate error message and the remainder of this step shall not be executed. If the -P option is not in effect, the PWD environment variable shall be set to the value that curpath had on entry to step 9 (i.e., before conversion to a relative pathname). If the -P option is in effect, the PWD environment variable shall be set to the string that would be output by pwd -P. If there is insufficient permission on the new directory, or on any parent of that directory, to determine the current working directory, the value of the PWD environment variable is unspecified.

That is, PWD is an output of the cd command, rather than an input, and as such is largely ignored by the cd command. Take note of the final statement regarding "unspecified". POSIX refuses to say what happens to $PWD in the event that a call to chdir() fails.

In discussion of Environment Variables for pwd, POSIX notes regarding PWD:

An absolute pathname of the current working directory. If an application sets or unsets the value of PWD, the behavior of pwd is unspecified.

That is, POSIX refuses to specify any other way that $PWD can be set other than as a successful use of the cd command (in turn, matching a successful call to chdir()).

In neither document is there wording that could be construed as saying that the path is "cached", but only that the working directory exists (presumably as a value) within the Shell Execution Environment. The discussion of cd itself does not mention the possibility of a directory continuing to exist without a name. Nor does the cd document itself give any attention to a non-existing directory. That is done in the description of the chdir() function as one of several causes of failure. For example

[ENOENT]
A component of path does not name an existing directory or path is an empty string.

The description of chdir() does not mention any special treatment of "." perhaps because POSIX avoids describing in detail the procedure by which an absolute pathname is computed using getcwd(), e.g., allowing for filesystems without "." and "..". If it did, someone might have added wording to point out that implementations may treat chdir(".") as a no-op.

Going back to step 10, it says explicitly:

If these actions fail for any reason, the cd utility shall display an appropriate error message and the remainder of this step shall not be executed.

Updating PWD is done after that sentence, so there is no possibility of changing its value in the event of failure.

Going back to pwd, it says of the -L option

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.

and later

If neither -L nor -P is specified, the pwd utility shall behave as if -L had been specified.

So there is a chain of act-as-ifs, but nowhere an explicit statement that the shell uses the PWD variable itself as the container for the working directory. Rather, there are the points noted as unspecified which allow for the possibility that some POSIX-compliant application might choose a different way of organizing its data, so long as it met the acts-as-if points.

10
  • Your comment about "Environment Variables" for pwd will mean that: PWD=/no/valid/path pwd should never work. That is not practical, and POSIX should not be followed.
    – user79743
    Oct 24, 2015 at 3:00
  • The value cached is NOT the same as $PWD. The $PWD is an exported variable.
    – user79743
    Oct 24, 2015 at 3:04
  • I didn't say that it did :-) Oct 24, 2015 at 14:01
  • You did say: the working directory exists (presumably as a value) within the Shell Execution Environment. What does that mean, then?
    – user79743
    Oct 24, 2015 at 18:41
  • I was commenting on a statement in the other proposed answer, which you did not challenge at the time. Oct 24, 2015 at 18:45
2

Repeating a description of some personal interpretation of POSIX documentation will not help the OP. There are several other questions that have explained POSIX in detail. And yet, this question.


Some basic descriptions:
A. It is a known characteristic of Linux/Unix that a file (directory) could be erased while it is being used. That is why you could keep seeing a movie after you erase it. Only when the inode of the directory entry is removed is the file really gone.

B. It also happens that the shell keeps an internal value for the actual pwd, even if the value of the environment variable $PWD is changed.

The first comment at this accepted answer of @yaegashi shows where to look (the source of pwd):

I'm correct. See the source of pwd_builtin(). It just prints the content of the_current_working_directory. – yaegashi Jul 31 at 16:01

As shown, the shell still keeps a knowledge of the correct value of pwd ("/home/user") even starting with an odd value of PWD.

C. It should be easy to understand that the value kept by the shell could get out of sync with reality in some corner cases.

Doing a cd . inside an erased directory seems to be one of those corner cases. It also seems that the shell adds the dot to the last known pwd on failure.


Your questions:

1. Remove the dir for PWD and execute the builtin pwd.

$ pwd
/tmp/hello
$ rmdir $PWD;   pwd
/tmp/hello

The builtin pwd reports the value of pwd that the shell retains in memory.
An external /bin/pwd will report a failure.
It is interesting to report that an pwd -P (even if a builtin for Bash) will report a failure:

$ pwd -P
pwd: error retrieving current directory: getcwd: cannot access parent directories: No such file or directory

And, after using the builtin pwd -P the normal pwd also fails. I believe is because the value of pwd in memory gets updated.

2. cd $PWD

I then tried to move to the directory itself and of course it was not possible (even though I was already there):

   $ cd $PWD
   bash: cd: /tmp/hello: No such file or directory

Any real attempt to follow the path to the directory will fail at the point in which a directory fails to be read (as when it does not exist).

3. cd .

But then I tried with cd ., as it is supposed to be the same as cd $PWD. And this worked... somehow:

   $ cd .
   cd: error retrieving current directory: getcwd: cannot access parent directories: No such file or directory

The shell attempts to access the path that it knows is the pwd, and it fails. And that is what the shell reports, a failure !. No problem here.

4. New Path.

To my surprise, now I was in a new "something":

$ pwd
/tmp/hello/.

The value in memory for PWD got out-of-sync with reality and a dot (.) got added to it. As any repeat use of cd . will add a dot.

$ cd .; pwd
/tmp/hello/./.

And, quite unexpectedly, a cd .. will make the pwd and $PWD only ...

$ cd ..; pwd; echo $PWD
..
..
$ cd ..; pwd; echo $PWD
../..
../..

5. Previous PWD.

And if I tried to go to the previous directory, it of course fails:

$ cd -
bash: cd: /tmp/hello: No such file or directory

It is so because the previous value of PWD (stored in bash in OLDPWD) could not be followed.
It was (as you reported above) /tmp/hello/., which (being hello erased) could not be followed, and makes cd - or cd $OLDPWD fail.

$ echo $OLDPWD                 # at this point following your exact procedure.
/tmp/hello/.

6. Man pages

Funny thing also is that not even a man page works while in there:

Man pages worked for me at this point, well, in fact, at all points.

0
1

POSIX requires the current directory path to be cached and the removed directory still exists without name.

cd . did just apparently add that string to the currently cached name.

4
  • 1
    Can you provide a reference to the POSIX requirement that you mention?  Can you at least be more specific about what the requirement is?  Is it just a requirement that the shell's builtin pwd command must report the last known name?  Or is it that the operating system must continue to honor the names . and .. after they no longer actually exist?  (Or both?  Or more?) Oct 23, 2015 at 14:08
  • See the POSIX standard - pwd man page, pwd caches the last name in $PWD and if $PWD contains some string it will be printed.
    – schily
    Oct 23, 2015 at 14:56
  • 1
    @schily POSIX describes pwd as what we call an "external command" of the shell, a separate program, NOT a builtin. Calling such external program changes nothing inside the shell (not a var or value). And will print the physical path in most cases.
    – user79743
    Oct 24, 2015 at 20:17
  • POSIX lists "cd" as an intrinsic function. This makes it obvious that it must be a builtin.
    – schily
    Oct 26, 2015 at 9:50

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .