<file grep ...
It would give me pause. If I saw cat file | grep ...
I would understand it instantly, as would any other Unix user. Therefore the latter is better code. <file grep ...
It would give me pause. If I saw cat file | grep ...
I would understand it instantly, as would any other Unix user. Therefore the latter is better code.Shell has tricky and arcane corners that are best to avoid, but you still do better to know about them, lest they bite you (shellcheck helps).
I don't think that "I/O redirection can precede the command name" is particularly tricky or arcane.
What bothers me is that it doesn't work with loops:
<input.txt while read -r line; do thing; done # error
while read -r line; do thing; done <input.txt # fine
The shell's grammar is quite a thing.The typical retort I hear is “but it’s just this one feature, how hard is it to learn just one feature?” But you’re only considering your favorite feature. To the casual code reader, it isn’t just that one feature, it’s everyone else’s favorite obscure feature as well.
Using cat makes your code readable to a wider audience. Using cat has real upside with no downside. Forcing your readers to learn more shell syntax has real downside with no practical upside.
Yes, let's be considerate and not use these features. But then we must agree on the specifics of the subset. If we're going to do that, then let's just learn the language proper instead. Or, avoid shell scripts entirely.
Maybe there's an analogy with writing prose. I _could_ use rare words and uncommon syntax, but the modern style is a better approach. Is "abstruse" a rare word? Will a reader be frustrated to necessarily learn about split infinitives? What about I/O redirection at the beginning of a command?
Sometimes the readability is the most important feature and all other concerns can be compromised or sacrificed to readability.
But for me, usually not. As far as I'm concerned that is limited to training materials and showing your work in math exams. The rest of the time it's merely a goal after all other goals, ie you don't want things to be actually obfuscated, all else being equal.
Everything in programming is new to someone at some point. There are useful features few people know about, and you get them to be widespread and familiar by using and talking about them.
Just because you only ever learned about `for` loops doesn’t mean you shouldn’t understand and use `map`.
Furthermore, a little memory lapse or typo, and, oops! >byebyefile
`<fileA command` OTOH at least visually gives off the impression that it's sending the command's output to the left (changing the prompt, perhaps?)
grep <pattern> file?The whole point of the "never use cat" Unix advice is a war between instrumentalism and design purism. Should things be known mostly for what they're useful for, or mostly for what they were made to do? If you understand this, then the war is not soluble but you can at least phrase a third position that will reconcile both sides. If you don't understand this, then the war is over an issue that doesn't even exist.
Hot take: `<file> | abc` and `abc | <file>` should both make sense because a file should be understood by default as a command that reads from and writes to a particular destination, and the shell should take it seriously that `abc | <file>` needs to be easily undoable if the file wasn't chmodded +x appropriately.
For an instance where there are thousands of bash scripts out there that work around a case where `abc >file` does not work, consider the times when you want to write to a file which you don't have write permissions to, with sudo. my favorite way to do this is with `tee` but I have also seen others: but `>file` is not one of them! Because it's emphatically not part of the algebra of the rest of the shell; the rest of the shell is written in pipes, this one command says "I'm going to make your pipeline terminate, this step will be un-pipeable."
For another example where the algebra doesn't consistently handle the whole system, consider that in a normal language you would consider writing a function which returns a value and also prints debug logs to the process's stdout. Bash can't do this on its own, you have to figure out which /dev/tty is attached to the process and then write your debug log to that TTY, and then your script probably fails in interesting ways when it itself is redirected to a file and there is no TTY that it is attached to anymore...
And how do you envision this would work inside a non-interactive Bash script? A shell option or environment variable that removes the confirmation step? With alternative syntax this would indeed work, because then it’s explicit.
> this one command says "I'm going to make your pipeline terminate, this step will be un-pipeable."
And your method would work like tee and not only write to the file, but also pass it on to the next step in the pipe? If not, the pipe still terminates at that point.
CAT(1)
concatenate [one or more] files and print on the standard output
A list of length one is still a list. It's best to write code that can handle lists of any size, not to write special cases that handle a fixed number of primitives.You can swap `cat` for `zcat`, for example, and things still work in just the same way. This would be very awkward to do with input redirection.
Maybe using `cat` like this appeals to people with more of a functional programming exposure.
If we were going to completely change the shell behaviour I think `file > grep` makes more sense than `file | grep`.