why is 'cd' a shell built in and not an executable?
It IS and executable - /usr/bin/cd
Why did you think it was a shell built-in?
It doesnt exist in the bin....
Unix gives a message....
"cd is a shell built in"
What exactly gives you that message?
What flavor/version of UNIX?
Are you looking in /usr/bin ?
What shell are you using?
What does "man cd" say?
What does "which cd" say?
What does "whereis cd" say?
What does "find / -name cd -print" say?
Red Hat Linux 7.1
I used the command:
type -all cd
What answers do you have for the other 6 questions?
In many modern shells (bash, ksh), cd is actually a builtin. I believe that the reason for this, is that it executed much faster that calling an outside application for such a simple command. Some systems may have the binary available, although I'm not sure if that's for backwards-compatibility or not...
As it happens, AIX and Solaris have the executable
/usr/bin/cd however, RedHat does not. And yes...
ksh and bash do have a "cd" built in. It looks
like the source for it is there but not the
executable.
Folks, the current working directory is a datum that is stored on a per-process basis. The chdir(2) changes this value for the current process. After a fork(2), the child process will inherit the cwd from its parent.
Now think about the code:
cd /etc
cat passwd
This is being executed by a shell. Let's say the shell has a pid of 1200. When the shell encounters the "cd /etc" line, we really must have the cd command executed by the current shell as a built-in. So then shell the will execute the c code "chdir(/etc)" which sets the shell's cwd to /etc. Next, the shell encounters the "cat passwd" command. This is not a built-in. So the shell must fork(2). The parent shell, pid 1200, will simply wait(2) for the child. But the child process, let's say it got pid 2200, will exec(2) the cat program. When cat(1) starts to run, it still has "/etc" as its cwd. So when it opens the file called passwd, it will actually be opening "/etc/passwd". Eventually, pid 2200, will exit. This will cause pid 1200 to finally return from its wait call. Pid 1200 can get pid 2200's exit code from the wait call. But that's the only thing it will get from pid 2200.
Now think about:
/usr/bin/cd /etc
cat passwd
Here we have a problem. /usr/bin/cd is not a built-in. So the shell forks creating process pid 2200. 2200 will then exec(2) /usr/bin/cd. Now process 2200, which is now cd will invoke chdir("/etc") and so now it's cwd is indeed /etc. But now it exits. The shell, still pid 1000, returns from its wait call. But we have not changed the shell's current working directory. So now the shell encounters "cat passwd". It forks, creating pid, say. 2201. Process 2201 will then exec the cat command. But the cat command has inherited the cwd from the shell. It has no way to know that previously the shell executed "/usr/bin/cd /etc". We are in the wrong directory. This is why cd must be a shell built-in. cd was built-in even in the first bourne shell.
From a shell, any shell, try:
cd /
cat passwd
/usr/bin/cd /etc
cat passwd
pwd
cd /etc
cat passwd
pwd
It is only recently that /usr/bin/cd has started to exist. Unix got along fine without it for decades and it would get along fine if it vanished. It is largely useless and very misleading as this thread has shown. The only semi-rational reason to ever code something like:
/usr/bin/cd /etc
would be if you wanted to test whether or not you can sucessfully "cd /etc". The exit code from /usr/bin/cd indicates whether or not the chdir succeeded. You could check the exit code. But I have a suspicion that /usr/bin/cd may be someone's idea of a joke.
To further clarify all this stuff, /usr/bin/cd is not a joke or fluke...
its' purpose is when "cd" is invoked without any arguments,
it will change the current working directory of the invoking
shell to the HOME directory (provided HOME is set).
Having said that, bash and newer versions of ksh and sh
may now act this way thereby obsoleteing /usr/bin/cd but
for the moment, the executable is included on AIX, SunOS,
HP-UX and SysVR4.
Sorry, but that is simply wrong. There is no way for /usr/bin/cd to influence any other process' concept of a current working directory. No shell ever invokes it for any reason ever. Change its name and see if anything breaks. All shells will simply do a chdir() to change their own cwd.
On an HP-UX 11.0 box I have just:
cd /usr/bin
mv cd xxcd
chmod 0 xxcd
Then I used lsof to insure that no process was executing xxcd or otherwise had it open. I tried sh, ksh, csh, and bash. With each shell, I tried multiple cd statements including one with no arguments. Each cd statement worked with each shell.
What is leading you to conclude that /usr/bin/cd is needed by a shell?
Nothing leads me to conclude that /usr/bin/cd is needed by
a shell. The only possible reason I can think of is that early
versions of shells did not default the argument to chdir(2).
Nowadays, this is not the case. My statement comes from the
unix man page cd(1) ...
DESCRIPTION
/usr/bin/cd
The cd utility will change the working directory of the
current shell execution environment. When invoked with no
operands, and the HOME environment variable is set to a
non-empty value, the directory named in the HOME environment
variable will become the new working directory.
So... as you can see, I didn't make this up. I'm not saying it's
necessary anymore just that is existed for some reason
and that reason is stated in the man page. In fact, all shells that
I know of (at least today) do default the chdir() argument
to whatever $HOME is set to.
I do see that language on the sun man page. Hp's man page is more rational:
That language preceeds the existence of /usr/bin/cd, they should now drop the "only" from the sentence.
With HP-UX, 10.0 was the first release to have /usr/bin/cd. No earlier version of HP-UX had one.
It really is new. It was never used by any shell. There is no way to write a shell that uses /usr/bin/cd. There never was any way to write a shell that uses /usr/bin/cd in any release of any version of unix. /usr/bin/cd is virually useless.
Believe me... I do not debate the dubious value of
/usr/bin/cd. I'm only hopeing someone actually knows
why it exists. I do not believe it was a joke. The only other
possibility I can think of would be that it may act similar
to sourcing a file containing environemnt variables.
For example:
create a file called .testthis
with one line...
export HOME=/tmp
...then from your home directory do
$. .testthis
$cd
$pwd
...for the remainder of that shell session, HOME will be /tmp.
Again reasons for doing this are dubious at best but maybe
someone out there can shed some light on the reason for
having /usr/bin/cd
Perhaps it would help if we looked at /usr/bin/cd, not from the context of listing files in a directory in an interactive shell, but for a command sequence:
NOTE: MAY NOT WORK.... TRY WITH EXTREME CAUTION !!!
/usr/bin/cd /tmp; rm -rf *
NOTE: MAY NOT WORK.... TRY WITH EXTREME CAUTION !!!
This would appear, if I'm not mistaken, to spawn a child process that would remove all the files in the /tmp directory. After that task completes and the child process exists; the original shell process and current directory would be the same as before the command sequence was executed.
Another example might be as simple as:
/usr/bin/cd /usr/spool/mail; ls -la > /tmp/mail_stuff
One could argue that the sequence:
ls -la /usr/spool/mail > /tmp/mail_stuff
would accomplish the same thing.... agreed.
However, it seems that there are numerous possible scenarios where /usr/bin/cd could be useful when used in a command line sequence and subshell
That's a better reason than I could think of
Well, I just wanted to add that on Solaris /usr/bin/cd is a two-line shell script.... It does what any other shell script does.... On HP-UX I am not sure what it is at the moment, but I will check it out tomorrow....
I can honestly say that I have never encountered a problem which required the use of this 2-line shell script...
For the record, here it is:
cmd=`basename $0`
$cmd "$@"
That's it.. Nothing more....
Quoth the raven - nevermore...
- dEvNuL
From the HP-UX /usr/bin/cd:
#!/usr/bin/sh
# @(#) $Revision: 72.2 $
# This is the execable version of cd implemented using the
# posix shell built-in cd command.
cd $@
exit $?
Odd... I never even thought to check if it were binary...
Sorry, that won't work. I sure hope nobody tried that. Try this instead:
/usr/bin/cd /tmp; /usr/bin/pwd
Even when commands are stacked with semicolons, we still have a shell processing the command line. And it still must spawn a child, let the child run /usr/bin/cd, wait for the child to die, spawn a second child, which will run /usr/bin/pwd.
Actually there are some comments as well which date it Feb 6, 1995 and it has a 1995 copyright. This gives us some idea of when it appeared. 1995 does sound about right.
HP's version is also a simple script:
cd $@
exit $?
HP doesn't show a date, but it does have a comment showing that it is revision 72.2. It's hard to imagine 72 revisions on a script like this, but I guess it's not a job you assign to your best programmer.
The format of Sun's script shows that it's using its name to find the command. I got a list of all the files in /usr/bin linked to the same file. The list is alias, bg, cd, command, fc, fg, getopts, hash, jobs, kill, read, test, type, ulimit, umask, unalias, and wait. Only kill and test really make any sense. Oh well.
I can't actually test on any of our systems; because none of them have /usr/bin/cd........
I agreed!!! Don't try the rm example above unless you are in a very safe directory with only temporary/test files. I don't have any way to test since /usr/bin/cd does not exist here.
This has been very interesting...... but I'm not sure what it all means, at this point. Are there any lessons learned?