It’s that time again, when we highlight “Hard Code,” an opinion column for developers by I. M. Wright, Microsoft development manager at large. The column’s motto is (and we hope always will be) “Brutally honest, no pulled punches.” If you enjoy this column/podcast, you’ll enjoy I. M. Wright’s “Hard Code” (Microsoft Press, 2008), which includes 49 columns and numerous Eric Asides contextualizing, clarifying, and complementing I. M.’s unpulled punches. (Eric Brechner is Director of Development Excellence in Microsoft’s Engineering Excellence group.)
“It's the end of the fiscal year. Most engineers associate this time with performance review season, but for principal-level engineers and higher it's also executive review season. Time to waste weeks of your life writing slides for executive presentations that will be rewritten five times before they are never presented.
Executive reviews aren't a waste of time—occasionally you need an experienced, authoritative voice to blow apart your assumptions and refocus your efforts on desired business results. Preparing isn't a waste of time—being forced to explain yourself to others always helps your thinking, and I've got no desire to look like an idiot in front of the person who approves my compensation. The real waste of time is the focusing on getting the right slides for every season and situation instead of getting the right strategy.
Smart, high-level people simply don't know how to cope with executive reviews. They think it's a time to show off instead of a time to listen. They respond inappropriately to executive criticism of their badly presented, unsuitable slides. I've done it too—it's a trap set by our superiors filling out the poor templates dictated by their superiors. It's the misinformed leading the uninformed. Well, now it's time to break that cycle, avoid the pitfalls, and focus on what matters—valuable feedback on your clear and concise plans.
Why do so many otherwise intelligent people bungle executive reviews? I believe there are two reasons—exuberance and confusion.
Exuberance over the importance of the moment. It's so important that we must cover every detail rather than focus on what's important. Twisted huh? Who defines what's important? The executive. What's important to the executive? Ask. Press for clarity. Don't accept second-hand smoke from an assistant. It's not good for your health.
Confusion between executive reviews and presentations. Both use slide decks. Both involve presenting. The difference is that in presentations the presenter is in control. In executive reviews, executives and their posses are in control. Failure to recognize this difference leads to failure in your review.”
Commenter Dean Earley asks, "Why is there a 'current directory' AND an current drive? Why not merge them?"
Pithy answer: Originally, each drive had its own current directory, but now they don't, but it looks like they do.
Okay, let's unwrap that sentence. You actually know enough to answer the question yourself; you just have to put the pieces together.
Set the wayback machine to DOS 1.0. Each volume was represented by a drive letter. There were no subdirectories. This behavior was carried forward from CP/M.
Programs from the DOS 1.0 era didn't understand subdirectories; they referred to files by just drive letter and file name, for example, B:PROGRAM.LST. Let's fire up the assembler (compilers were for rich people) and assemble a program whose source code is on the A drive, but sending the output to the B drive.
Since we gave only a drive letter in response to the Object file prompt, the assembler defaults to a file name of FOO.OBJ, resulting in the object file being generated as B:FOO.OBJ.
Okay, now let's introduce subdirectories into DOS 2.0. Suppose you have want to assemble A:\SRC\FOO.ASM and put the result into B:\OBJ\FOO.OBJ. Here's how you do it:
The assembler reads from A:FOO.ASM and writes to B:FOO.OBJ, but since the current directory is tracked on a per-drive basis, the results are A:\SRC\FOO.ASM and B:\OBJ\FOO.OBJ as desired. If the current directory were not tracked on a per-drive basis, then there would be no way to tell the assembler to put its output into a subdirectory. As a result, DOS 1.0 programs were effectively limited to operating on files in the root directory, which means that nobody would put files in subdirectories (because their programs couldn't access them).
From a DOS 1.0 standpoint, changing the current directory on a drive performs the logical equivalent of changing media. "Oh look, a completely different set of files!"
Remembering the current directory for each drive has been preserved ever since, at least for batch files, although there isn't actually such a concept as a per-drive current directory in Win32. In Win32, all you have is a current directory. The appearance that each drive has its own current directory is a fake-out by cmd.exe, which uses strange environment variables to create the illusion to batch files that each drive has its own current directory.
Dean continues, "Why not merge them? I have to set both the dir and drive if i want a specific working dir."
The answer to the second question is, "They already are merged. It's cmd.exe that tries to pretend that they aren't." And if you want to set the directory and the drive from the command prompt or a batch file, just use the /D option to the CHDIR command:
(Notice that the CHDIR command lets you omit quotation marks around paths which contain spaces: Since the command takes only one path argument, the lack of quotation marks does not introduce ambiguity.
Doesn't the Win32 subsystem also keep track of the per-drive current directory, not just cmd.exe? How else can one pass drive-relative file names to CreateFile()?
The clever thing about using the equals prefix for these magic variables is that you can't set them with the 'set' command because the equals is taken as a token separator so the syntax is wrong – set believes the variable name is missing. That removes the need for a chunk of code in cmd.exe that would have to add tracking to these magic variables so setting them did exactly what cd does.
Though I personally find it useful (or maybe I'm just used to it from the MS-DOS days) for the command prompt to remember multiple places where I was so I can quickly switch between them. Trying to work on two different directories on the same drive quickly becomes annoying having to type out the full path name for every command, even with tab completion helping.
Thanks for the tip! I've wanted to change drive and directory for a long time but for some reason never thought to try "help cd" and find the switch. :P
It's a rotten shame Microsoft didn't think to copy the solution Cromemco came up with for dealing with file hierarchy issues using CP/M style commands. What they did was have a flat UNIX file system, and VMS style assigned symbols. So "assign M: /a/source" "assign N: /b/destination" and…
Typically we had I: assigned to includes and L: assigned to the libs directory and passed to C80 and L80 on the command line.
@Rick C: what are you doing there? Setting an environment variable named cd isn't remotely the same thing as aliasing 'cd /d' to be executed when the user types 'cd' at the prompt.
By the way, when I execute 'set "', I also get an variable like that: '=::=::'. I wonder why it is there. I don't have a drive '::' on my computer (although it's possible however, due to a bug in mountvol: call mountvol without parameters and write down the ID of some volume. Then enter 'mountvol :: {the ID you just wrote down}' and – baaam, you have a '::' drive. Try 'dir ::'. Changing the drive letter won't work however. But the mountvol trick works with all chars, you can also create a drive ù: or 4: or even ^G:, where ^G is Ctrl+G, which will result in a beep.).
It's an evolved language. It wasn't planed, designed or any of those fancy words. People sat down and wrote what they felt useful. Sometimes they documented what they did. Sometimes they didn't.
If you really wanted, you could probably port bash to win32 (no, I mean a native win32, not cygwin) and get different syntax. Despite all appearances, you aren't actually tied to the nasty mess that is cmd.exe
set "foo" displays all environment variables starting with foo. So set "" displays everything. And a missing end quotation mark is ignored.
I do wonder if the behavior to show the hidden variables is an intended feature or a just a side effect of the implementation, though.
Drive current directories lead to the interesting case of drive-relative paths; if the current path on drive D: is OBJ, then feeding CMD.EXE a path like "D:FOO.ASM" causes it to refer to "D:OBJFOO.ASM". If you're working extensively with two directories, you can SUBST one of them to a drive letter and use drive-relative paths to keep your commands short and simple.
This does lead to an interesting ambiguity. Since the colon was later used for Alternate Data Streams, how should one interpret the path "D:FOO.ASM" in an environment where the current directory has a file named "D"? It could be the ADS "FOO.ASM" in file D, or a drive-relative path on drive D:.
It also leads to another interesting question: how do you put an alternate data stream on the root of a drive letter? "D::stream"? Or does that mean the named stream attached to the current directory on D:?
I guess for your case (a file called "D") you can make it unambiguous by specifying an absolute path ("C:Food:stream").
@Marquess: IIRC, the MinGW bash is actually a cygwin bash with the cygwin bits statically linked in.
@ShuggyCoUk, Rick C, James: Instead of finding a way to control the "cd" command, you can use the "pushd" and "popd" commands. They'll work cross drive letters, set those special command prompt variables, and provide the added ability to walk backward the directory stack.
Very useful for batch files to quickly move to a new location, and get back to your original one regardless where it is.
@Brian G: Actually, many DOS commands let you set a variable with the same name as the command, which the command then uses as default options. CD appears to be one of them, since the behavior is different depending on whether or not there's an environment variable.
In 360KB-floppy-disk-only days, it was quite common for the A: drive to have your program, and the B: drive for your data. It makes sense to have drive-specific current directories for that scenario.
The win32 functions [GetFullPathName and thereby everything else] do recognize these variables – and the documentation suggests that SetCurrentDirectory *ought* to set them [though in fact it doesn't, but the CRT chdir function does]. What's lacking is any [documented – I mean, it's obvious enough what effect SetEnvironmentVariable is likely to have on the present implementation] way to set it _without_ changing the drive at the same time.
Also, the real current directory is more, er, real – since it's an open handle rather than simply a stored pathname.
@Coney: "If you really wanted, you could probably port bash to win32 (no, I mean a native win32, not cygwin) and get different syntax. Despite all appearances, you aren't actually tied to the nasty mess that is cmd.exe"
Of course, that ties you to the nasty mess that is Bash. *Maybe* slightly less nasty than cmd, and certainly nasty in different ways, but still most decidedly quite nasty.
@Marquess, @James: You are confusing MSYS and MinGW. They are separate things, even if often used together.
MSYS is a fork of an earlier version of Cygwin, and thus a POSIX emulation layer, with some special features to make interoperability with "native" Win32 programs easier. MSYS does include the bash shell.
MinGW is the gcc compiler and related tools. They are normal "pure" Win32 programs, and can be used with no MSYS or other POSIX emulation involved in any way, if you want to.
As far as I know, POSIX shells like bash would be virtually impossible to meaningfully port to "pure" Win32, without ifdeffing out most reasons to use it, I mean.
What bugs me more is that the current directory is a per-process variable and not a per-thread variable.
Regarding the echo. thing — it's all to do with quirks of the command line processor. See, back in ye olde days when you had COM files and the command line was passed in the 127 bytes of the PSP reserved for it, it so happened that the first byte of that space was the separator between the command and the rest of the arguments. Since this was almost always a space, most programs ignored it and just used the remaining 126 bytes as the command line. But it did mean that there was a difference between "echo" and "echo." and "echo ." — the command lines received were different in all three cases, and the defined behaviours were "display whether echo is on or off", "display nothing followed by a newline", and "display a dot followed by a newline".
But why did it "display nothing" in the second case? Because the command line wasn't empty (so it wasn't supposed to show whether echo was on or not) but the text to display (starting from the *second* character of the command line — remembering that the separator character gets skipped) was empty. In fact it didn't really matter what that separator character was, as long as DOS regarded as the end of the command — you could use "echo:" or any of quite a few variations. People just settled on "echo." because it was the least obtrusive. Yes, it's a bit silly, but once "echo" was defined to produce some other output (and using quotes was defined to display those quotes) then there wasn't much else that could be done.
And @Rick C: The %CD% environment variable contains the current working folder (provided it hasn't been redefined), not parameters that change the behaviour of the CD command. To be consistent, that latter one would have been CDCMD, but that doesn't work. Probably because it would break a lot of batch files.
Because I.D.GUY's post is in upper case and I have the attention span of a moth, it has become the most important and useful thing I have read today. I've already emailed all my bank details and a photo showing where I keep the spare door key and I look forward to having my very own HOLOGRAM.
@Miral: I didn't know if CD supported that syntax, I just decided to try it and see…and it turns out not to work.
It's even more interesting in Windows NT: In the NT kernel, there is no such concept as current directory. The current directory is faked in user mode by kernel32.dll and ntdll.dll. It does this by holding an open file handle to the directory logically represented as the current directory, and setting up child processes to inherit this handle.
When you open a file in NT, you eventually call NtCreateFile (see ZwCreateFile in Windows Driver Kit). The OBJECT_ATTRIBUTES structure contains a RootDirectory field. If you put a file handle to a directory in the RootDirectory field, the filename you specify will be interpreted relative to that directory. kernel32.dll thus implements current-directory-relative filenames by passing the current directory handle as the RootDirectory field.
Linux buffs may recognize this mechanism as the "*at" series of file calls: openat, chmodat, unlinkat… Another feature that NT got before Linux. =)