This is a specific critique about the usability of the Unix shell, and I think most of this is still valid. The inside jokes (less is more) and overloaded meanings of command names is still an issue today, for example. Some of the visibility has improved by various means, but overall there is still a fair amount of 'black box' that takes arcane knowledge to peek in to.
Naming is one area I appreciated Microsoft's approach in Powershell. They have command names with a Verb-Noun structure, and full clear words, sometimes quite long. Then, after establishing clear canonical forms for the commands, they add a few well chosen aliases for short invocation and memorization.
Of all the critiques though, the Cognitive Engineering objection, that the system is not well designed to be used by human capacities, is still true across many platforms, especially in esoteric areas like the command line.
Also interestingly, I am guessing that this is the same Donald A. Norman of 'The Design Of Everyday Things'. It's fascinating to see these ideas in flight in 1981.
How do we address these things? How does one "redesign Unix" today?
Except that PowerShell commands, 'cmdlets', are really just .NET classes that run within PowerShell, like Ruby and Python classes, none of which are the same as working with GNU or BSD utilities and other executables in POSIX shells. That PowerShell's interface obfuscates its true nature is a huge violation of honest design, a massive violation of what's described in "Design of Everyday Things," leads to false comparisons, and misleads people into thinking it is providing the same functionality as working with executables in POSIX shells when it's actually providing chaining functionality like that in other scripting languages.
Isn't the purpose of a shell to obfuscate the gritty details of what happens behind the scenes? In Linux, when I execute a binary or a shell script, there is no obvious difference to me. Can you explain why it matters or would be considered bad design?
Interesting. You can, of course, work with actual executables in that environment too. Can you highlight some of the areas where it causes different behaviors or other design issues?
> Except that PowerShell commands, 'cmdlets', are really just .NET classes that run within PowerShell, like Ruby and Python classes, none of which are the same as working with GNU or BSD utilities and other executables in POSIX shells.
That's categorically incorrect and shows a lack of knowledge of both Powershell and .Net classes. But instead of me showing you that you're wrong, let me teach you how to prove to yourself that you're wrong.
Open up Powershell, type in
( get-command get-date ).dll
This will find the dll on your system for the get-date cmdlet, but any will do ( C:\Windows\Microsoft.NET\assembly\GAC_MSIL\Microsoft.PowerShell.Commands.Utility on my box). Now spin up ILSpy or any .Net decompiler.
Let's look at the GetDateCommand class. That's a 400 line class, which according to you, shouldn't exist (as cmdlets are "really just .Net classes"). In fact this entire DLL shouldn't exist.
But GetDateCommand is one of PS's simplest commands since it wraps DateTime (in CorLib), so "wait! See!" you say. But what you need to understand is that Powershell is built on top of .Net, .Net is effectively Powershell cmdlet's "kernel" so just like UNIX commands communicate with the actual kernel, cmdlets are going to leverage functionality in their "kernel" (.Net whenever possible).
My point is, that no, Powershell cmdlets are NOT just .Net classes. However you CAN use .Net classes directly in Powershell. For example:
The fact that you (and others I've seen here and elsewhere) continue to believe otherwise further demonstrates how deceptive PowerShell's design is.
What's interesting is that the cmdlet documentation is very clear:
"Cmdlets differ from commands in other command-shell environments in the following ways:
"Cmdlets are instances of .NET Framework classes; they are not stand-alone executables....
"Cmdlets do not generally do their own parsing, error presentation, or output formatting. Parsing, error presentation, and output formatting are handled by the Windows PowerShell runtime."
I literally just spoon fed you exactly how to go look for yourself about how cmdlets work and how they're distinct from the .Net classes they represent. I honestly don't know what more I can do.
> The fact that you (and others I've seen here and elsewhere) continue to believe otherwise further demonstrates how deceptive PowerShell's design is.
You realise I understand how Powershell works top to bottom, right? Where are they "deceiving me?" You can yourself can go learn about Powershell's artitecture (you have the tools, I've given them to you, and you clearly have access to the documentation).
> What's interesting is that the cmdlet documentation is very clear:
Wait is your biggest issue that cmdlets are held within DLLs of classes instead of standalone binaries? Because based on the snippets you posted I can only assume that is what your issue is.
I'm only passingly familiar with PowerShell but really, it's Microsoft, in the bloody documentation, that literally states precisely that cmdlets are instances of .NET Framework classes:
"Cmdlets differ from commands in other command-shell environments in the following ways:
Cmdlets are instances of .NET Framework classes; they are not stand-alone executables.
We can have an ontological debate about how an instance of an-object-as-a-concept is different from an instance of an-object-as-memory-content but I think that's best left to amateur philosophers.
You're right with the caveat that they can also be powershell scripts.
Anyone arguing that PS isn't really just 'scriptable .Net' is unfamiliar with the more advanced ideas of PS.
PS is different things to different people, but from a pure tech perspective the above is an apt description of PS.
edit:
I should also mention I find the idea of using PS as an example of an easy, intuitive shell to be laughable at best. Anyone who believes that has definitely tried to do much more than simply Get-Item, New-Item, type things. Once you start getting into more advanced things it becomes a hairy mess.
I had trouble parsing this - Could you explain, in a sentence or two, why cmdlets are more than .NET classes? What's the significance of the GetDateCommand class existing?
The only force greater than the frustration of a new comer in front of these cryptic commands is the frustration of an experienced user if you ever change even one character in any of these commands.
The other problem is discoverability. If it requires to check a documentation then it's probably badly designed in the first place. This is the power of GUI, a new user can visually observe the state of the system and see all the options available from there, and make progress quickly. I would expect any newly designed command interface to have an intellisense-like drop down for both commands available and arguments of these commands, with a short description and a link to the documentation if required.
Discoverability can be configured in zsh as part of completion system. I've found that this covers many of my needs:
% grep -<TAB><TAB>
--after-context -A # specify lines of trailing context
--basic-regexp -G # use basic regular expression
--before-context -B # specify lines of leading context
--binary-files # specify type to assume for binary files
--byte-offset -b # print the byte offset with output line
--colour --color # distinguish matching string
(...)
> If it requires to check a documentation then it's probably badly designed in the first place.
This notion is perhaps what is wrong with computing today.
If you come at any interface "tabula rasa" the only way to figure it out short of documentation/instruction is trial and error. "Discoverability" is so a distraction at best, and even then heavily dependent on prior (cultural) experience.
The idea of a GUI and Intellisense is to have an embedded short documentation.
Next to each checkbox, a GUI should have a plain text, short description of what it does (and a couple of words is often enough). Each item in an intellisense drop down has a short description too. The idea is not to eliminate documentation but to display it in the current context so that:
1. users make the correct choice when they type (i.e. the description will prevent them from making a mistake they would have made otherwise)
2. users know what other choice is available
3. users do not need to look for some offline documentation that may or may not refer to the version of the program they use (or the distribution of linux they use!) or may apply in a different context
4. users do not need to memorize occasionally used commands
That would be "--help", which GNU requires[1] in all[2] of their programs. Combined with the tab completion that others have mentioned and the full documentation in man(1)/info(1), it is easy to discover how to use most commands iff you are willing to actually read the documentation that is available.
"The only 'intuitive' interface is the nipple. After that it's all learned."
s/intuitive/discoverable/
GOOD GUIs are nice for discoverability, and the same is true for CLIs. There are many GUIs that are absolutely horrid, which rely on non-obvious icons or assume certain usage styles and offer no assistance if you don't have the same background assumptions as the GUI's creator.
Having used many different types of programs over the last ~30 years, I have wasted far more time with bad GUIs than I have with bad CLIs. At least with the CLI I can grep the output or documentation for what I want. I cannot grep a menu or GUI form to find some option I've forgotten the name of.
What this comes down to is that it is fairly easy to make a good GUI for simple things, and good programmers can make a nice GUI for the moderate and complex tasks. This is where discoverability is primarily useful, as it assists the inexperienced user and the dilettante.
For those of us that want to use our computer in a far more serious way, what the UNIX style CLI (shell) provides us is an interface that is still very learnable (IFF you invest in the time it takes to read the documentation!), that enables crafting commands that are simply not practical in any GUI. If you don't believe this, tell me how I can, for example:
- recursively walk an arbitrary directory tree of images at ($SRCDIR)
- except for any dirfectgory that ends in '.backup'
- finding any file that is NOT a PNG,
- convert that that file into a PNG
- but saving the new files in new directory tree ($DSTDIR),
creating any necessary parent directories
That would be a mess in any GUI. In bash + imagemagick, it would be:
for i in "${SRCDIR}/**/!(*.backup)/!(*.png) ; do
dst="${DSTDIR}${i#${SRCDIR}}"
mkdir -p "$(dirname "${dst}")"
convert "${i}" "${i%.*}.png"
done
It is my opinion that Visual Studio is Microsoft's best designed software and I am convinced that it is because it is the only software they produce where the developers do any serious dog fooding! All other softwares (Office and Windows mainly) seem to be developed for some hypothetical user which ain't me or anyone I have met.
Visual Studio introduced a few years ago a fully searchable GUI, which I think is a great idea. There is search box at the top and it will search through menus and options. Windows 8 started doing this too for some control panel functions but it is very incomplete.
I recently find it easier to bite the bullet and write the command on the spot, rather than to try to locate some custom made three liner somewhere. Just writing the logic from scratch becomes effortless with a bit of practice. It helps me focus on the task-at-hand, rather than digging into memory if I solved an issue before, and if I stored it somewhere convenient, with an apropriate name.
After having successfully located the snippet, only too often I have discovered that it does not do exactly what I want right now. Then I am left with the choice of changing the past script to also fit the new purpose, without destroying the previous work.
All this mental effort to save yourself three lines of typing seems a questionable endeavor. I have come to love the throwaway scripts.
They are still referring to documentation then, just now it is in-lined with the knobs they are operating. As such, still failing (in the absolutist sense) the idea of not having to check documentation.
And i see much the same working on CLI, via tab completion etc. Using the Linux ip command in the zsh shell can be interesting in that regard.
That is less direct though as I assume it is getting the information from the man page, which on Linux may not be maintained with the command (the BSDs are better here), rather than extracted from the source/api of the command itself.
First time i used an ATM, one of my parents were there with me. As such i got instructions in how to do it.
I don't have a car with a built in GPS.
Operating a stereo comes back to the whole experience thing, as most of the buttons are "old". Most of the button icons to operate a CD player are the same that was used for a casette player back in the day. And likely ones first encounter with either was back when one was a kid trying to get their parent to put on some music or other.
The closest i recall to reading about coming at computing "tabula rasa" was about a computer in a wall in India. And there the kids did what kids do, they poked and prodded and broke stuff to see how it worked.
This to the point that they developed their own cultural contexts for various icons (one was referred to via some deity name because it looked like a set of drums the deity was depicted using).
Having fuzzy search/completion is also a mandatory thing. The "no news is good news" is a very important negative factor for newcomers. Of course programmers and scientists have mileage to learn 'semantics' of operators by heart and learn to compose effects in their head so it's not as important to them.
Nowadays one could setup a zfs-on-linux so you can have free FS level undo.
I believe the next major revision to command-line interfaces will be natural language input, preview, correct/verify. Maybe just input, action, then possibly undo/redo with modification.
No need for the user to remember if it was called make_dir or mkdir or whatever.
Why would this be any more true of shells than it is of programming languages? The sort of things people do with the shell requires a certain specificity and simplicity (who wants to implement an NLP just to implement a compatible shell?)
Natural language input has displaced the graphical interface, somewhat. People use it every day on their phones.
I saw the majority of his complaints as basically "it's too hard/unfamiliar", a sentiment which I don't really agree with as I've noticed the steepness of learning curve of various software tends to be correlated with how powerful it is - those who give up early might not see that.
Inconsistent command names shouldn't be a difficulty - shell commands are like any other language, whose vocabulary is quickly learned with repeated use.
The manual doesn't bother warning against this either, although it does warn of another, related infelicity: "Beware of 'cat a b > a' and 'cat b a > a', which destroy the input files before reading them."
I'd consider it generous to see a mention of shell redirection in cat's manual, since that's done before it ever gets executed; in general, interactions between commands can't be exhaustively documented because they are numerous, and Unix assumes you can put two and two together.
..or maybe I'm just old and accustomed to it, after having used various OSs over the years, but that's sort of the point: Thanks to this "lowest-common-denominator" type of interface design, ease-of-use seems to have massively replaced learning, dissuading and distancing users from having control of their machines at a time when such control is becoming increasingly important.
The result of `cat a b > a` couldn't possibly be the result that the user intends, so it seems like poor design by definition. I can see how it would be tricky to handle it properly, but I do think it's possible to handle it properly without compromising the expressiveness of the shell.
In modern bash, this case is already protected with the "noclobber" option (enabled with "set -C" or "set -o noclobber"). It is described in the "REDIRECTION" section of bash(1), subsection "Redirecting Output".
It fails ">" redirections with an error message when it would clobber an existing file (you probably wanted to append to the file wiht ">>"). If you really intended this behavior, the new redirection operator '>|' has the old behavior.
$ set -o noclobber
$ echo foo > bar
$ cat bar
foo
$ cat bar > bar
bash: bar: cannot overwrite existing file
$ echo baz > bar
bash: bar: cannot overwrite existing file
$ echo baz >| bar
$ cat bar
baz
That would break a very large number of existing scripts that depend on being able to replace a file with a traditional ">" redirection.
You can set it as the default for your own interactive shell if you want by adding
set -o noclobber
to your ~/.bashrc file. It is probably a good idea to add it after the line that check if it is being used non-interactively, which probably looks something like this
if [[ $- != *i* ]] ; then
# Shell is non-interactive
return
fi
# add interactive-shell-only settings here
While you're editing .bashrc, you may want to also add these options that are available in modern bash that I find make the experience a lot nicer.
# support new glob patterns like !(foo|bar|...)
# for example, this move everything EXCPET the .h files
# mv !(*.h) /some/dir/
shopt -s extglob
# support ** in globs to match zero-or-more-recursive-directories
shopt -s globstar
# if you're annoyed at having to type "nohup" to prevent
# programs from closing when you close the shell window
shopt -u huponexit
Right, it definitely would break a lot of existing scripts, so I understand why its not a default. The thing is though, that really proves the argument that some parts of Unix have a bad out of the box user experience! I personally love Unix and find it amazingly powerful, but I've made the same mistakes as everyone and killed entire boxes, and really with some simple UX changes that could be fixed in the bulk of cases.
Edit: I forgot to add, thanks for the detailed answer!
noclobber is also an option setting in zsh, and I always have it set. The little extra confirmation it requires before clobbering is actually a nice confirmation that you are doing what you intended when you did intend it, and a nice rescue in case you didn't.
It won't save you from clobbering from another machine when using scp, though, so watch out.
At first I was going to say that handling this properly would require shell to have magic powers. After trying to come up with a proof of why it's impossible, I think I found a simple solution. There are two problems the author was complaining about. The first one is:
cat paper.* > paper.all
This has the potential to use up the whole disk. The fix here is easy: do globbing before redirection. But this does not solve the other problem:
cat a b > a
This has a simple workaround which is even easier to type: cat a >> b. But the trap remains. How could the shell fix it? By pretending the user wrote this instead:
cat a b > temp; mv temp a
Let's look at the trade offs. We do a bit of extra work: one rename operation. This should be pretty cheap, and solves this problem as well as paper.all.
The real trouble I think is coming up with a good name and location for tmpfile that is guaranteed not to mess up the operation of the command we're running. If we put it in the current directory, we get the wrong answer from ls. If we put it in /tmp, it will mess things up when we're working in /tmp. However, after doing a few minutes of research, it appears that this problem is already solvable on Linux: by using the O_TMPFILE flag, there is no need to give the file a name at all, until after the command finishes running.
Sometimes I start a long-running process, redirecting it's output, and periodically look at the output file while this process is running. Your proposed solution wouldn't allow for that.
If the user knew what they were doing they wouldn't have typed that.
`cat a b > a` is the same as `cat b > a`. There's no reason to add the a before b except to confuse.
>I saw the majority of his complaints as basically "it's too hard/unfamiliar", a sentiment which I don't really agree with as I've noticed the steepness of learning curve of various software tends to be correlated with how powerful it is - those who give up early might not see that
Many powerful systems are unfamiliar and difficult to use— but a system that is unfamiliar and difficult to use is by no means necessarily powerful.
At any rate, the issue isn't that Unix is difficult to use, because it isn't. What's difficult is using it well. What's difficult is learning all the idiosyncrasies, all the details you have to get right to write good software in the Unix ecosystem. What's difficult is learning when and why
rm *
does not do what you expect, recognizing the flaw in code like
mv "$PROJECT_ROOT"/* .
that can nuke your entire machine because the default semantics of the Unix shell are... well, questionable.
The thing is, using Unix well isn't difficult because the individual things you have to learn are difficult; the problems with the above commands are very simple and easy to address. Using Unix well is difficult because these problems arise goddamn everywhere.
>Inconsistent command names shouldn't be a difficulty - shell commands are like any other language, whose vocabulary is quickly learned with repeated use.
>I'd consider it generous to see a mention of shell redirection in cat's manual, since that's done before it ever gets executed; in general, interactions between commands can't be exhaustively documented because they are numerous, and Unix assumes you can put two and two together.
Excuses. Inconsistent anything is a difficulty. The user of a system should not have to compensate in any capacity for the failure on the part of the system's designers to ensure consistency— of names of all things. That interactions between parts of the same system are so complex that it would be an act of generosity to document them is not the mark of a powerful system; it is the mark of a poorly designed one.
And that's the thing: Unix is poorly designed. Maybe it was revolutionary nearly a half-century ago, but if someone unveiled an operating system, today, built on passing around and interpreting ASCII-encoded strings, with the occasional undecorated integer thrown in for kicks, they'd be laughed out of the room.
>Thanks to this "lowest-common-denominator" type of interface design, ease-of-use seems to have massively replaced learning, dissuading and distancing users from having control of their machines at a time when such control is becoming increasingly important.
This is happening, to a degree. However, there is a very important distinction to be made between the sort of "ease of use" that comes from reducing a system to a minimal high-level mode of interaction, and the sort of "ease of use" that comes from fixing the mistakes of previous iterations of the interaction model. Replacing the process of learning the aforementioned inconsistencies, pitfalls, and general frustrating complexities of Unix and its descendants with a system of equal or greater power that is genuinely easier to use is not a bad thing.
I remember this rant! I was chuckling about how superior Tenex (and later TOPS-20) was from UNIX and the article in Datamation really spoke to me. No esc command completion, no typing a '?' and seeing all the possible completions, no ^T to give you an update on what your process was currently doing. UNIX totally sucks at all of that.
Of course once you have trained your fingers you don't think about it any more. I missed that the first time around. This author does too of course, and so did a huge part of the industry. So many things were written with all sorts of discoverable features and helper stuff and the truth was that a lot of them can't get out of your way once your fingers know what to type! You can get so far and then you hit a wall, you have to walk through the "feature" that is no longer useful to you as an experienced user, because there is no way to get rid of it.
That is when you start to appreciate the minimal design. And loathe the very things that this article holds up as grand design truths. That is the secret of the success of the simple shell syntax.
One of the features of the command line ftp client is that (most of) its internal commands are full words; but as long as they remain unambiguous, you can abbreviate them as much as you want. Why more systems don't adopt this approach, I don't understand.
It's an inconvenience, but it's hardly an exotic situation. If it breaks backwards compatibility just save it for a major version release. It wouldn't even do the wrong thing, just complain about a certain command being ambiguous, and you'd have to fix it.
Trivia: cat is actually an abbreviation of catenate, a lesser used (now) synonym for concatenate.
Unix is of course horrible for beginning users but if you think you can do better (as in fixing the problems without doing collateral damage) you are probably wrong.
Another aside: The UNIX Hater's Handbook should be read by every Unix sysadmin and programmer.
The problem with the UNIX Hater's Handbook is that it's 10% spot-on observations about real problems with Unix(-type) systems, and 90% pointless whining and regurgitating of ancient tribal positions. Unfortunately a lot of the people posting complaints were playing to the crowd more than anything else.
Great idea for a mailing list ("let off some steam! complain to like-minded souls!"), maybe not such a good idea for a book...
The linked article in question is the same thing. The author complains it won't run on his PDP-11.
I beg you, however, to reconsider "not such a good idea for a book." It is a perfect idea for a book. It is precisely the kind of information that we're going to lose when Google shuts down Groups, or when the last fidonet archive goes offline. Books like the Unix Hater's Handbook are the only historical context we have for the rise of UNIX, and the death of TWENEX and the LispM.
cat(1)'s versatility is mostly a side effect of the essential nature of file descriptors, which I believe is good.
Criticisms of command, standard library and FHS naming are evidently quite ancient. I have little sympathy for them, given that other mainstream systems are usually worse. However, one can see GoboLinux and NixOS for alternative takes. I also will grant that creat(2) is embarrassing.
Modern versions of rm(1) (or at least GNU rm and I believe Solaris rm, as well) have a default policy of refusing certain glob patterns without explicit approval so as to prevent user error. I do not favor this much in principle, but this criticism is now dated. That you aren't asked if you're sure every time you do an operation that is potentially dangerous (which can be so many things) can be seen as a good thing.
Configuring your shell isn't particularly elegant, though understanding general distinctions like sessions isn't all that difficult.
ed(1) is seldom used these days, obviously. The functionality of fgrep(1) and egrep(1) have largely been merged into more monolithic grep(1) implementations, though the former two remain as aliases.
Overall, modern Unix-like UIs are actually pretty good as far as these things go. It could be much better, but whenever people try to fix things they're quite ironically met with resistance from people too conformant to the status quo, and the better ways are inevitably relegated to either academic curiosities or cult/heterodox practices that are espoused by a loyal few.
On the other hand, whenever a new solution does make it, it's rarely truly better because it's also based on some paradigm that is relatively well ingrained in the computing popular culture. When someone criticizes the FHS these days, it's almost always a concealed way of saying "Why isn't this like the directory structure of Windows or OS X?", rather than any interest in having the most ergonomic solution.
As a cautionary note, this article isn't really about anything people are using today. "Unix" doesn't today mean anything very similar to what it meant in 1981. The Commodore PET, Apple II and MS-DOS 1.10 of that time did not have great user interfaces either. And a lot has happened since then.
This reminds me of that great essay by Neal Stephenson called "In the Beginning was the Command Line".[1] In it, he compares Unix to an industrial strength screwdriver called the "Hole Hawg".
"But I never blamed the Hole Hawg; I blamed myself. The Hole Hawg is dangerous because it does exactly what you tell it to. It is not bound by the physical limitations that are inherent in a cheap drill, and neither is it limited by safety interlocks that might be built into a homeowner's product by a liability-conscious manufacturer. The danger lies not in the machine itself but in the user's failure to envision the full consequences of the instructions he gives to it."
> [Casual users] are apt to expect more intelligence from the system than the designer knows is there.
A million-fold increase in processing power later, and this is still one of the biggest problems for novice programmers or others just starting to look under the hood.
The reality is Unix isn't easy to get started in, but there are lots of resources once you know how to find them.
I learned by having a close friend and co-worker guide me through the early stages of using Unix, eventually I knew how to find solutions to my own problems (it was more a matter of where to look and some basic terminology).
The different flavours of Unix make this a bit of a double edge sword in that while solutions are similar on most platforms, finding the location of config files and different ways to install packages is often well explained but not always for the platform you're working with.
That's so insanely obvious in retrospecr. I guess there's a tiny performance hit - but surely not enough to not implement this idea. Kudos. Like it.
Edit - I generally tab complete my paths so it wouldn't help me out that often. But there are definitely times where that litte extra messaging would help. Hell, why not a fuzzy "did you mean" message
$ cp foo /real/long/path/name/to/destination/ /real/long/path/name/to/destination/
cp: target '/real/long/path/name/to/destination/' is not a directory
I'm still surprised I have to remember things like parameter order and that parameters don't have some kind of auto complete yet. Or that I don't get hints or definitions of parameters as I'm building up the command.
Naming is one area I appreciated Microsoft's approach in Powershell. They have command names with a Verb-Noun structure, and full clear words, sometimes quite long. Then, after establishing clear canonical forms for the commands, they add a few well chosen aliases for short invocation and memorization.
Of all the critiques though, the Cognitive Engineering objection, that the system is not well designed to be used by human capacities, is still true across many platforms, especially in esoteric areas like the command line.
Also interestingly, I am guessing that this is the same Donald A. Norman of 'The Design Of Everyday Things'. It's fascinating to see these ideas in flight in 1981.
How do we address these things? How does one "redesign Unix" today?