I don’t get all of the “useless use of cat” hate. Shell is for ad-hoc stream-of-conciousness scripting. It’s not supposed to be perfect or optimised. If you think about the pipeline starting with the file so that
cat file | do something | do something else
makes more sense than the counterintuitive ordering of
do something < file | do something else
then more power to you, do what make sense to you. A human is not going to notice the performance penalty of copying the bytes a few more times in an interactive shell session.
The “useless use of cat” meme is just a “gotcha” superiority complex with no bearing on reality besides somebody getting to feel like they “corrected” you. How many other random tiny insignificant performance hits fly by your smugness every day that don’t enter the memeosphere so you don’t get to feel smart about repeating them?
If you think it’s a real problem, then detect this case in the shell and change the execution strategy there. You don’t get to feel smug, but you will solve the actual “problem”.
How do you produce shell pipelines? For me, it’s always stepwise: I run something, visually inspect the output, then up-arrow and | to another command that transforms the output somehow. Repeat until I get what I want. < file fails this test, I think?
I don’t follow. Like you, I build shell pipelines “stepwise. I run something, visually inspect the output, then up-arrow and | to another command that transforms the output somehow. Repeat until I get what I want.”
How is cat file | better (or worse) than < file? In other words, if my original pipeline begins < file, can’t I keep using up-arrow and tweaking what follows, just as I can with cat file |?
Maybe you’re thinking of < fileat the end of the pipeline? If so, I think that viraptor’s whole point was that you can move < file to the front of the pipeline—exactly where cat file | sits.
That doesn’t pass my test, because < file doesn’t produce any output by itself. If you start off with < file | something else then sure, but I’ve never done that! I find it nonintuitive. But if it works for you, groovy.
That absolutely makes sense. Thanks for clarifying. (I imagined you were starting with cat file | something. If I first wanted to check the contents of file, I sometimes do the same as you describe: cat file and then cat file | whatever. Other times I do less file first because then I can bounce around in the file more easily.)
I’m sorry, but I still think that there may be something else going on. I can use "$(<file)" to assign the contents of a file to a variable in bash 3.2 on macOS.
Re your larger point, you say “there are other cases where < doesn’t work, while cat does” and “This doesn’t work (but it works in other shells).” I think I (sort of?) agree. cat and < are different, and there are an enormous number of differences (e.g., for example) between different shells and even different versions of the same shell. (/bin/sh on macOS is currently 3.2, but I usually run bash 5.1 from MacPorts. Those two have a lot of differences.)
Nevertheless, I think that the OP’s point stands: < file do something generally works as well as cat file | do something. I am not at all a purist about UUoC—like ketralnis, I think that people who say UUoC are generally just being jerks. But, all of that said < file is also important to learn. It often comes in handy, and you can often substitute it for cat file |—though, again, I agree that they are not 1 to 1.
I agree! This project is a joke. Perhaps that should be at the end of the README. If cat wanted to not be pipeable, then it wouldn’t be pipeable. If someone actually measures a performance problem because they’re working with a stream of bytes instead of a random access file descriptor, then they should change it. The left-to-right reading of cating first is nice!
I agree it’s rare that this is an actual problem in a program, to me it’s more that it’s an indicator that the writer might have some things to learn including possible:
the various ways you can pipe stuff into a program in addition to |.
that cat can also be used for concatenating file, it’s not just for printing a file’s contents.
Once you picked that signal up, dealing with it in a way that’s not about stroking your own ego is of course a good idea.
➜ $(which sh) --version
GNU bash, version 5.1.12(1)-release (x86_64-pc-linux-gnu)
Copyright (C) 2020 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software; you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Adding a space between the 1 and the closing bracket on line 6 of good-cat fixes it.
I don’t get all of the “useless use of cat” hate. Shell is for ad-hoc stream-of-conciousness scripting. It’s not supposed to be perfect or optimised. If you think about the pipeline starting with the file so that
makes more sense than the counterintuitive ordering of
then more power to you, do what make sense to you. A human is not going to notice the performance penalty of copying the bytes a few more times in an interactive shell session.
The “useless use of cat” meme is just a “gotcha” superiority complex with no bearing on reality besides somebody getting to feel like they “corrected” you. How many other random tiny insignificant performance hits fly by your smugness every day that don’t enter the memeosphere so you don’t get to feel smart about repeating them?
If you think it’s a real problem, then detect this case in the shell and change the execution strategy there. You don’t get to feel smug, but you will solve the actual “problem”.
It doesn’t have to be counterintuitive. This works too:
There’s a slight benefit to the redirecting this way in that the file remains seekable. (It doesn’t with cat)
How do you produce shell pipelines? For me, it’s always stepwise: I run something, visually inspect the output, then up-arrow and
|
to another command that transforms the output somehow. Repeat until I get what I want.< file
fails this test, I think?I don’t follow. Like you, I build shell pipelines “stepwise. I run something, visually inspect the output, then up-arrow and
|
to another command that transforms the output somehow. Repeat until I get what I want.”How is
cat file |
better (or worse) than< file
? In other words, if my original pipeline begins< file
, can’t I keep using up-arrow and tweaking what follows, just as I can withcat file |
?Maybe you’re thinking of
< file
at the end of the pipeline? If so, I think that viraptor’s whole point was that you can move< file
to the front of the pipeline—exactly wherecat file |
sits.That doesn’t pass my test, because
< file
doesn’t produce any output by itself. If you start off with< file | something else
then sure, but I’ve never done that! I find it nonintuitive. But if it works for you, groovy.Hmm, it displays the file in zsh, but apparently not in bash.
But now I know how to annoy both groups!
That absolutely makes sense. Thanks for clarifying. (I imagined you were starting with
cat file | something
. If I first wanted to check the contents offile
, I sometimes do the same as you describe:cat file
and thencat file | whatever
. Other times I doless file
first because then I can bounce around in the file more easily.)I’m not sure that’s something I ever gave any attention. I mean, it’s slightly different and I don’t mind ¯\(ツ)/¯
This doesn’t work in
/bin/sh
on my latest MacOS.Maybe something else is going on?
macOS 12.1, bash 3.2 (at
/bin/sh
), and it works fine here.True, I should have clarified what I was trying to do. Your example works for me, but there are other cases where
<
doesn’t work, whilecat
does;This doesn’t work (but it works in some other shells):
This works:
I’m sorry, but I still think that there may be something else going on. I can use
"$(<file)"
to assign the contents of a file to a variable in bash 3.2 on macOS.What are you trying to do next?
Re your larger point, you say “there are other cases where
<
doesn’t work, whilecat
does” and “This doesn’t work (but it works in other shells).” I think I (sort of?) agree.cat
and<
are different, and there are an enormous number of differences (e.g., for example) between different shells and even different versions of the same shell. (/bin/sh
on macOS is currently 3.2, but I usually run bash 5.1 from MacPorts. Those two have a lot of differences.)Nevertheless, I think that the OP’s point stands:
< file do something
generally works as well ascat file | do something
. I am not at all a purist about UUoC—like ketralnis, I think that people who say UUoC are generally just being jerks. But, all of that said< file
is also important to learn. It often comes in handy, and you can often substitute it forcat file |
—though, again, I agree that they are not 1 to 1.[Comment removed by author]
I agree! This project is a joke. Perhaps that should be at the end of the README. If cat wanted to not be pipeable, then it wouldn’t be pipeable. If someone actually measures a performance problem because they’re working with a stream of bytes instead of a random access file descriptor, then they should change it. The left-to-right reading of
cat
ing first is nice!I agree it’s rare that this is an actual problem in a program, to me it’s more that it’s an indicator that the writer might have some things to learn including possible:
|
.cat
can also be used for concatenating file, it’s not just for printing a file’s contents.Once you picked that signal up, dealing with it in a way that’s not about stroking your own ego is of course a good idea.
Nothing wrong with
cat a b | c
. Piping cat’s output into another process is only an “anti-pattern” if you invoke it with a single argument.I have modified the
README
to clarify this is meant to make fun of not likingcat
-into-pipe.Should be tagged
satire
in that case.Done.
Nice!
Seriously: shellcheck will catch “useless” cat in scripts. In interactive work, use cat to your heart’s content ;)
This is wrong on so many levels…
./good-cat a b | …
./good-cat -n a > numbered-a.txt
OK, it is satire, but still too bad.
This could be easily fixed with an extra test, only check for pipes if
[ $# -eq 1 ]
good-cat -v
should do something funny.Would
exec cat $@
be better?Done
Isn’t the
$@
supposed to be quoted?"$@"
my
sh
doesn’t like it:Adding a space between the 1 and the closing bracket on line 6 of good-cat fixes it.
Fixed!
Nice. Love the read me.