143

I've always wondered why cd isn't a program, but never managed to find the answer.

Anyone know why this is the case?

AkshaiShah
  • 3,629

7 Answers7

187

The cd command modifies the "current working directory", right?

"current working directory" is a property that is unique to each process.

So, if cd was a program it would work like this:

  1. cd foo
  2. the cd process starts
  3. the cd process changes the directory for the cd process
  4. the cd process exits
  5. your shell still has the same state, including current working directory, that it did before you started.
Sam
  • 2,488
  • 9
    Your five steps are correct but "if cd was a program it would work like this" should be "when cd is used in its external program implementation, it does work like this". – jlliagre May 19 '12 at 12:21
  • 2
    Not being a systems programmer, nor really having a deep knowledge of the ins and outs of interacting with the shell, I would have expected the shell to expose its current working directory, and cd to be a program that accesses and alters that property. Understanding, after looking at this answer, that that's probably sub-optimal to how it actually works for many reasons. – Jason Oct 31 '14 at 18:48
  • A link to the source code of the cd command in dash: https://git.kernel.org/pub/scm/utils/dash/dash.git/tree/src/cd.c – Daniel F Oct 19 '23 at 15:44
119

cd in addition to being a shell builtin, is actually also a program on POSIX compliant OSes. They must provide independent executables for regular utilities, like cd. This is for example the case with Solaris, AIX, HP-UX and OS X.

Obviously, a builtin cd is still mandatory as its external implementation doesn't change the current shell directory. However, the latter can still be useful. Here is an example showing how POSIX envision how this cd command could be used:

find . -type d -exec cd {} \;

On a POSIX system, this oneliner will report an error message for all directories you aren't allowed to cd in. On most Gnu/Linux distributions, it fails with that error message though:

