The useful use of cat

(mrmr.io)

90 points | by LorenDB 2 days ago ago

110 comments

  • Arcuru a day ago

    Using `cat file.txt | ...` has always felt more natural to me, so I still use it.

    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`

    https://github.com/akavel/up

    • latexr 18 hours ago

      > Using `cat file.txt | ...` has always felt more natural to me

      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.

      • knighthack 18 hours ago

        I don't care about fancy '<' syntaxes.

        I want to type 'cat'.

        The visual imagery of summoning a cute cat to do my POSIX sorcery trumps all else.

        • latexr 17 hours ago

          > I want to type 'cat'.

          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.

        • kazinator 9 hours ago

          Since when can you summon a real cat using the word cat? Or any word?

      • oneeyedpigeon 18 hours ago

        They're saying they find "cat" more natural than "<" which I can understand. Unless you regularly use "<", it's probably easier to remember "cat" and it's more self-documenting.

        • latexr 18 hours ago

          > They're saying they find "cat" more natural than "<" which I can understand.

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

          • oneeyedpigeon 18 hours ago

            You're assuming they don't know about `<file.txt ...` That may be the case, but I can still see an argument for "cat" being more natural than "<" in this context.

            • latexr 17 hours ago

              > You're assuming they don't know about `<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.

      • Arcuru 12 hours ago

        No I can't. Because I use fish shell. `cat` can also be a single character if you alias it :)

    • REDS1736 a day ago

      Wow that looks amazing, thanks for telling me about this!

      • agumonkey a day ago

        Indeed. A good addition to fzf and bat

  • codesnik a day ago

    if you prefer to use cat just to read your pipeline from left to right, you can just start your command with file redirection. It doesn't have to be at the end of the command.

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

    • chgs a day ago

      Or you could just use cat

      • mingus88 a day ago

        Coward, use `tail -c +0` like the rest of us useless command cowboys

      • kazinator 8 hours ago

          cat file|grep x
          <file grep x
          grep x file
        
        The two alternatives are several keystrokes less and remove a useless process.
    • globular-toast a day ago

      If I saw

          <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.
      • MathMonkeyMan 20 hours ago

        My coworker said the same thing. But now that you know, will it give you pause?

        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.
        • cellularmitosis 18 hours ago

          “Subsetting” a language is a useful practice. Consider the set of language features someone has to know in order to understand your code. If you can make that set smaller (without unreasonably sacrificing functionality), you lower the barrier to entry. More people can read your code.

          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.

          • Brian_K_White 7 hours ago

            Readability is a concern, but it is only one concern among a bag of conflicting concerns, and it is meaningless to make decsions based on it before deciding the priority of all concerns.

            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.

          • MathMonkeyMan 17 hours ago

            You make good points. The only part I disagree with is "forcing your readers to learn more shell syntax." Who am I to say what the reader does or doesn't know about the shell? I don't even know what I don't know about the shell. The language is a minefield. I always have to look up the rules for how prefix/suffix substitution works, or the finer points of heredocs, or just when exactly something should be quoted, or the precedence of boolean operators, or the difference between "[" and "test", or ...

            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?

      • latexr 18 hours ago

        By that argument, no one would ever learn anything.

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

      • CBLT a day ago

        I don't feel <file is obscure. I've seen shell in that style from coworkers and from open source. Your value judgement against it might just be your experience, rather than something universal.

        • strunz a day ago

          It's definitely more obscure than cat, and even those familiar might be confused with it at the beginning of the line

        • g-b-r a day ago

          For sure many people don't know what it does

          Furthermore, a little memory lapse or typo, and, oops! >byebyefile

      • zahlman a day ago

        `<file` at the start might not be idiomatic, but when I see it I think it makes perfect sense. It puts the pipeline "in order", starting with redirection from the input (and presumably ending with redirection to the output, if not stdout).

        • HappMacDonald 21 hours ago

          I usually interpret `command <fileA >fileB` as meaning "shove fileA into this command, and shove output into fileB". The "arrows" make visual sense this way.

          `<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?)

          • oneeyedpigeon 18 hours ago

            If only we could do `fileA> command >fileb` for the ultimate readability...

      • sgarland a day ago

        Or just use

            grep <pattern> file?
        • pastage 21 hours ago

          The point is you do not know if file is a stream, an archive, too big, currently empty or just contains bad data for your current test. So using cat file is a way to standardize debugging or make development easier.

          • cellularmitosis 18 hours ago

            Also, you can replace cat with pv to get a progress bar. Or replace cat with socat to stream stdin from a network socket. Etc etc.

      • crdrost a day ago

        The problem is that according to this argument `cat` should never be used for the thing it was designed for, because if you use it with more than one argument, it would give most Unix users pause, but if you saw it with only one argument they would understand it instantly.

        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.

        • oktoberpaard 21 hours ago

          So `abc | <file>` would write the stdout of `abc` to the file? Meaning that if you forgot to mark your script as executable, you’ve now lost all your work? I think `abc > <file>` works perfectly fine and pipes don’t need to substitute this behavior.

          • crdrost 20 hours ago

            That's the point is that it's up to the shell to make sure that you don't use your work if you do the wrong thing. Could be as simple as prompting you to make sure -- "Overwrite <filename>? [yn]: " -- or could be something where the old inode is still available under some magic path for a few more commands, or could be that you use special syntax for the command, like "@<path>" is the stdin/stdout command form for the path... or it could be something else entirely, really.

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

        • pastage 21 hours ago

          Can you explain what fou mean by <file> | abc? I have no clue what that would mean. Further what does undoable mean in a shell context?

        • a day ago
          [deleted]
        • makapuf a day ago

          What if you want to grep a bash script file?

        • globular-toast 19 hours ago

          It is the thing it was designed for:

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

      • DevilStuff 21 hours ago

        Better code, yeah I agree. It's way more easily understandable. At least for me though, 99% of my bash use is stuff that will never be seen by anyone else, so the simpler keystrokes one may prove useful. Maybe if I get used to it :)

      • AStonesThrow a day ago

        Both of those invocations would give me pause, because grep is a command that can handle file arguments on its own; in fact it can incorporate knowledge of those filenames into its output quite usefully, so I would generally avoid redirects into grep when I could use it to explicitly, directly, open the files in question.

    • gorgoiler a day ago

      It’s shell specific though, just like using FOO=bar cmd prefixing to make environment changes. Better to use env(1) and keep everything rooted in Unix processes rather than shell syntax, imho.

        # bashisms
        <file tr z-o m-g | …
        FOO=bar python3 …
      
        # processes
        cat file | tr z-o m-g | …
        env FOO=bar python3 …
      • shiomiru 20 hours ago

        File redirection is specified in POSIX:

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

      • MathMonkeyMan 20 hours ago

        Both of the features that you mention are specified in POSIX, which means that bash, ksh, zsh, sh, ash, dash, etc. implement them.

        I find myself googling "OpenGroup shell command language" pretty often to check this sort of thing.

  • kelnos a day ago

    People who get up in arms about "useless" uses of cat need to chill out. I read my pipelines left to right. Using a '<' redirection to pull the data in after the command means I have to swap my reading direction back and forth, and why bother with that? I have better things to do with my brain energy.

    • bloppe a day ago

      As others have mentioned, you can put `<` anywhere in the command, including at the very beginning. But, I agree that getting up in arms about this is really dumb.

  • sim7c00 a day ago

    cat all the things. i cat | grep and im not ashamed of it! cat | less - why not.? its not going to melt my pc more or less if i cat | more | less...

    • Lockal 18 hours ago

      You may miss some optimizations for cat | tail. And you can't --follow a cat, obviously!

    • moffkalast a day ago

      It's the cat's meow!

  • js2 a day ago

    > Some people insist that we should never use a cat

    Rather, 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.

  • echoangle a day ago

    > Because for me it'd only be a local maxima.

    Nitpick: this should be „maximum“, maxima is the plural.

    • smitty1e a day ago

      Note the 'local'. If the output has several humps, multiple minima/maxima are possible.

      • zahlman a day ago

        Yes, but "it" and "a" in the original sentence are singular.

  • terminalbraid 2 days ago

    My main use of cat is for clipboard pasting (if I can't leverage pbpaste or something more appropriate). If I want to get something into a file or get into a pipe as stdin `cat > file` or `cat | cmd`, paste, then end with cmd-D to send an EOF and terminate the cat. For files it circumvents "helpful" formatting you would get pasting into an editor.

    • codesnik a day ago

      if you're on os x, you can also do

        pbpaste > file
      
      and if you're using zsh, just

        > file
      
      is sufficient, without cat
  • 9 hours ago
    [deleted]
  • xelxebar 18 hours ago

    Speaking of redirections... Here's another fut bit of lesser-known shell:

        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.
    • Brian_K_White 7 hours ago

      what is a use of that vs f () { cat file; }

      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.

      • xelxebar 26 minutes ago

        In this example? It's clearly useless and somewhat tongue-in-cheek. Redirecting stdout has some straightforward uses, e.g.

            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.
  • its-summertime a day ago

    The underlying topic for this is that, most shells treat files/redirections as part of the individual commands and not part of the pipelines. Files should be sometimes treated as commands instead, they plug into the fds of a process the same way as commands do.

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

    • rolfan a day ago

      From the file you linked, the built-in just copies the input files one by one, sequentially, into stdin. All done in chunks of 4096 bytes, a common page size.

      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.

  • fifilura a day ago

    "A la test driven development. "

    I would not call that test driven development (where are the testcases?)

    Rather more like REPL driven programming.

    • delichon a day ago

      Whether there are formal examples or not, the thing you're doing during REPL driven programming is testing. It's just rapid, iterative and informal.

      • bityard a day ago

        TDD refers to a specific kind of testing methodology, not just any and all testing. Manual testing is not TDD.

        • a day ago
          [deleted]
  • userbinator a day ago

    The logic of the author's argument seems rather convoluted. To me, cat or not is a simple decision: does the first useful command in the pipeline accept input from a file, or only stdin?

  • a day ago
    [deleted]
  • ttyprintk 2 days ago

    A better example is ‘pv -l’. Seeing cat in a heavily-used pipeline means pv is not available.

  • wwalexander a day ago

    You can still just begin with <somefile instead of cat somefile |. Does it really matter? No, but I don’t think pointing out useful and concise built-ins is necessarily asinine.

    • wwalexander 11 hours ago

      Something worth noting that I just noticed: on macOS (zsh), <somefile on its own seems to automatically page the output, whereas cat will simply print the entire file.

      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!

    • jchw a day ago

      Yeah, there are still some edge cases where cat is useful, but more often than not I just begin the redirection. I reckon some don't realize you can put it at the beginning, which does sometimes feel more ergonomic in a pipeline.

      • JoshTriplett a day ago

        It's not that it feels awkward to write the redirection first. It's that it feels awkward to write `< file cmd`, with no operator between the filename and the command, and the redirection operator not pointing at the command.

        • jchw 14 hours ago

          To me, the < and > redirections never point toward or away from the command. Rather, they point into or out of the file. And really that's because they do: like, >file is really 1>file, and <file is really 0<file. I think if your intuition on how redirections work is based on whether it points at the command it's worth rethinking how you look at redirections.

      • kortilla a day ago

        A pipeline without pipes connecting is just a field of shit

        • MathMonkeyMan 19 hours ago

          IO redirection doesn't construct a pipe, but piping does.

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

    • kelnos a day ago

      Can you? In bash I end up with no input at all if I start the line with '<somefile'.

      • xelxebar 19 hours ago

        Ahem, try this: <file cat

    • AStonesThrow a day ago

      No, in ordinary usage it doesn't matter, and it's not like I would go out of my way to change someone else's script code.

      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.

  • globular-toast a day ago

    cat concatenates files together. I often use it for that. Who cares if "files" is just one file? Using cat regardless keeps things uniform. I like it. People who get funny about it are just trying to be clever. Yes I know there are other ways to do it. But you understood what cat did, otherwise you wouldn't have commented on it.

    • Svoka a day ago

      literally what cat stands for. conCATenate.

  • PLenz a day ago

    I honestly use tac more then cat

    • yjftsjthsd-h a day ago

      I'm not sure I have ever used tac for real. Could you give an example?

      • sgarland a day ago

        When you want to tail a file, but you want it in descending order and you’re not sure how much of it you want:

            tac file | less
        • unnah 19 hours ago

          I'll just stick with tail -f until they implement tac -f

        • yjftsjthsd-h a day ago

          I guess the difference is that I expect things to read top to bottom, so I'd do that as

            less foo.log
            G
          • sgarland a day ago

            Fair enough, if that’s how you want it to read. less on its own is certainly quite capable.

      • chgs a day ago

        Reading a log file from most recent entry for example

        Cat *log|grep error|tac for example

        Tail -500 log | tac

    • shaftoe444 a day ago

      In my experience it's best to do

        cat file | tac
  • tptacek a day ago

    Does anyone take the "don't use cat" stuff seriously?

    • gorgoiler a day ago

      It’s a hump curve with virtue signalling in the middle, like a thousand other things in life.

      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.

    • Ferret7446 a day ago

      It's more of a symptom of someone who doesn't understand what's actually going on, i.e. cargo cult scripting.

      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

      • cybrox a day ago

        90% of the time, I care more about someone like that understanding my script than the tiny performance impact of an extra process or 'misusing' a function.

      • 20 hours ago
        [deleted]
    • jonjonsonjr a day ago

      I tend to agree with this sentiment but this year I came across two situations where the use of cat is actually harmful and not just useless: https://github.com/jonjohnsonjr/til/blob/main/post/readat.md...

      I still instinctively start my pipelines with cat half the time, but now I have complicated feelings about it.

    • prmoustache 20 hours ago

      Depends what you mean by seriously.

      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.

    • rzzzt a day ago
    • tux3 a day ago

      Well, this is the internet!

      We 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!

  • kreyenborgi 19 hours ago
  • cocodill a day ago

    and a cat is also good against mice

  • nullc a day ago

    I usually start with cat -- later the head doesn't just get replace the cat may too, e.g. replacing it with some command whose output was only temporarily stored in a file for development purposes.

  • smitty1e a day ago

    I'm a believer in the ditt-k-pow, DTTCPW, the Dumbest Thing That Could Possibly Work.

    What, exactly, are the arguments against `cat`?

    • cybrox a day ago

      The argument is that it's redundant because 'cat f | head' does the same as 'head f'

      I don't mind either way but I find starting with a cat and reading LTR to be very easy to understand.

  • x3n0ph3n3 a day ago

      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.
    • pbaam a day ago

      The cat command can be omitted there, as tee reads from standard input by default, even if stdin points to a terminal. I was going to comment an actually useful (and unavoidable in bash) use of cat and ssh, which is to do do nothing with standard input and redirect it to a 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.
      • x3n0ph3n3 a day ago

        I want do paste from my clipboard, not copy another file.

        • pbaam 19 hours ago

          I know, it's intentionally unrelated. But if you read my first sentence, you can do what you are interested in without using cat.

            sudo tee somefile > /dev/null
          
          And you will be able to paste from your clipboard or write anything you want. Without cat or piping.
  • lifeline82 a day ago

    [dead]

  • 18 hours ago
    [deleted]