The useful use of cat
mrmr.io
mrmr.io
If I need to build a long command I've been using the excellent `up` tool to do it, e.g. `cat file.txt | up`
Use whatever you prefer¹, but if what you care for is the logic of reading left to right, you can do so without `cat` by doing instead `< file.txt | ...`
¹ It’s absurd this needs to be spelled out, but here we are.
I don’t think that’s what they’re saying. I think they’re making the point that going left to right with the file first is what makes sense to them, as opposed to `... < file.txt`.
And you’re assuming they do. It’s safe to assume at least one person reading the comment won’t know about the feature, the information is useful to more than that poster.
> but I can still see an argument for "cat" being more natural than "<" in this context.
You’re also assuming I don’t understand that sentiment. That has nothing to do with my original post. I don’t care what you use, do whatever makes you happiest. I was merely offering an alternative that would still satisfy what I perceived to be the original point (command piping left to right) to share knowledge, I was in no way criticising their method or saying they shouldn’t use it.
This is a useless conversation.
I want to type 'cat'.
The visual imagery of summoning a cute cat to do my POSIX sorcery trumps all else.
Then do. I’m certainly not stopping you, or saying you’re wrong to do it. I offered an alternative with no judgement for the other method.
<file grep something | do something else ...
this works in bash too, but if you're using zsh, there're a couple of nice shortcuts <file
on it's own works as more file
and > file
...
...
^D
allows you to put something into the file quickly without firing up the editor. Though > file << end
...
end
will give you nicer line editing experience.more zsh redirection tricks here: https://zsh.sourceforge.io/Doc/Release/Redirection.html#Mult...
<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.Furthermore, a little memory lapse or typo, and, oops! >byebyefile
grep <pattern> file?`<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?)
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`.
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`.
cat file|grep x
<file grep x
grep x file
The two alternatives are several keystrokes less and remove a useless process. # bashisms
<file tr z-o m-g | …
FOO=bar python3 …
# processes
cat file | tr z-o m-g | …
env FOO=bar python3 …https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V...
So is FOO=bar cmd.
(I guess it is shell-specific if you include csh in the game, but then what isn't...)
I find myself googling "OpenGroup shell command language" pretty often to check this sort of thing.
pbpaste > file
and if you're using zsh, just > file
is sufficient, without catRather, people argue against useless use of cat, which is different. Personally, I'd never argue against it on the command-line even though some do. It's once you commit your pipeline to a script where you may as well remove it.
There are also suitable uses of cat that I don't think anyone would argue against. Besides the obvious use of outputting one or more files, it's also often used with here docs.
Nitpick: this should be „maximum“, maxima is the plural.
`cat x | head` attaches `cat x`'s stdout to `head`'s stdin, `< x head` just attaches `x`'s fd to stdin. In this way, treating a file like a "light-weight" command (light-weight because no backing process) makes more sense, `open x | head` perhaps
Though I will note that, in a way, bash supports this, as it has a modules system, and one of the pre-existing modules in the source tree is a `cat` replacement, through my lack of understanding of C prevents me from working out if its optimal
https://git.savannah.gnu.org/cgit/bash.git/tree/examples/loa...
Is it as optimal as just attaching the file descriptor? No, because you will have to pipe the contents. Is it optimal (or at least optimal enough)? Considering how pipes work, yes.
I would not call that test driven development (where are the testcases?)
Rather more like REPL driven programming.
f(){ cat;} <file
f # prints out contents of file
I.e. You can bind redirections to function definitions that end up applying at call time.Why make the shell open and read the file and pipe in to the cat process when cat would open and read the file itself directly?
I guess the point is not cat but the redirection itself. ie, if the contents of f() were not simply cat, and maybe there was no handy place to put the filename within. Though, I think I still struggle to think up an example even then.
log(){
foo "$1"
bar "$2"
} >>logfile
Redirecting stdin is a little more subtle: contents(){
maybeReadStdinA
maybeReadStdinB
} <input
where the first command to read stdin gets the contents of input and subsequent commands see nothing. cat | sudo tee somefile > /dev/null
is still my favorite way to paste text into an ssh session and save it to a protected file. <file ssh 'cat >file'
And you could just use scp, but I've found clients without scp and servers with the SFTP subsystem disabled. sudo tee somefile > /dev/null
And you will be able to paste from your clipboard or write anything you want. Without cat or piping. # Open foo.txt and use it as fd 0 in jq
<foo.txt jq '[1,2,3]'
# Create a pipe(). Use the write end as cat's fd 1 and the read end as jq's fd 0
cat foo.txt | jq '[1,2,3]'
Best thing would be a directed graph where each node is a process or a file. A process has at most one edge going into it (stdin) and at most two coming out (stdout and stderr). A file has exactly one edge touching it.Hard to do in text, though. Would look like lisp.
But if teaching or demonstrating shell features, I'd say that it's important to know that redirection is the most efficient, explicit, and available method.
Indeed, a student could learn "cat file | ..." as a standard idiom and never use an initial redirect, but what happens when they come across one "in the wild"? They should know its proper interpretation, because it can otherwise be a bit jarring to see and difficult to interpret.
So I think I’d definitely tend to prefer <somefile when building up a pipeline. But certainly nobody should be shamed for doing things in the way that feels familiar and ergonomic to them!
What, exactly, are the arguments against `cat`?
I don't mind either way but I find starting with a cat and reading LTR to be very easy to understand.
Cat *log|grep error|tac for example
Tail -500 log | tac
tac file | less less foo.log
G cat file | tacWe had just gotten over tabs vs spaces after barely a thousand years of noticing the other side is objectively wrong.
Something new that you're definitely doing wrong to blog and argue and bicker about; it can't be helped!
I still instinctively start my pipelines with cat half the time, but now I have complicated feelings about it.
Specifically, it indicates some combination of:
1. isn't aware of processes/pipes
2. isn't aware of cat's "primary" functionality
3. isn't aware of shell input redirection
4. isn't aware that shell input redirection can be put before the command
Noob+naive: use cat because don’t know better.
Experienced+smart ass: use shell direction to distinguish oneself from noobs.
Older+wiser: use cat again because it flows left to right, isn’t bash specific like “<file foo”, doesnt special case examples with only one file as input, and allows something other than a file as input or lifting the body of the pipeline into a function without changing the text…
…but mostly because it doesn’t make one come across as a nit picking asshole to the expensive noobs you’ve just spent months trying to hire.
If by that you mean religiously avoiding cat at all cost, no. If by that you mean thinking about what you are actually doing on your commandline I say I do. Which makes me realize that yes, cat is more often than not redundant unless you are actually outputting the concatenation of multiple files.