find: `cd': No such file or directory

And here is the answer to your question, "Why is cd not a program?" by one of the original Unix co-author. On a very early Unix implementation, cd (spelled chdir at that time) was an external program. It just stopped working unexpectedly after fork was first implemented.

Quoting Dennis Ritchie:

In the midst of our jubilation, it was discovered that the chdir (change current directory) command had stopped working. There was much reading of code and anxious introspection about how the addition of fork could have broken the chdir call. Finally the truth dawned: in the old system chdir was an ordinary command; it adjusted the current directory of the (unique) process attached to the terminal. Under the new system, the chdir command correctly changed the current directory of the process created to execute it, but this process promptly terminated and had no effect whatsoever on its parent shell! It was necessary to make chdir a special command, executed internally within the shell. It turns out that several command-like functions have the same property, for example login.

Source: Dennis M. Ritchie, “The Evolution of the Unix Time-sharing System”, AT&T Bell Laboratories Technical Journal 63(6), Part 2, Oct. 1984, pp.1577–93

Unix Version 1 (March 1971) chdir manual page states:

Because a new process is created to execute each command, chdir would be ineffective if it were written as a normal command. It is therefore recognized and executed by the Shell.

jlliagre
  • 61,204
  • Interesting. but is there any shell that does not implement cd as builtin command and actually calls that external command? – Nils May 16 '12 at 21:06
  • That's interesting. The link you gave does say that cd is a "regular built-in utility", which "shall be implemented in a manner so that they can be accessed via the exec family of functions [...] and can be invoked directly by those standard utilities that require it (env, find, nice, nohup, time, xargs)". Yet the spec for cd itself says that "if it is called in a subshell or separate utility execution environment, [...] it does not affect the working directory of the caller's environment." – Ilmari Karonen May 16 '12 at 21:37
  • 12
    ...so, apparently, POSIX mandates that there shall be an independent cd executable, but that it shall do nothing (except possibly emit error messages if called with the wrong arguments). Weird. – Ilmari Karonen May 16 '12 at 21:39
  • 6
    Oh well, if it's true, that wouldn't be the stupidest thing in POSIX. – Kaz May 16 '12 at 23:06
  • 1
    @Rudolf What definition of "program" are you using here? IMHO a script is a program, it's just, well, scripty :) but it's still a series of commands which are interpreted by the computer to perform a task, and which can be saved and run repeatedly.. – Rick Mangi May 17 '12 at 00:56
  • 6
    The POSIX cd page also says "Since cd affects the current shell execution environment, it is always provided as a shell regular built-in.". – Mikel May 17 '12 at 00:57
  • This is from OSX 10.7.2 `cat /usr/bin/cd #!/bin/sh

    $FreeBSD: src/usr.bin/alias/generic.sh,v 1.2 2005/10/24 22:32:19 cperciva Exp $

    This file is in the public domain.

    builtin echo ${0##*/} | tr \[:upper:] \[:lower:] ${1+"$@"}` so the only thing it does is act as an alias to execute the builtin command

    –  May 17 '12 at 01:33
  • 4
    @Ilmari Karonen: It is incorrect to state the cd command does nothing. It tests if you can change your directory to the provided argument and returns a status accordingly. – jlliagre May 17 '12 at 05:00
  • @Kaz: see previous comment. – jlliagre May 17 '12 at 05:01
  • @Jarrod Robertson Every Unix vendor is free to implement these commands the way it likes. As the builtin cd and the external cd should behave the same way, calling the builtin implementation from a script is valid and wise. – jlliagre May 17 '12 at 05:11
  • 1
    Thus, for all intents and purposes, /usr/bin/cd and cd are completely different things. – Kaz May 17 '12 at 05:14
  • @Nils All Unix shell implementations provide a cd builtin. Not doing it would be quite complicated to implement (you would need for the shell to provide a way for an external process to pass that information) and pretty risky. – jlliagre May 17 '12 at 05:31
  • 6
    @Kaz, they are not completely different things. They do the same thing but only the builtin one affects the current shell. – jlliagre May 17 '12 at 05:32
  • 13
    @Kaz: Please don't call me silly while I'm just reporting a fact. You might agree or disagree with POSIX but don't shoot the messenger. – jlliagre May 17 '12 at 05:37
  • 2
    @jlliagre: Removed my comment and -1. You're right, POSIX does seem to say external cd must exist. Bizarre. – Warren Young May 17 '12 at 05:42
  • @jlliagre I didn't use the word "executable" and I'm not misunderstanding anything at all. I just wanted to say that on my OS X, the program that changes shell's working directory is the shell itself. – Rudolf Adamkovic May 17 '12 at 10:31
  • @jlliagre Removed my very first comment and +1 for the improved version of your answer. – Rudolf Adamkovic May 17 '12 at 10:39
  • 2
    @jlliagre Good stuff. Thanks for pointing this out. – George M May 17 '12 at 12:27
  • @jiliagre "They do the same thing but only the builtin one affects the current shell" means precisely that they do not do the same thing. I don't understand how you can even write a sentence of this form, and yet hit Enter. :) – Kaz May 17 '12 at 15:45
  • 4
    @Kaz: if you trace the execution of both variants, you'll find out a call to the chdir system call is eventually performed in both cases. This is what I mean with "They do the same thing". – jlliagre May 18 '12 at 05:27
  • An operation only does the same thing if it is the same operation operating in the same environment/scope on the same datum. chdir has a hidden implicit argument: the identity of the calling process, on whose current working directory variable it operates. This means that a chdir in process A and a chdir in process B are not the same operation (even if they are called with the same absolute path). Think of a database transaction. Is a $100 deposit into my bank account the same operation as a $100 deposit into your bank account? If so, you should not mind if the bank mixes them up. – Kaz May 18 '12 at 06:13
  • 2
    Of course, I know the command effect on the calling shell is different but it's just semantics. Let's say my definition of "doing the same thing" is "deposit $100 on a bank account" then whose account it is doesn't matter. – jlliagre May 18 '12 at 07:58
  • So the external cd makes sense, if you want to check, if you are able to execute the built-in cd successfully. Great - this is just what I currently need for a script of mine... – Nils May 18 '12 at 19:52
  • So let me get this straight... if I were to make my own shell for Unix, I would have to handle the specific case of cd? Why does it even exist in the first place if each Shell implements cd differently? –  May 22 '12 at 14:31
  • 3
    All shells basically implement cd the same way, i.e. by eventually calling chdir: http://pubs.opengroup.org/onlinepubs/9699919799/functions/chdir.html – jlliagre May 22 '12 at 20:03
  • @jlliagre @Nils @IlmariKaronen They must *not* provide independent executables for regular utilities, like cd! The page you link says "The utilities named in Regular Built-In Utilities [cd is one of them] are frequently provided in built-in form". The cd page says "Since cd affects the current shell execution environment, it is always provided as a shell regular built-in". Why do you say they must exist as an external program? – Totor Mar 21 '13 at 23:24
  • 2
    @Totor You should read the full paragraph: "all of the standard utilities, including the regular built-ins in the table, but not the special built-ins described in Special Built-In Utilities , shall be implemented in a manner so that they can be accessed via the exec family of functions as defined in the System Interfaces volume of POSIX.1-2008 and can be invoked directly by those standard utilities that require it" – jlliagre Mar 21 '13 at 23:57
  • @jlliagre Mea culpa, you're right. My Debian system does not respect this POSIX-ness, I'm afraid... – Totor Mar 22 '13 at 00:03
  • 3
    @Totor so Linux is not Posix. Nothing new here. – Nils Apr 10 '13 at 12:43
  • The screaming horror is I can make a /usr/bin/cd that changes the shell's working directory, but your prompt will not update when you run it. – Joshua Jan 25 '18 at 22:02
  • Would that find . -type d -exec cd {} \; do anything? I'd think not, beyond creating a short-lived child process which changes directory and then exits. In particular, one could not do anything in that find command, or anywhere after it, from within that directory. This is the case with all such "use cases" POSIX envisions, which is why no volunteer wastes time implementing it. If you are paid, however, for work in a committee, I can imagine that people don't mind spending half an hour on writing a script that does nothing anyway for a use case which is senseless. – Peter - Reinstate Monica Nov 20 '18 at 15:50
  • 1
    @PeterA.Schneider By design, you can't do anything from within that directory but it is nevertheless possible to write a find command that make use of this failing cd command and thus act depending on whether you can cd to a directory or not. – jlliagre Nov 21 '18 at 21:58
  • @jlliagre That's right. Interesting. – Peter - Reinstate Monica Nov 22 '18 at 06:02
51

From the Bash introduction (What is a shell?):

Shells also provide a small set of built-in commands (builtins) implementing functionality impossible or inconvenient to obtain via separate utilities. For example, cd, break, continue, and exec) cannot be implemented outside of the shell because they directly manipulate the shell itself. The history, getopts, kill, or pwd builtins, among others, could be implemented in separate utilities, but they are more convenient to use as builtin commands. All of the shell builtins are described in subsequent sections.

