5 ms·
My primary complaint about Unix CLI is that the commands try to do two disparate things simultaneously: outputting text for humans and generating data for consu
by Udo 10y ago
My primary complaint about Unix CLI is that the commands try to do two disparate things simultaneously: outputting text for humans and generating data for consumption by other commands. In a properly designed command line ecosystem (it's far, far too late for that now), there would be good human readable output in one mode, and tools would exchange structured, already escaped data amongst themselves in another mode. Alternatively, the shell would render the output for humans, and tools would just communicate in machine format.
Consider for example executing a compression command on all jpg files in a folder and below. Currently, the syntax goes something like this:
find . -iname "*jpg" -exec gfxcompress {} \;
Naive Unix users would have expected this to be achievable by piping, but no! In reality there is a special syntax of the find command that does this, and it's not completely clear how you would deal with file names further down the line, or how you would achieve further chaining. This syntax is also completely unique to the find command. You can't just plug in another data source and perform the same operation. In a CLI where tools are designed to be chained, it would look something like this:
find | match :name="*jpg" |> gfxcompress :name
In this example I used an imaginary |> operator to indicate I want something performed on every item, and I can refer to the "name" field of each directory entry directly. If I wanted to filter by time, for example, I might use the :time field instead, without any need for explicit support. If I wanted to mutate the name somewhere in the chain, I could do it. If I wanted to chain additional commands, it would be obvious how. And it would just work.
The same goes for error handling. Imagine a compiler spitting out structured error information which you could use directly in other tools without having to parse and interpret it first.
- CWuestefeld 10y agoWindows PowerShell seems to offer exactly what you're looking for.
- Udo 10y agoI'm not a Windows user, so I'm not familiar with it. And "just move to Windows" isn't exactly the kind of solution I was hoping for, if I'm being honest. Some day someone in the vast Linux/Unix ecosystem hopefully sees this problem too and does something about it, but it's a controversial idea at this point.
- d0mine 10y agoThe usability, robustness and the ease of composition of simple text interfaces is not accidental. Pipes survived for many years for a reason (though they are broken on Windows -- it is hard to pipe data as is without corrupting it). SOAP, WSDL have taught us already that more "structure" does not mean better. Binary interfaces such as provided by systemd or http/2 may teach us again the importance of simplicity, human-readability and avoiding too frigid constraints failing in the face of natural evolution between different software versions.
- joelthelion 10y agoThe problem with power shell is that it misses basics such as grep that command line users take for granted.
- ctolkien 10y agoNot really arguing against your point - however when in Powershell, as everything is an object, you don't end up using the same "string heavy" techniques that required grep with more traditional shells.
- ColinCochrane 10y agoSelect-String is the PowerShell equivalent of grep. https://technet.microsoft.com/en-us/library/hh849903.aspx https://technet.microsoft.com/en-us/library/hh849903.aspx
- ygra 10y agoShould even have grep as its default alias.
- besselheim 10y agoIt has "sls" as its alias, even easier to type.
- deleted 10y ago[deleted]
- l0b0 10y agoThis. It would be really great to have a language like PowerShell on Linux. Being able to pass file names reliably[0] without disgusting hacks would be great, for a starter. And, unlike in current nix shells, a new shell should separate responsibilities cleanly between tools (`find` has both `-exec` and `-delete` unlike most commands; `grep` has `-c`), return reusable results (damn you `ls`!), have sensible scoping rules, stop on the first error (like `errexit`, `nounset` and `pipefail` implemented sanely), and just generally be more like a real language. [0] http://www.dwheeler.com/essays/filenames-in-shell.html http://www.dwheeler.com/essays/filenames-in-shell.html
- Symbiote 10y agoThe first feature already exists. You write for i in **/*.jpg; gfxcompress $i in Zsh, or shopt -s globstar # (Which you would have in your .bashrc) for i in **/*.jpg; do gfxcompress $i; done in Bash. Or, if gfxcompress supports multiple arguments, just gfxcompress **/*.jpg Treating the output as objects would be an improvement. I write things like for id in $(< file-containing-ids ); do r=$(curl -Ss http://api.example.org/get/$id | jq .status) if [[ $r ... ... done or the more traditional columnar output and awk, but it would be nice if the shell knew about this, enabling tab completion and avoiding a lot of boilerplate.
- Udo 10y agoI was afraid someone might come in and declare this a non-problem, or school me on the example I chose in an effort to invalidate my overall argument. For every example I could conceivably come up with, there is a locally optimal arcane way of achieving a good result. That's not what this is about. Functional (im)possibility was not the message of my argument, ease of use and structural cleanliness was. The issue here is not really finding a "better" way to implement this exact example, it's finding a better mechanism for what the example represents. Can you make everything work with today's tools? Absolutely. Can you do it without arcane switches and weird corner cases? Mostly not. The question I'm raising is more whether this pain is inevitable or not.
- Symbiote 10y agoA shell for loop is not arcane — that's one of the shell's most basic control structures. It uses one fewer external command (no "find"), which is a great way to avoid the weird (and common) corner cases. But anyway, I think you missed where I wrote, "treating the output as objects would be an improvement", and presented a better example in support of your suggestion.
- Udo 10y ago> But anyway, I think you missed where I wrote, "treating the output as objects would be an improvement" We already agree about that part (at least sort of). Instead I wanted to address the part where you declared my example invalid and where we're talking about nothing else but the details of this one example. Did I choose it poorly? Was it too simplistic? Maybe, it really depends on your tastes. I content that arguing about whether or not the find command is usefully implemented is besides the point. The problem lies deeper and is more systemic than that. I'm not arguing that a loop construct is arcane, I'm arguing that in practice such scripts turn arcane really quickly, and that there is something substantial to be gained from having tools exchange information in a structured, machine-centric format - and also from switching to a human-centric control format.
- heinrich5991 10y agoYou can actually do what you want with piping: find . -print0 | grep -z '.*.jpg$' | xargs -0 gfxcompress
- saurik 10y ago(-0n1 likely, as it sounded from the example like gfxcompress is designed to operate only on one item at a time.) To make this explicit for readers: find, grep, and xargs actually already has the requested feature of "good human readable output in one mode, and [] structured, already escaped data [] in another mode", through -print0, -z, and -0, which also works great when combined with read -rd $'\0'.
- J_Darnley 10y agoOn this point of using a null char to separate things: do you people think that automatically using it when I/O is not connected to a terminal is a good idea?
- LukeShu 10y agoSort of. Obviously, for a great many tools that wouldn't be an option because it would break compatibility for scripts that have been running for decades. And \0 isn't necessarily a safe delimiter for all types of lists. But, for lists of file names (and other strings that cannot contain \0), yes, I believe that automatically using it when stdout is not connected to a terminal would be a reasonably good idea.
- continuational 10y agoCode point 0 / ASCII symbol number 0 is valid in ASCII and Unicode strings, so it simply can't be used for termination.
- OJFord 10y agoimaginary |> operator to indicate I want something performed on every item xargs?
- douche 10y agoSo, Powershell, basically?