1. 36
    1. 62

      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”.

      1. 16

        than the counterintuitive ordering of

        It doesn’t have to be counterintuitive. This works too:

        < file do something | do something else

        There’s a slight benefit to the redirecting this way in that the file remains seekable. (It doesn’t with cat)

        1. 18

          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?

          1. 3

            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 < 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 where cat file | sits.

            1. 5

              If my original pipeline begins < file . . .

              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.

              1. 8

                Hmm, it displays the file in zsh, but apparently not in bash.

                But now I know how to annoy both groups!

                < myfile cat | wc -l
              2. 1

                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.)

          2. 2

            I’m not sure that’s something I ever gave any attention. I mean, it’s slightly different and I don’t mind ¯\(ツ)

        2. 1

          This doesn’t work in /bin/sh on my latest MacOS.

          1. 5

            Maybe something else is going on?

            macOS 12.1, bash 3.2 (at /bin/sh), and it works fine here.

            sh-3.2$ < wtf.c grep assert | sed 's/assert/wtf/'
            #include <wtf.h>
            	wtf(sodium_init() != -1);
            1. 3

              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, while cat does;

              This doesn’t work (but it works in some other shells):


              This works:

              data="$(<file cat)"
              1. 1

                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.

                sh-3.2$ data="$(< wtf.c)"
                sh-3.2$ printf "${data}\n"
                #include <assert.h>
                #include <sodium.h>
                int main()
                	assert(sodium_init() != -1);
                	return 0;

                What are you trying to do next?

                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.

          2. [Comment removed by author]

      2. 15

        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!

      3. 3

        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.

    2. 8

      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.

      1. 7

        I have modified the README to clarify this is meant to make fun of not liking cat-into-pipe.

        1. 4

          Should be tagged satire in that case.

          1. 2


    3. 6


      Seriously: shellcheck will catch “useless” cat in scripts. In interactive work, use cat to your heart’s content ;)

    4. 3

      This is wrong on so many levels…

      • ./good-cat a b | …
      • ./good-cat -n a > numbered-a.txt
      • So called UUoC is mostly a myth and unfounded hate.

      OK, it is satire, but still too bad.

      1. 4

        This could be easily fixed with an extra test, only check for pipes if [ $# -eq 1 ]

    5. 3

      good-cat -v should do something funny.

    6. 2

      Would exec cat $@ be better?

      1. 1


        1. 6

          Isn’t the $@ supposed to be quoted? "$@"

    7. 2

      my sh doesn’t like it:

      line 6: [: missing `]'
      ➜ $(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.

      1. 2


    8. 1

      Nice. Love the read me.