Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the `Cd` command?

I was writing some code, navigating my computer (OSX 10.11.6) via the command line, like I always do, and I made a typo! Instead of typing:

cd USB

I typed

Cd USB

Nothing happened, but it didn't register as an invalid command. Perplexed by this, I did some investigating: I checked the man entry. There was no entry. I found the source file (/usr/bin/Cd) using which Cd, and then cated it:

#!/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+"$@"}

What is this, and why is it here? How does it relate to freeBSD?

Any help would be amazing, thanks!

like image 513
bren Avatar asked Dec 10 '22 15:12

bren


2 Answers

macOS uses a case-insensitive filesystem by default[1] , which can be misleading at times:

which Cd is effectively the same as which cd and which CD in terms of returning the (effectively) same file path.

Confusingly, even though all 3 command refer to the same file, they do so in a case-preserving manner, misleadingly suggesting that the actual case of the filename is whatever you specified.

As a workaround, you can see the true case of the filename if you employ globbing (filename expansion):

$ ls "$(which Cd)"*  # could match additional files, but the one of interest is among them
/usr/bin/cd  # true case of the filename

Bash (the macOS default shell) is internally case-sensitive. That is, it recognizes cd as builtin cd (its built-in directory-changing command).

By contrast, it does NOT recognize Cd as that, due to the difference in case.

Given that it doesn't recognize Cd as a builtin, it goes looking for an external utility (in the $PATH), and that is when it finds /usr/bin/cd.


/usr/bin/cd is implemented as a shell script, which is mostly useless, because as an external utility it cannot affect the shell's state, so its attempts to change the directory are simply quietly ignored.
(Keith Thompson points out in a comment that you can use it as test whether a given directory can be changed to, because the script's exit code will reflect that).

Matt's answer provides history behind the inclusion of the script in FreeBSD and OSX (which mostly builds on FreeBSD), but it's worth taking a closer look at the rationale (emphasis mine):

From the POSIX spec:

However, 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 (env, find, nice, nohup, time, xargs).

In essence, the above means: regular built-ins must (also) be callable stand-alone, as executables (whether as scripts or binaries), nut just as built-ins from within the shell.

The cited regular built-ins table comprises these utilities:

alias bg cd command false fc fg getopts jobs kill newgrp pwd read true umask unalias wait

Note: special built-in utilities are by definition shell-internal only, and their behavior differs from regular built-in utilities.

As such, to be formally POSIX-compliant an OS must indeed provide cd as an external utility.

At the same time, the POSIX spec. does have awareness that at least some of these regular built-ins - notably cd - only makes sense as a built-in:

"Since cd affects the current shell execution environment, it is always provided as a shell regular built-in." - http://pubs.opengroup.org/onlinepubs/9699919799/utilities/cd.html

Among the regular built-in utilities listed, some make sense both as a built-in and as an external utility:

For instance kill needs to be a built-in in order to kill jobs (which are a shell-internal concept), but it is also useful as an external utility, so as to kill processes by PID.

However, among the regular built-in utilities listed, the following never make sense as external utilities, as far as I can tell Do tell me if you disagree , even though POSIX mandates their presence:

alias bg cd command fc fg getopts jobs read umask unalias

Tip of the hat to Matt for helping to complete the list; he also points that the hash built-in, even though it's not a POSIX utility, also has a pointless script implementation.


[1] As Dave Newton points out in a comment, it is possible to format HFS+, the macOS filesystem, in a case-sensitive manner (even though most people stick with the case-insensitive default). Based on the answer Dave links to, the following command will tell you whether your macOS filesystem is case-insensitive or not:
diskutil info / | grep -iq '^\s*Name.*case-sensitive*' && echo "case-SENSITIVE" || echo "case-INsensitive"

like image 78
mklement0 Avatar answered Dec 28 '22 22:12

mklement0


What is this?

The script itself is a portable way to convert a command, even with random upper casing, into the equivalent shell builtin based on the exec paths file name, that is any part of the string after the final / in the $0 variable). The script then runs the builtin command with the same arguments.

As OSX file systems are case insensitive by default, /usr/bin/cd converts running Cd, CD, cD and any form of cd with a / fs path (like /usr/bin/cd) back to the shell builtin command cd. This is largely useless in a script as cd only affects the current shell it is running in, which immediately closes when the script ends.

How does it relate to freeBSD?

A similar file exists in FreeBSD, which Apple adapted to do case conversion. Mac file systems by default are case insensitive (but case preserving).

The $FreeBSD: src/usr.bin/alias/generic.sh,v 1.2 2005/10/24 22:32:19 cperciva Exp $ header is the source information in the file.

Most of the underlying OSX system comes directly from FreeBSD or was based on it. The Windowing system on top of this and the Cocoa app layer is where OSX becomes truly Apple. Some of the lower level Apple bits have even made it back into FreeBSD like Clang and LLVM compiler.

Why is it here?

The earlier FreeBSD svn commits shed a bit of light:

A little bit more thought has resulted in a generic script which can implement any of the useless POSIX-required ``regular shell builtin'' utilities...

Although most builtins aren't very useful when run in a new shell via a script, this compliance script was used for the commands alias bg cd command fc fg getopts hash jobs read type ulimit umask unalias wait. POSIX compliance is fun!

like image 44
Matt Avatar answered Dec 28 '22 22:12

Matt