Hacker News new | past | comments | ask | show | jobs | submit login
Effective Shell (effective-shell.com)
188 points by signa11 on June 22, 2022 | hide | past | favorite | 65 comments



This is going to confuse the frankfurter out of a newbie. First it presents the idea that Windows has a shell, and even multiple shell programs. Screenshots 'n' everything.

But then, nope:

> Windows is not anything like Linux under the hood. So to get a shell working, we have three options:

> Use a tool which provides common Linux tools which have been written to work with Windows

> Use a "virtual machine" running Linux

> Use the Windows Subsystem for Linux

What "common Linux tools which have been written to work with Windows" is referring to is installing Cygwin.

This a fairly hamfisted presentation of a subject matter that could be handled in a way that is simultaneously easier to grasp and factually correct.

First of all, your goal is to teach about a specific command interpreter language, you really want to clarify how there are multiple ones which have different syntax, and brush that whole topic aside as fast as possible.

> You can see what the version is of your shell by running:

> bash --version

Yikes. If we know we are running Bash, the way to know what version it is is:

  echo $BASH_VERSION
and not to run another instance of Bash.

Can this book teach something simple, like comments?

> The shell ignores any text which follows a # hash symbol.

Wrong:

  $ echo 'all of # this is seen'
  all of # this is seen
"this is seen" looks like an example of "any text" to me.

