The Unix Philosophy from another point of view
... "do one thing well" is just a consequence.
A definition
By the Unix philosophy, we mean the concept of "let's do one thing and do it well".
Namely, grep can filter lines of text. It does not do a lot of other things; it does not understand Javascript, it can not connect to a network, and it can't send email, either. And yet, you can use it a tool, for this one purpose, in the process of accomplishing pretty much all of these goals: you can look for a Javascript function with it, you can use a network-mounted file system (... or combine it with wget or curl to sift through a HTTP response), and yes, you can also pipe the results into an email.
Compare all this to, for example, a GUI image batch renamer tool:
It can do regex replace. It can move files. It even seems to have a "Javascript rename" option, whatever that might mean. All in one. It'll probably do most of what you'd ever need. If you want something else though... tough luck.
The conclusion seems to be simple: just keep your tools small, focused and sharp. Except...
Why "simple"?
Because then you can combine them in various ways!
grep will happily interact with exiftool because both operate on text. Or there is xargs, to feed lists of files to other programs. All this can be launched from a shell script, or right from a command line one-liner. As it looks like...
Unix commands are lines of text, operating on lines of text.
... also, other files, but "lines of text" is usually part of it.
Because of this, you can compose them in many possible ways:
- shell scripts run them in sequence, turning multiple commands into a single command
- pipes let you transform data in multiple stages
- you can even nest them: xargs operates on the actual text representation of a command, turning a single invocation into multiple ones, depending on what's coming on stdin.
However... do we need "simple" for this? Just look at the gcc command, for example. It can compile C and C++ programs, produce object files, link object files, create executables, shared libraries, dependency files; it can produce error messages in different formats, and has a gazillion options. And yet, no one would argue that invoking it makes your script less unix-y.
(Also, just look at awk. It's an actual programming language; it's fairly far towards the "non-simple" end of the spectrum. But it is configured by textual arguments, it consumes lines, it emits lines. That is all it takes to be unix-y.)
So... what's up with simplicity then?
Well, they're small because
they don't have to be larger.
If you write a GUI app that can find lines of text with a specific pattern in it, it's mostly useless until you also add functionality to specify which files to operate on (... extra feature: entire directories!); you do need to present the results somehow (scrollbars!), maybe save it (more dialog boxes). Maybe allow saving previous searches? (A dropdown. Do we store previous queries in the registry? Add an option to clear them?)
Meanwhile... grep has to do none of these things, since it has the right interfaces (textual config, text in, text out) already available. You don't have to add a file open dialog; the shell already has nice completion, and then there is cat to read files (... although, technically, grep can also read files for you for convenience). Presenting results? Just pipe it into "less" or whatever you like! Saved searches... well, shell command history. Or put it into a shell script.
But it's also good to be small because
"small" forces you to fit into the "lines of text" API.
If grep had options to send you email with the results or save it into a file, it'd be tempting for its authors to say: "we covered everything, why do we need stdout at all". Ohh you can't use "less" that way? Let's add a pager, too! Of course, all these things are probably sub-par implementations of things that already exist. Before you know it, you have an integrated app, probably with a GUI, that can do everything for you, but not too well.
If, on the other hand, you're not allowed to add an email client, you'll have to interoperate with one. Given how this is Unix, the way you'll do this is "piping to stdout". As a result, you've "added" a bunch more features, too, ones you didn't even think of, because by making your program composable with one other component, you've made it composable with many of them.
This is post no. 7 for Kev Quirk's #100DaysToOffload challenge.
... comments welcome, either in email or on the (eventual) Mastodon post on Fosstodon.