Michael Mrozek
  • 93,103
  • 40
  • 240
  • 233
cjc
  • 2,837
32

For April Fool's this year, I wrote a standalone version of cd.

No one got the joke. Sigh.

Anyone who isn't sure that cd must be built into the shell should download it, build it, and try it.

Read its man page, too. :)

Warren Young
  • 72,032
  • Really useful code! :-) – dschulz May 17 '12 at 05:13
  • 8
    Good you to see someone working for making Gnu/Linux more POSIX compliant. Your implementation is not only a good joke but actually something missing from Linux distributions ... – jlliagre May 17 '12 at 06:30
  • 9
    I think I'm going to try again next year, citing the POSIX issue. ;) – Warren Young May 17 '12 at 07:03
  • 6 years later: Well, did you? – Peter - Reinstate Monica Nov 20 '18 at 15:55
  • 1
    @PeterA.Schneider: I thought it was clear that I was joking, so to be clear, no, I'm not actually going to expend a bunch of effort trying to get this into OSes and OS-like projects such as Cygwin that currently lack /bin/cd. If you want to take my code and make that your own personal quest, you're welcome to do so. – Warren Young Nov 20 '18 at 16:01
4

The cd command in shell cannot be a separate process because in Unix there is no mechanism to change the current working directory of a different process (not even the parent process).