A hash comment marker is ignored if it is not quoted and escaped so as to be part of a word. The hash is also part of certain bits of syntax:

  $#               (dollar hash) number of positional parameters

  ${variable#pat}  expand the content of variable, chopping off
                   a leading portion which matches the pat pattern.
                   Example:

                     $ echo $TERM
                     xterm-256color

                     $ echo ${TERM#xte}
                     rm-256color


The classic Windows shell has many flaws in its design, appears to have come from OS/2, and it's amazing that it works at all for how it has been used:

https://blog.nullspace.io/batch.html

Powershell is a new arrival, limited outside of Windows. I admit that I don't know it.

The POSIX shell also has severe problems with ambiguity, and it is not an LR-parsed language:

https://archive.fosdem.org/2018/schedule/event/code_parsing_...

Use the right tool for the job, imperfect as they may be.

It is unfortunate that awk didn't grow and supplant the shell, as for many years the BWK "One True Awk" did use straightforward lex and yacc grammars.


> The classic Windows shell […] appears to have come from OS/2

It’s actually based on the DOS command shell [1], which in turn was modeled after CP/M [2].

[1] https://en.m.wikipedia.org/wiki/COMMAND.COM

[2] https://en.m.wikipedia.org/wiki/CP/M


Alas, incorrect. Notice the "Comparison with COMMAND.COM" section.

https://en.wikipedia.org/wiki/Cmd.exe


Could you elaborate on what exactly you think is incorrect?


From the wiki, command.com did not run pipelines in parallel, and in consequence could not redirect standard error:

"For example, on OS/2 and Windows, it can use real pipes in command pipelines, allowing both sides of the pipeline to run concurrently. As a result, it is possible to redirect the standard error stream. (COMMAND.COM uses temporary files, and runs the two sides serially, one after the other.)"

Obviously, this process and I/O management would require a massive rewrite, sufficient to make cmd.exe a new (but somewhat compatible) implementation. This was not possible in DOS or CP/M.

Delayed variable expansion, pushd/popd, file name completion, expression evaluation and improved control structures are all fundamental changes that are strongly influenced by the Berkeley C Shell written by Bill Joy. The wiki specifically mentions the C Shell.

The 8086(/8) binary for command.com is not found in modern windows (I think the "wow16" layer was removed from 64-bit windows); cmd.exe is a 32-bit binary running under wow32, which it must be to invoke these new features in system calls that could not exist in DOS.

The wiki says this about the relation between command.com and cmd.exe:

"cmd.exe is the counterpart of COMMAND.COM in DOS and Windows 9x systems, and analogous to the Unix shells used on Unix-like systems."

I think that very little source code from command.com ended up in cmd.exe.


Yeah, I didn’t mean it in terms of of source code and implementation, and certainly a lot of features were added since DOS, but since both OS/2 and Windows (NT) were intended to be successors of DOS, their shell took over the syntax and conventions of the DOS shell.


...but also some of Bill Joy's influence as well.

It is compatible with, but distinct from what preceded it.


Hello, this is dwmkerr the author - your comments are all spot on - the earlier in the book you go the more work it needs, I've learnt a lot along the way TBH probably the entire first part needs a rewrite, that's actually going on at the moment as I'm working with a publisher.

I've linked to this post/comments on the repo (issue #210), they should all get addressed but it might be a while before the online version gets sorted, I'm closing out the final chapters online, once I've done this I'll likely go back to the beginning and rewrite/edit/put in a more consistent style (more code snippets, fewer screenshots etc).


I agree with all your comments from a technical point of view.

I disagree with all of them from an educational point of view.

Having said that, it helps to clarify whether the audience is technical or education oriented. The title implies a technical book, so I'm with you on the comments.


For the education oriented audience you need to explain everything, in some good order of revelation. Ideally not all at once. What is it that we are calling "shell"? Why is it called that? Where does it come from? Why are there different kinds of these things? Why do we go out of our way to install some other one intended for some other system? What's Linux?

This author doesn't have a chance in a room full of newbies. You have to have your technical dope straight, and it's not enough.


It is often best to present a problem or a definition in a simplified way. After grasping the basics, we can extend or go into more depth. Otherwise, students can’t see the forest for the trees. Of course it is still important to state that the definition is presented in a simplified form.


Another case where # does not start a comment:

  $ echo foo#bar
  foo#bar


Windows has a shell, it's called explorer.exe :)


This is indeed the thing that Windows has called "shell" before adopting that word for command interpreters (PowerShell).

explorer.exe uses the "ShellApi" to do some of its things, which applications can also use, like launching a file via its associated application (ShellExecute/ShellExecuteEx).


These are great comments. The code is open source. I'm sure the owner would love a pull request that would address these concerns.


I don't want to discourage you, but if you know the subject matter and are inclined to work on a book, you should just write your own from scratch as the sole author: the normal way most books are written.

(I personally wouldn't write a book about the shell. I would be too paralyzed by the thought that at any moment, Stéphane Chazelas might crack it open.)


The amount of people writing books on subjects as a way of learning something new, is too high. I’ve seen so much trash these days. Especially game design or programming books.


"The best way to get the right answer on the Internet is not to ask a question, it's to post the wrong answer." - Ward Cunningham (possibly)

Thanks to those who've commented with corrections/suggestions, I'll go through them try and get the content fixed up where possible. Will need to have a lie down and possibly an epidural before going through the comments again to pull out the changes needed.


For people who think that they're the intended audience of these kinds of guides: do you find them effective?

I learned how to use a POSIX shell years before I learned how to program (outside of a shell, that is), and I've often wondered how to help people (particularly younger colleagues) who learned things the other way around.

My experience so far is that a lot of people get stuck in an "experience trough," for lack of a better phrase: they're comfortable navigating around the filesystem and running basic commands, but struggle with some of the more powerful building blocks that can make someone a real shell power user (`xargs`, nontrivial uses of `find`, here-strings, subshells and blocks, process substitution, etc.).


The single biggest stumbling block I've seen in my co-workers is quotes. Most of them think in terms of python/javascript/etc where quotes mean string type, and have no concept of how args and splitting on spaces works, or how it's all stringly-typed.

That "experience trough" you describe I think exists because they don't actually understand the fundamentals, so any time they've attempted something new it's based on assumptions from a totally different paradigm, does something strange and unexpected, and reinforces sticking to the same safe and simple commands.


Yup. I came the same path as you, and realized about the same.

It's just a different mindset. Instead of a std library you have tons of prebuilt binaries ready to perform actions and talk to one another. And that is really, really, powerful once you learn what's all available and how to use/abuse it.

Software engineering in general is so different. Tons of mantras to follow, TDD, OOP, etc.

Each has its place, but generally if I can do it in a few lines of shell, why spend 20 minutes writing a hundred lines of something more pure? On the other hand, shells do not seem well suited for anything large and complex, admittedly. So watching people waste an hour writing something that could be a Bash one liner is about as painful as watching someone try to debug a 500 line bash script.


The better phrase might be the "tar pit of immediacy", from the Unix chainsaw talk by Gary Bernhardt. Highly recommend: https://www.youtube.com/watch?v=ZQnyApKysg4


I did “enough shell to be dangerous” which I guess counts as “these kinds of guides” and it opened the door for using the shell for me.

I’m not really sure it’s fair to assume that you can use advanced topics after reading any book aimed at beginners.

Also, no one is going to learn how to use any of your advanced topics unless they have a need. And people rarely have a need for those things.


Do you mind elaborating? What do you recommend for progressing out of that? I'm pushing for Red Hat learning from my employer. Any favorite learning resources?


This is the problem: I don't really know. It doesn't help that POSIX sh (and GNU Bash) are objectively terrible programming languages, and that it takes a decent amount of masochism to "enjoy" them in the way that (I think) engenders competence.

For me, it was a lot of scripting and then iterating on those scripts when I realized that they conceptually broke down in places (even if they never broke in practice). Things like spaces where you don't want them, needing to pass around data that might be too large for `argv` (because of stack limits), etc.


One needs to sit down and read through in-depth documentation or a book like this one. Many people don’t seem to have the patience or interest to do so.


have you found any ways to get past that experience trough? I definitely feel like i’m there now


The author of the musl C library has a very poor opinion of the POSIX shell, and has published his "tricks" to force expected behavior.

This is very useful for those who are stumped by a failing script.

"I am a strong believer that Bourne-derived languages are extremely bad, on the same order of badness as Perl, for programming, and consider programming sh for any purpose other than as a super-portable, lowest-common-denominator platform for build or bootstrap scripts and the like, as an extremely misguided endeavor. As such you won’t see me spending many words on extensions particular to ksh, Bash, or whatever other shells may be popular."

https://www.etalabs.net/sh_tricks.html


For me, it helped to stop thinking of the shell as an efficient way to accomplish a task (it certainly is), but as a way to express my intent (the way I would in a normal programming language). That, combined with some automated tooling (shellcheck) and a handful of best practices (arrays over strings, avoiding the "cat pipe" pattern[1], etc.) has worked well for me. But I don't know how well that generalizes!

[1]: https://www.linuxjournal.com/content/put-down-pipe


> avoiding the "cat pipe" pattern

For me, `cat | grep/sort` is easier to remember as it follows some unixy philosophy on how to do things.

I wonder why that article author is so obsessed with putting down cat/pipe. Maybe in scripts where you process large files and sort/grep it is less ineffective. But when writing shell commands, I see this as an empty argument to retrain my "muscle memory" and learn "different syntax".

In fact, when writing powershell I do the same: `gc something | sls something`. But I know it is less effective, but I'm lazy to look up proper syntax: `sls ?? something` - But if I ever will need that in a script to process non trivial amount of files-lines - I'll look it up.


Along those lines it's interchangeable with other sources like nc or ssh or whatever, with no changes to sort/grep.


I did it by blocking out time to read the manual pages. Yes, it is slow and tedious. But the manuals make a lot more sense once you already know how the tools work at a basic level.


> but struggle with some of the more powerful building blocks that can make someone a real shell power user (`xargs`, nontrivial uses of `find`, here-strings, subshells and blocks, process substitution, etc.).

I am one of those... as soon as I don't know how to do something in the shell, though, I just fire up a REPL or a "scratch" in the IDE and write what I want in a "proper" language. Sorry, but shel scripting is not always the best way to go. I eventually did learn me some AWK and that was useful, but most of the time I don't use even that, it's just so much easier for a programmer to use a programming language to do stuff.


Which repl do you use to replace xargs?


Conceptually, xargs applies some logic to each item in a list. Many programming language support this, e.g. Python:

    for item in some_list:
        foo(item)
Of course, the above is a replacement for "xargs -n 1" only; but I argue that the batching functionality of xargs isn't really needed in programming languages.


Not GP, but I use perl, which I suppose isn't much better from the perspective of something that's easy to learn :/


You don't need xargs when you're using a programming language.


Good guide to shell in general.

Google shell style guide [0] was also a good read. I thought that the "When to use Shell" section is a section that is good for any kind of guide, not just for bash / shell.

Also, maybe not so much a pitfall / bug, but something I had to deal with recently was that bash does not handle the EINTR when calling write() in the printf and echo builtins [1][2][3], etc.

[0] https://google.github.io/styleguide/shellguide.html#s1.2-whe...

  If you are writing a script that is more than 100 lines long, or that uses non-straightforward control flow logic, you should rewrite it in a more structured language now. Bear in mind that scripts grow. Rewrite your script early to avoid a more time-consuming rewrite at a later date.
[1] https://unix.stackexchange.com/a/487260

  handle the EINTR when calling write() in the printf and echo builtins. 
[2] https://github.com/torvalds/linux/blob/ca1fdab7fd27eb069df13...

  Q: what's up with this '/bin/echo' ?

  A: bash's builtin 'echo' command does not check calls to write() against
   errors. If you use it in the cgroup file system, you won't be
   able to tell whether a command succeeded or failed.
[3] https://lists.gnu.org/archive/html/bug-bash/2018-01/msg00031...

  write() not retried after EINTR in printf and echo


I will use the word `bash` for convenience. `bash` is powerful because it is convenient. `bash` is also powerful because it doesn't tell users how to behave or what to do. `bash` is all about empowering the user. That is what makes it great. `bash` is a meta language for the convenience and the empowerment of programmers. Over time, impossible tasks become trivial. You start off by using `bash` to create simple models of larger/more complex problems, then you adopt pipes and redirection, and eventually you don't need much handholding and you are 10x better at modeling problems, mocking designs, and general problem solving. Will there be 8 hour days to understand some simple concepts? Sure. Do they pay off? Yes. Absolutely yes. Undergraduate computer science background makes bash even more useful and helpful. Outside of college learning, you might be able to grok some things using man pages, tutorials, and various resources (like books).


Bash is so painful to use without vi mode (set -o vi).


I've actively used bash and vi every day for decades now, but somehow never tried this mode.

It's fascinating. I wonder if it will be worth it for the change of command-line editing habits. It certainly feels faster for most command-line edits.


I am a vi user and tried vi mode in zsh but never liked it. For some reason the modal aspect doesn’t fit for me in the context of the shell. I’ve learned enough emacs bindings to work the shell.


It changed my life. It's like your prompt becomes a mini editor now and you can copy/paste arguments around in your command. I love it.


Heavy shell and vim user here, but I simply cannot imagine how this might be convenient. A bunch of readline-style shortcuts will do the job just fine. And they are everywhere. SSH, database prompts, even login screens support them.


Preemptively justifying something more efficient that you haven't used, because what you have is "fine," is a behavior I see from heavy Vim users when they justify not using IDEs, which are much more efficient than Vim for writing code. (I'm also a heavy Vim user, and IDEs are so much nicer for coding, especially efficiency). So, I'd say your story checks out.


I did try this mode some time ago and found it really inconvenient. Also I'm not a person to dismiss IDEs, and I never recommend Vim to anyone I know as a better way of coding.

No hard feelings, I see how you might have read that from my comment.


Programs that use readline can be configured to use vi mode via inputrc. For the few that aren't you can always use rlwrap. Overall there's no downside.


I hadn't heard of these options before, thanks for highlighting them.


I've never heard of bash vi mode before. Thanks for this.


Obligatory pedantry: Bash is A shell. It's definitely not "The Shell".


It kinda almost is though.


With Ubuntu using dash as its /bin/sh and macOS defaulting to zsh for interactive shells (I think their /bin/sh is still bash, but haven’t checked), this is probably the least true now that it’s been for a couple decades.


Also, as noted in another comment: windows has its own shell(s) which are entirely unrelated to bash.


People have said the same thing about sh.

And neither bash nor sh are that great, so no reason to put down the idea that they shouldn't be the "default"


Over the past 10 years it has been losing market share to zsh and to a lesser extent, fish. It's really isn't anymore.


Bash is a chameleon.

When called as /bin/sh or if the POSIXLY_CORRECT environment variable is set, it will act as a POSIX shell.

If not, it will behave very differently. The one major difference that I know is that aliases will be ignored in the execution of a script, so "alias p=printf" will fail (for example). This is not POSIX-compliant.


Aliases seem to work on Bash 5.0.17:

    #!/bin/sh
    alias p=printf
    p ok


Try with #!/bin/bash shebang.


Ah, I misread the post. My bad.


Thanks for taking the time to do this and share it. I think it's a great resource for someone just starting out (this guy). Then more advanced materials can take it from there.


I can tell you put quite a bit of work into this, and good job! But there are some major inaccuracies, just from going through the first few pages:

> When we talk about "The Shell", we're normally referring to the simple, text-based interface which is used to control a computer or a program.

This is a bit nitpicky, but I don't like this definition. This is conflating a TUI, a shell, and a terminal.

"The Shell" usually refers to `bash`, `zsh`, `fish`, `powershell`, or `cmd.exe`. The interface is the terminal (emulator), because programs like to dump ANSI control codes and the terminal takes those control codes and turn them into human-readable output with colours and such.

> Windows is actually being updated at the time of writing to provide a Linux-like shell interface as part of the core operating system (this is known as the Windows Subsystem Linux

No, no, no. This is just wrong.

If this was WSL1, you would be at least partially correct, since it's a re-implementation of linux syscalls and runtime environment on Windows, kinda like reverse-wine.

But with WSL2, windows now ships the entire linux kernel and virtualises it. The objective is not to provide a linux-like shell interface (whatever that means), but to let people run linux binaries on windows in a linux environment.

Either way, the fact that `bash` (or any other shell) runs on WSL is a result of bringing along the rest of the linux runtime environment.

> install instructions

For the entire WSL section, I'm pretty sure that you can install wsl these days with just `wsl --install` on powershell. Maybe it only works on win11 or something, I'll have to look up the docs. Winget also has ubuntu2004 in the repos. But this is not a linux-like shell on windows, this is using linux, virtualised, on windows.

Downloading homebrew just to install bash is ... interesting. Catalina is 3 years old, and I don't see any bash4/5 specific features that you're using (I had to google bash release notes lol). `zsh` is probably preferable anyways imo.

Also as a sidenote, you can get pretty close to a linux-like environment by just installing busybox on windows. You'll have to be careful around things that powershell aliased away, but if you decide to just prefer busybox for everything then you're like 90% of the way there.

> wget

For some bizarre reason, macos doesn't ship with `wget` by default. Don't ask me why :/

> renaissance of the shell

In my personal experience, shell usage has gone down, not up. What used to be shell scripts are now getting more and more specialised: sysv services became systemd, the rise of devops meant giant piles of Dockerfiles and YAML, etc.

It's still a critical part of system administration and development, but its tentacles are slowly receding from poking into every corner of everything to slightly less pokes in everything.


> For some bizarre reason, macos doesn't ship with `wget` by default. Don't ask me why :/

Probably because of its licence: wget is GNU, curl MIT. Apple stopped updating bash, e.g., due to licence changes, and won’t, in general, ship GPL based tools.

Homebrew to the rescue.


> shell usage has gone down, not up

I agree with you, but the "giant piles of Dockerfiles" are actually mostly shell commands anyway.


> For some bizarre reason, macos doesn't ship with `wget` by default. Don't ask me why :/

Because curl is better?




Consider applying for YC's Spring batch! Applications are open till Feb 11.

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: