Ah yes, one of the super toxic throw away comments anyone can make which alienates one participant and makes the other feel super smug.
These things are mostly going away, phone keyboards killed the “grammar nazi”, no one can spell anymore and no one knows who is at fault. I’m looking forward to a world where our default isn’t trying to win the conversation, but to move it forward more productively.
I’m looking forward to a world where our default isn’t trying to win the conversation, but to move it forward more productively.
Probably a bit optimistic
On UUOC: I suspect it was originally in the spirit of fun. It becoming a cultural gatekeeping tool (as many, many in-group signifiers become) is a separate phenomenon and I think it’s probably best to view it as such
IMO the issue is that pointing at other people’s code and saying “lol that’s dumb” is really only something you can do with friends, with strangers over the internet it’s a very different thing
Often, people in these forums are friends to some extent and I’d assume that part of this that became toxic is that since you’re not ALL friends, it’s hard to tell who knows who and what’s a joke and what’s a dig.
“but Nelson”, you’ll say, “you can just do grep bar foo or grep bar < foo”. Yes, yes I could. but the next thing I’m going to do is
zcat foo.gz | grep bar
or maybe
cat foo baz | grep bar
or maybe
awk 'blah blah' foo | grep bar
and all of those things are way less edit distance in my readline editor than if I’d done the “optimal” thing in the first place. Note the end of the command doesn’t change. That gets real handy when it’s a long pipe and not just a single grep.
Always optimize the programmer’s time for one-off hacking.
(And all this before we even get to the topic of how many programs function differently if it thinks it’s talking to a tty or a pty. I do ls | cat all the damn time just to eliminate the colors when I need to.)
and all of those things are way less edit distance in my readline editor than if I’d done the “optimal” thing in the first place.
I feel like UUOC should not apply for interactive use; the time and resources it takes to execute cat is nonexistent compared to the amount of time it takes to refactor your pipeline to work around it. Shell scripts are a different story, as I have seen UUOCs legitimately result in slowdowns, although most are fairly benign as well.
Yes, but just < foo doesn’t work, so it’s still worse for iteratively building up a pipeline. I could remember all these special cases and workarounds, or I could just use “useless” cat and be at peace.
Many accusations that a cat is useless are technically wrong as well as obnoxious. They typically neglect the fact that unix is an interactive programming environment and shell programs are often composed incrementally. This is easiest to do left-to-right. And in scripts, redundant commands can make pipelines easier to read, so they aren’t useless.
Recently I have been comparing a collection of source trees. I start off by working out which subset of files I am interested in, eventually resulting in a command like
ls ~/path/to/repo/subdir/*glob*
Then I operate on those files with commands like
ls ~/path/to/repo/subdir/*glob* | xargs wc -l
Which might seem like a useless use of ls and a useless use of xargs, but I can uparrow to find a previous instance of the analysis, ctrl-k to chop it off the end of the command line, and ctrl-y to append it to the latest ls command. This still works when the analysis is a multi-stage pipeline.
You’re neglecting the fact that I was using unix as an interactive programming environment and my shell programs were composed incrementally. I wrote that I looked at the output of ls first, so of course I knew the rest of the pipeline would work safely. I wrote the ls commands before I knew I would need more automation, so it was much more convenient to append an xargs (which I could copy and paste) than to edit half a dozen ls commands.
One more reason not yet mentioned to prefer cat: it provides some degree of assurance that the named file won’t accidentally be written to instead of read from
Note that although in most circumstances cat file | stuff is unnecessary and should just be stuff < file, the difference is significant in certain contexts. E.g. I was working on a program that uses splice(2) on standard input, which won’t work if standard input is an actual file (or a character device), so cat file | stuff and cat | stuff would work while stuff < file and stuff would not.
One nice thing I found when trying to avoid “useless” cat invocations was that files can be piped like < myfile grep "foo" | bar | baz, which I find nicer than the more-common grep "foo" < myfile | bar | baz since it reads left-to-right. Previously I would achieve the same using cat myfile | grep "foo" | bar | baz.
Not a big deal, but nice to learn more about the shell I use everyday :-)
It’s always bugged me that bash can replace $(cat file) with $(< file) to read a file without any subprocesses, but if you just type < file at the prompt it doesn’t do anything (besides error if the file doesn’t exist).
It’s a shame UNIX wasn’t designed as a capability system. In a capability system, the shell would be responsible for opening files and passing them as file descriptors to each invoked command. UNIX has an awkward mix where the shell is responsible for passing in three file descriptors but other files are expected to be opened by the command itself.
In my ideal world, programs would start in capsicum mode and you’d get a parallel array alongside argv that told you which file descriptor numbers any file or directory arguments had been passed as. If you did cat foo then argv[1] would be the string ”foo” but fdv[1] would be 3 and the shell would pass an open file handle to foo in as file descriptors 3. You’d also then be able to tweak the command in the shell, to pass a read-only file descriptor instead of a read-write one (since cat doesn’t need write access to the input file).
Once you’re in that model, it’s easy to have a consistent concept that the shell is always the thing that does file name to file descriptor mappings and you can make that much more consistent in the shell syntax.
If you were to retrofit this to existing systems, you’d probably manage it via an environment variable rather than an additional argument to main.
Ah yes, one of the super toxic throw away comments anyone can make which alienates one participant and makes the other feel super smug.
These things are mostly going away, phone keyboards killed the “grammar nazi”, no one can spell anymore and no one knows who is at fault. I’m looking forward to a world where our default isn’t trying to win the conversation, but to move it forward more productively.
Ah yes, “the good old days”
The 1990s were so good that some people’s biggest problem was other people wasting a process in their shell scripts. 🙃
Probably a bit optimistic
On UUOC: I suspect it was originally in the spirit of fun. It becoming a cultural gatekeeping tool (as many, many in-group signifiers become) is a separate phenomenon and I think it’s probably best to view it as such
IMO the issue is that pointing at other people’s code and saying “lol that’s dumb” is really only something you can do with friends, with strangers over the internet it’s a very different thing
Often, people in these forums are friends to some extent and I’d assume that part of this that became toxic is that since you’re not ALL friends, it’s hard to tell who knows who and what’s a joke and what’s a dig.
My favorite useful use of cat:
cat foo | grep bar“but Nelson”, you’ll say, “you can just do
grep bar fooorgrep bar < foo”. Yes, yes I could. but the next thing I’m going to do iszcat foo.gz | grep baror maybe
cat foo baz | grep baror maybe
awk 'blah blah' foo | grep barand all of those things are way less edit distance in my readline editor than if I’d done the “optimal” thing in the first place. Note the end of the command doesn’t change. That gets real handy when it’s a long pipe and not just a single grep.
Always optimize the programmer’s time for one-off hacking.
(And all this before we even get to the topic of how many programs function differently if it thinks it’s talking to a tty or a pty. I do
ls | catall the damn time just to eliminate the colors when I need to.)I strongly agree.
A huge part of the productivity of the shell is interactively and iteratively building up a series of transformations to do something.
Depends.
Some older programs such as col(1) would require ‘<’ redirection due to ioctl() reasons.
I feel like UUOC should not apply for interactive use; the time and resources it takes to execute
catis nonexistent compared to the amount of time it takes to refactor your pipeline to work around it. Shell scripts are a different story, as I have seen UUOCs legitimately result in slowdowns, although most are fairly benign as well.For what it’s worth, you can do
< foo grep bar.Yes, but just
< foodoesn’t work, so it’s still worse for iteratively building up a pipeline. I could remember all these special cases and workarounds, or I could just use “useless” cat and be at peace.It does in zsh (as long as you don’t unset NULLCMD and READNULLCMD).
But I still use cat, easier to type and composes better.
Many accusations that a cat is useless are technically wrong as well as obnoxious. They typically neglect the fact that unix is an interactive programming environment and shell programs are often composed incrementally. This is easiest to do left-to-right. And in scripts, redundant commands can make pipelines easier to read, so they aren’t useless.
Recently I have been comparing a collection of source trees. I start off by working out which subset of files I am interested in, eventually resulting in a command like
Then I operate on those files with commands like
Which might seem like a useless use of ls and a useless use of xargs, but I can uparrow to find a previous instance of the analysis, ctrl-k to chop it off the end of the command line, and ctrl-y to append it to the latest ls command. This still works when the analysis is a multi-stage pipeline.
I absolutely agree with your point, but not with your example. Not because it’s useless - it isn’t - but because parsing ls can be dangerous.
I often build my pipeline with a short
for-loop in zsh instead:Exchanging
wc -lfor echo (or even cat) here allows for much of the same flexibility as in your example :)You’re neglecting the fact that I was using unix as an interactive programming environment and my shell programs were composed incrementally. I wrote that I looked at the output of ls first, so of course I knew the rest of the pipeline would work safely. I wrote the ls commands before I knew I would need more automation, so it was much more convenient to append an xargs (which I could copy and paste) than to edit half a dozen ls commands.
One more reason not yet mentioned to prefer
cat: it provides some degree of assurance that the named file won’t accidentally be written to instead of read fromWow, this was written before Google was the thing
Note that although in most circumstances
cat file | stuffis unnecessary and should just bestuff < file, the difference is significant in certain contexts. E.g. I was working on a program that uses splice(2) on standard input, which won’t work if standard input is an actual file (or a character device), socat file | stuffandcat | stuffwould work whilestuff < fileandstuffwould not.Are there any shells where cat is a builtin? I’d have thought it would be trivial for a shell to turn cat with a single argument into a redirect.
The real gem is at the end:
One nice thing I found when trying to avoid “useless” cat invocations was that files can be piped like
< myfile grep "foo" | bar | baz, which I find nicer than the more-commongrep "foo" < myfile | bar | bazsince it reads left-to-right. Previously I would achieve the same usingcat myfile | grep "foo" | bar | baz.Not a big deal, but nice to learn more about the shell I use everyday :-)
UUoC has been discussed many times, e.g. https://lobste.rs/s/jfydw6/turn_shell_commands_into_apis#c_epmju3
It should be seen as a food for thought that encourages seeing things from different angles, rather that a stick to beat others with.
This gives an example of a useful use of cat:
It’s always bugged me that bash can replace
$(cat file)with$(< file)to read a file without any subprocesses, but if you just type< fileat the prompt it doesn’t do anything (besides error if the file doesn’t exist).It’s a shame UNIX wasn’t designed as a capability system. In a capability system, the shell would be responsible for opening files and passing them as file descriptors to each invoked command. UNIX has an awkward mix where the shell is responsible for passing in three file descriptors but other files are expected to be opened by the command itself.
In my ideal world, programs would start in capsicum mode and you’d get a parallel array alongside argv that told you which file descriptor numbers any file or directory arguments had been passed as. If you did
cat foothenargv[1]would be the string”foo”butfdv[1]would be 3 and the shell would pass an open file handle to foo in as file descriptors 3. You’d also then be able to tweak the command in the shell, to pass a read-only file descriptor instead of a read-write one (since cat doesn’t need write access to the input file).Once you’re in that model, it’s easy to have a consistent concept that the shell is always the thing that does file name to file descriptor mappings and you can make that much more consistent in the shell syntax.
If you were to retrofit this to existing systems, you’d probably manage it via an environment variable rather than an additional argument to main.