If cd was a different process then it would have to change the current working directory of its parent (shell) which is not possible in Unix. Instead cd is a special built in command. The shell calls functions like chdir() and fchdir() changing its own current working directory.

Note : the kernel stores the inode number of the current working directory for every process. The child process inherits it's cwd from its parent.

sebasth
  • 14,872
0

cd is a shell built-in command. As easy as is. The man cd says it all. the cd command changes the working directory for all interpreters and (in a threaded environment) all threads.

  • Because the shell is the environment which takes care about your current working dirs ($PDW...) or cdable_vars. This builtin is ultimately the way that all user-visible commands should change the current working directory. You are able to test it that way: compile the bash without cd.c and try to write your own cd script, which trys to take care of all environment cdable_vars. This question is also more a developer related. I bet they could answer you this question in more deeper detail. –  May 17 '12 at 16:11
  • 2
    There is a very good technical reason that cd is built-in. I would suggest you read the highest ranked answers and consider how your answer can be improved. – Thorbjørn Ravn Andersen May 17 '12 at 16:22
  • The highest ranked answer was the worst i ever read! But huh? Who am i! –  May 17 '12 at 16:32
  • 4
    But it answers the question why. – Thorbjørn Ravn Andersen May 18 '12 at 11:21
-1

I think one thing missing in people answer is that current directory is a environment variable that each program can change. If you use 'export' command to see your current environment variables list, you will have:

declare -x PWD="/home/erfan"

in your results. Thus by 'cd' command we just want to modify this internal variable. I think if we try, we can chage the PWD variable of any pty in shell, of course. Like:

cder    #change current PTY $PWD variable

But I think there s no need in normal cases. In another word, we take help from bash(or any shell) to modify its internal variable defined.

Erfankam
  • 107
  • 3
    While it's true that Bourne shells expose the current working directory (CWD) as $PWD, that is not the primary storage location; the actual location is in the kernel's per-process structure. It is therefore incorrect to say that the CWD "is an environment variable." If it worked the way you suggest, this C two-liner would print the .. path, not the path you started it from: #include <stdlib.h> int main(void) { chdir(".."); puts(getenv("PWD")); } (C shells expose the CWD as %cwd instead, by the way.) – Warren Young May 23 '12 at 16:59
  • lets add some another lines to your app. #include <stdlib.h> int main(void) { chdir(".."); puts(getenv("PWD")); setenv(P"PWD", "/", 1); puts(getenv("PWD")); } What will we have as results? – Erfankam May 25 '12 at 07:52
  • 3
    That will just overwrite the value of a variable, with no side effect on the CWD. This is a better test to show that: #include <unistd.h> int main(void) { char ac[99]; setenv("PWD", "/", 1); puts(getcwd(ac, sizeof(ac))); } It will show the directory you started the program from, not /. – Warren Young May 25 '12 at 08:02
  • I think every process has a working directory and path variable too. Thus you by chdir just change this attribute of process. Shell has this attribute too and by cd we modify this attribure. – Erfankam May 25 '12 at 08:56
  • 4
    No, I'm telling you that $PWD only has meaning to the Bourne shell. It is just a way for the shell to communicate something it knows to shell scripts so they dont have to call pwd to find it. Any standalone program depending on the value of $PWD will be unreliable. – Warren Young May 25 '12 at 09:26