[HN Gopher] Useful Uses of Cat
___________________________________________________________________
Useful Uses of Cat
Author : todsacerdoti
Score : 144 points
Date : 2024-02-21 18:53 UTC (4 hours ago)
(HTM) web link (two-wrongs.com)
(TXT) w3m dump (two-wrongs.com)
| gruturo wrote:
| The concept presented is something I can agree with in principle,
| but "transforming a filename into the content of the file" is a
| really thin justification for a responsibility.
|
| By all means don't build something where you have cascading
| effect and need to retest an entire pipeline, but this is _not_
| it.
|
| P.S.
|
| And if you really really want to keep it separate, just do "<
| access.log head -500 etc etc etc" (no I didn't forget a pipe. And
| yes the "< inputfile" works even if it's in front of what you're
| calling).
| hk__2 wrote:
| > And if you really really want to keep it separate, just do "<
| access.log head -500 etc etc etc" (no I didn't forget a pipe.
| And yes the "< inputfile" works even if it's in front of what
| you're calling).
|
| Or just use `cat` and let the pipe separate the different
| steps. "< access.log head" is nice but it breaks this
| representation where each step is piped into the next one.
| Sure, once you're done fiddling you can rewrite the thing to
| remove the "cat", but when you are constructing the thing I
| find it clearer to use cat.
| stouset wrote:
| > Sure, once you're done fiddling you can rewrite the thing
| to remove the "cat"
|
| Or just... don't?
| margalabargala wrote:
| Sure. Don't. Or do. Whatever you prefer.
| kristjansson wrote:
| > it breaks this representation where each step is piped into
| the next one
|
| No it doesn't? < access.log head -n 500 |
| grep mail | perl -e ...
|
| is completely valid, and reads right-to-left as well as the
| cat version. IMO using stdin is preferable to either solution
| in TFA.
| ufo wrote:
| This works but it's so ugly :(
| kristjansson wrote:
| I think it's beautiful? Reads left-to-right, has nice
| symmetry if you pipe to an output file like:
| < infile some_cmd > outfile
|
| and real clarifies what's a shell command, what's a
| redirection, etc.
| n_plus_1_acc wrote:
| I agree that it's more elegant, but it requires you to
| remember the operator precendence of < vs |
| kristjansson wrote:
| It's not really a precedence relation as much as it's
| part of shell syntax. There are separators[0], and
| anything sequence of tokens between a separator is a
| command[1]. A command decomposes further, of course, but
| the only thing the programmer needs to know here is that
| separators are a different kind of thing than commands.
|
| [0]: newline, '||', '&&', '&', ';', ';;', ';&', ';;&',
| '|', '|&', '(', or ')'.
|
| [1]: https://www.gnu.org/software/bash/manual/bash.html#S
| imple-Co...
| TOGoS wrote:
| > _transforming a filename into the content of the file " is a
| really thin justification for a responsibility_
|
| This is one of those things where I think it is until it isn't.
|
| I sometimes second-guess myself when I think I might be over-
| single-responsibilifying. "Well in practice these two things
| are so trivial that this feels a little silly."
|
| It often turns out to have been a good call in hindsight,
| especially when working with other people who aren't
| necessarily thinking about these things at all. If the
| responsibilities have been sufficiently split up, they're more
| likely to change only the part that needed to be changed, and
| less likely to complectify the two things together that really
| shouldn't've been. Or when I go "oh wow that thing that I
| thought I overly-abstracted sure composes well with this
| unexpected new thing!"
|
| Hardcore separation of concerns is just another method of
| defensive programming.
|
| > _< access.log head -500 etc etc etc_
|
| It's too bad that the syntax is so different. Why does the
| first stage not end with "|"? There's space for shell syntax
| improvements, here. Maybe a 'cat'-like builtin that translates
| `cat foo | bar` into `bar <foo` so you can have the nice syntax
| but don't needlessly create processes would leave everyone
| happy.
| ajross wrote:
| > "transforming a filename into the content of the file" is a
| really thin justification for a responsibility.
|
| Uh... I dunno, but my lizard brain thinks that the whole idea
| of mediating filesystem operations on storage and IPC
| mechanisms like pipes is a lot _more_ complicated, magic, and
| deserving of a single command than merely filtering the data on
| stdin.
|
| I agree with the article and the logic, and think this historic
| meme was basically wrong originally. You string up your chain
| of pipelines with the first element being "where does it come
| from?" and not merely whatever the first operation happens to
| be just because that operation allows for some kind of file
| input or redirection syntactically.
| jiveturkey wrote:
| in the example given in the website, it could all be done in
| the perl! no need for the pipeline at all.
| svilen_dobrev wrote:
| well, whole thing can be written in the perl -ne ...
|
| but that absolutely kills modularity / composability
|
| so yes, "cat xyz" plays a source - and can be replaced with
| another source - without touching all other stuff.
| hk__2 wrote:
| > so yes, "cat xyz" plays a source - and can be replaced with
| another source - without touching all other stuff.
|
| This. I often have to read logs and being able to apply the
| same oneliner on older logs just by adding a "z" in front of
| "cat" to read .log.gz instead of .log files is extremely
| useful.
| kqr wrote:
| Good point! I often experiment with cat response.json and
| then replace cat with curl once I'm happy.
| lenerdenator wrote:
| came looking for feline-related content. left disappointed.
| lonlazarus wrote:
| Try tumblr
| whatsinaname123 wrote:
| Or reddit
| p1mrx wrote:
| Useful uses of cat:
|
| - pest control
|
| - entertainment
|
| - transporting small solar arrays
| omoikane wrote:
| > transporting small solar arrays
|
| I definitely need to hear more about this one.
| lenerdenator wrote:
| solar panel need sun.
|
| cat like sleeping in sun.
|
| attach solar panel to cat.
|
| cat occasionally moves to remain in sun as it naps.
| klyrs wrote:
| My cat recently discovered that her fur can absorb a toilet
| bowl full of water. This seems like it could be useful, if
| you need a very wet floor immediately.
| brightsize wrote:
| Here you go. This is the second time in the past few years that
| cat was used to process bikers in the NW WA wilderness. The
| first time was fatal. I used to live about 15min from where
| this incident occurred. https://www.seattletimes.com/seattle-
| news/cougar-attacks-cyc...
| xanderlewis wrote:
| Those looking for useful uses of the category of (locally
| small) categories will similarly be disappointed.
| grork wrote:
| Especially since the versatility of A Cat is unbounded.
| slingnow wrote:
| I'm sure this generic "joke" would be a real gut buster over on
| Reddit.
| lenerdenator wrote:
| As a cat servant I care not about gut-busting.
| Findecanor wrote:
| And here I had been hoping on tips and tricks for printing the
| file catalog on TRSDOS, Flex, Sinclair or RISC OS.
| glxxyz wrote:
| I was half-expecting it to be about this strange book:
| https://en.wikipedia.org/wiki/101_Uses_for_a_Dead_Cat
| kkfx wrote:
| an useful use, back in time, was to assemble partial ISO file of
| OpenSolaris SXDE, the first open version before Ian
| Murdock/OpenIndiana. It's more an anecdote but considering the
| usefulness of netcat in many cases such "cat binary use" might
| still be relevant in some embedded scenarios.
| gregfjohnson wrote:
| Thoughtful article - thanks. Humorous aside: I recently came
| across a hilariously named, occasionally useful, related utility:
| "tac".
| ryukoposting wrote:
| It ships with coreutils! `rev` is another one in a similar
| vein, though that's not coreutils.
| hiAndrewQuinn wrote:
| `cat x | Y | Z` is Subject-Verb-Object.
|
| `Y x | Z` is Verb-Subject-Object.
|
| That's why I prefer using cat "uselessly".
| kazinator wrote:
| < x Y | Z > w
|
| Y takes input redirected from x, piped into Z, which outputs
| into w.
| TeMPOraL wrote:
| > _Y takes input redirected from x, piped into Z, which
| outputs into w._
|
| I.e. x | Z | tee w | Y
|
| ? that's... something else entirely.
| _kst_ wrote:
| No, the command is < x Y | Z > w
|
| where x and w are files, not commands.
|
| Something that "cat file | ..." advocates might be
| overlooking is that a redirection ("<inputfile",
| ">outputfile", "2>errorfile") can appear anywhere within a
| simple command, so these: command -option
| < file < file command -option command <
| file -option
|
| are all exactly the same -- and of course very similar to
| cat file | command -option
|
| If the purpose of typing "cat file | command" is to put the
| input file at the beginning (which does make logical
| sense), you can achieve the same thing with "< file
| command". Admittedly, it does look at bit strange if you're
| not accustomed to it. (It even works with csh and tcsh.)
| neuromanser wrote:
| cat is a verb though
| pwdisswordfishc wrote:
| "x" is not a subject.
| kazinator wrote:
| This blogger does not seem to know that in the POSIX shell
| syntax, redirections can be specified anywhere in the command:
| < access.log head -n 500 | grep mail | perl -e
|
| Now you can delete "head -n 500 |".
|
| > _If we then delete only the head processing step we're left
| without a step that transforms the string access.log into the
| contents of the access log._
|
| By introducing "cat access.log" we have the same problem: if we
| delete only the cat processing step, we're left without a step
| that transforms the string access.log into the contents. For the
| useless cat to have the nice property that you can cleanly delete
| it from the command line, you need: < access.log
| | cat | head -n 500 ...
|
| :)
| jxy wrote:
| I'm in the camp of using <input X|Y|Z >output
|
| The point of this syntax is that I can readily replace it with
| F() { X|Y|Z } <input F >output
| iforgotpassword wrote:
| Since I like spaces around stuff I started putting two spaces
| after the infile at some point. < infile x | y
| | z > outfile
|
| I just didn't like how the filename was so close to the command
| name ;)
| CBLT wrote:
| I'm in this camp as well, starting with input file redirection
| just makes so much sense to me.
| </proc/0/environ xargs -0
|
| I also don't tend to want enormous volumes of text in my
| terminal scrollback so I generally view files or pipe verbose
| commands to `less`, then when I find what I want to send to the
| terminal I use the `|` less command to pipe it to `cat`.
|
| Or to grab just a few lines for my later reference:
| kubectl get po/my-pod -o yaml | less /* find the lines
| I'm interest in */ -N |^sed -n 34,35p
| TehShrike wrote:
| Typo in the title - should be "cat" not "Cat" (same as the h1 in
| the linked article)
| ufo wrote:
| If the shell were to treat cat as a builtin, could it implement
| cat| as a stdin redirect?
|
| I wish I could write "useless cat" without people pestering me
| about it.
| kazinator wrote:
| Bash allows the $(< file) syntax instead of $(cat file), and I
| think the latter might be converted to the former.
| neuromanser wrote:
| It's built-in in Zsh: The substitution '$(cat foo)' may be
| replaced by the faster '$(<foo)'.
|
| https://zsh.sourceforge.io/Doc/Release/Expansion.html#Comman.
| ..
| NelsonMinar wrote:
| Another reason to do `cat | foo` is because you can then quickly
| change it to `zcat | foo` or some other source for the data by
| editing the very beginning of the command.
| pimlottc wrote:
| less is useful for this if you have the lesspipe [0]
| preprocessor installed
|
| 0: https://github.com/wofr06/lesspipe
| avgcorrection wrote:
| Most immediate use: I have to google less shell syntax.
| bee_rider wrote:
| The reason these sequences of commands always start with cat, for
| me at least, is that I just cat'd the file only to find it was
| too long or noisy. cat filename.txt
| Up | grep "thing I want"
|
| Is fewer keystrokes than cat filename.txt
| grep "thing I want" filename.txt
|
| Or more likely cat filename.txt
| grep filename.txt "thing I want" grep "thing I
| want" filename.txt
| hnlmorg wrote:
| You can also do: grep "thing I want" !$
|
| Bash (and similar) will replace !$ with the last parameter of
| the previous command.
|
| This is a trick I've used lots when wanting to perform a non-
| piping operation on a command I've 'cat'ed (eg 'rm -v !$')
|
| I'd never criticise anyone for "useless" use of 'cat' though.
| If the fork() overhead was really that critical then it
| wouldn't be a shell command to begin with.
| mmh0000 wrote:
| Even easier, press alt+. (fewer keystrokes too! cli golf is
| fun), it'll copy-and-paste the last parameter from the
| previous command. If you press . multiple times it'll go
| further back into your history.
| bumblebeast wrote:
| I just use <esc>. On the command line to bring back the last
| argument.
|
| Then I can look at it before hitting return
| harrisi wrote:
| shopt -s histverify
| sophacles wrote:
| I look through a lot of logs, so I've aliased `not` to `grep
| -vE` and using the method you describe over several iterations
| i end up with a history that has a lot of cat
| log | not spam1 | not spam2 | not 'spam(3|4)' | .... | less
| kqr wrote:
| I really like that alias. Thanks!
| mmh0000 wrote:
| I've been using Linux for a very long time and never thought
| of that as an alias. I love it! Thank you for sharing.
| lambdaba wrote:
| Zsh global aliases can be nice for commands commonly used in
| pipelines: alias -g V='| grep -vE'
| alias -g L="| $PAGER"
|
| Then you can do cat log V spam1 V spam2 L
|
| I also like alias -g G='| grep' alias
| -g X='| xargs'
|
| The possibilities are endless...
| TylerE wrote:
| So are the injection possibilities. That makes me very
| nervous.
| lambdaba wrote:
| What's the scenario?
| TylerE wrote:
| Something sets $PAGER to "rm -rf /" or something along
| those lines.
|
| Really I'd be more worried about _accidental_
| invocations. Aliases are not scoped, so if you're dealing
| with one of those programs with non standards option
| handling and uppercase single character switches...
| lambdaba wrote:
| I actually use `alias -g L='| nvimpager'`,
|
| As for accidental invocations, yeah I agree it feels
| dangerous, but in a few hundred thousand lines of shell
| history since I set them, never had a problem.
| TylerE wrote:
| Haven't had a problem, or haven't noticed you've had a
| problem? I kid - but only partially.
|
| What I really wish for is some sort of tool that would
| let me pipeline like that, but also easily examine each
| step in the chain for sanity. Sort of a workbook for
| shell.
| lambdaba wrote:
| Auto-expanding aliases seems like it would help:
| function expand-alias() { zle _expand_alias
| zle self-insert } zle -N expand-alias
| bindkey -M main ' ' expand-alias
| deathanatos wrote:
| I've always though UUoC while working _interactively_ are fine.
| One shouldn 't interrupt their flow to fix minutia in a one
| time command.
|
| UUoC criticism, to me, belongs when one sits down to script.
| 0cf8612b2e1e wrote:
| Eventually I will want to debug/change the script where we
| get right back to the situation where I want cat at the front
| of the line.
| neuromanser wrote:
| Press alt and dot (full stop) to insert last word from the
| previous command line: $ cat file $
| grep stuff alt-.
|
| Alternatively, make use off the READNULLCMD mechanism in Zsh:
| $ < file
|
| translates to $ ${READNULLCMD:-more} < file
|
| Thus you can $ < file
|
| then UP (or ctrl-p which I find more ergonomic) and continue
| with "grep stuff": $ < file grep stuff
|
| (Redirections can be anywhere in the command.)
|
| https://zsh.sourceforge.io/Doc/Release/Redirection.html#Redi...
| hawski wrote:
| Try: < file.txt grep pattern
|
| Less keystrokes.
| matvore wrote:
| If we're going to talk about unnecessary extra processes like
| useless cat, we should merge the head and grep commands into a
| sed, and possibly just merge everything into perl:
| <access.log sed -n '/mail/p; 500q' | perl -e ...
|
| If perl is processing the file line-by-line then filtering lines
| by regex and stopping at line X is trivial, and you don't even
| need sed.
| jkingsman wrote:
| I think your point is valid and I don't dispute it, but if I
| had a nickel for every time I said "just do it all in perl" and
| regretted it, I'd be... well, perhaps not a rich man, but I'd
| have lunch covered for a few weeks.
| matvore wrote:
| Perl has the advantage of only having one implementation,
| unlike sed and grep (e.g. BSD or GNU) and /bin/sh (can be one
| of many POSIX shells), so upgrading this pipeline to 100%
| perl is safer in some respects. The example in the article is
| light on details so it's hard to comment very deeply.
|
| I have heard snarky Perl putdowns ad nauseam at work and on
| HN and may have regretted using it a handful of times but I
| can say worse or similar for other popular tools,
| languages...
| b5n wrote:
| I usually do it all in awk: awk '/mail/ && NR
| <= 500 {...}' access.log
|
| If you want N matched lines: awk '/mail/ && i
| < 500 {i++; ...}' access.log
| matvore wrote:
| I wish I knew awk as well as I know perl, since then I
| wouldn't need to hear recommendations for CPAN modules and
| spurious style prescriptions.
| txutxu wrote:
| After read the article... all the reasoning... I think I could go
| with this for such reasons exposed: perl -ne
| 'last if $. > 500; /mail/ && print' access.log
|
| When you're done with the part after the &&, remove the 'last if
| $. > 500'
|
| For me, the most useful use of (gnu) cat, is cat -A weird.file
|
| It saves my day or solves weird issues (X-files) with files
| generated by (not so) junior sysadmins, copy/pastes, end of
| lines, invisible diffs, etc... many times each year.
| cgs1019 wrote:
| `cat -n` prints with line numbers, which is occasionally handy.
| JTbane wrote:
| nl?
| nonethewiser wrote:
| I like this sort of blog post - simple, clear example in a short
| format. Of course we can nitpick the implementation but I think
| the principle is solid: don't be afraid to sacrifice some brevity
| for modularity or composability.
|
| I do think he could have woven in the Parnas source a bit better.
| The first quote seems to rely a lot on the surrounding context of
| the essay and its never contextualized. This bit:
|
| > The problem is that these subsets and extensions are not the
| programs that we would have designed if we had set out to design
| just that product.
|
| Just feels a bit disjointed in the article. I get the high-level
| message but thought it could be woven into the blog authors
| narrative better. Anyways, it sounds like a good article though.
| kqr wrote:
| Author here. You're right. Originally I had a much longer
| excerpt but it got a bit rambly. I overcorrected and cut out
| too much; I'll see what I can do to fix it. Thanks!
| cljacoby wrote:
| I also use cat this way, and for me the biggest reason is it just
| allows for a more intuitive left-to-right reading of any
| pipeline.
|
| Things like this:
|
| head -n 500 access.log | grep ...
|
| head -n 500 <access.log | grep ...
|
| Feel like you start with the filename, then go leftwards to the
| first operation, then start reading rightward again through the
| pipe. At least in my brain, it feels slightly more awkward.
| lubutu wrote:
| You can do... <access.log head -n 500 | grep
| ...
|
| ... though that's less familiar to many, I'm sure.
| cljacoby wrote:
| Neat, I didn't realize you could order it like that.
|
| I'm realizing now another (and potentially stronger
| influence) is just years of muscle memory starting pipelines
| with cat.
| didntcheck wrote:
| I wish math notation and computer programming had just settled
| on postfix over prefix early on, it's so much more natural to
| read. Of course, we kind of get it with object oriented
| programming, some languages have UFCS [1], F# has that pipe
| operator etc.
|
| It's funny, when learning programming, I think Haskell was the
| language that introduced me to the pattern of having a chain of
| operators processing a stream to build up a result (and I'd
| later cover it again in SICP), and I loved how clean it looked
| compared to imperative code. But I now find it one of the
| harder to read languages due to it all being prefix, whereas
| Java/Kotlin/C#/Javascript now all have stream constructs that
| use method calls, so read left-to-right, source-to-sink
|
| And I'm reminded that I need to give Forth a proper go sometime
|
| [1] https://en.wikipedia.org/wiki/Uniform_Function_Call_Syntax
| az09mugen wrote:
| Among the uses of cat, I saw no one mention you can write or
| append text to a file, like this : cat >
| file.txt # after this command, just type your text
|
| Or to append : cat >> file.txt
|
| Can also work with Here Doc syntax : cat >
| file.txt << EOF This is the text to write down
| EOF
| jl6 wrote:
| If only output redirection syntax was w> instead of >w. Then you
| could write pipelines with compelling HTML-esque symmetry:
|
| <x Y | Z w>
| badrabbit wrote:
| cat is cool but I also like it's sibling tac, I use it quite a
| bit.
|
| It's also possible to make a much faster cat (I have, considered
| naming it cheetah),10%+ faster.
| renewiltord wrote:
| Honestly, I've being UUOC all my life and everything is fine.
| Never run into a problem. I mostly use `zsh` and `< file | cmd`
| works easily even when you remove the `cmd` so that's fine. But
| honestly, it's both bought and lost me nothing, which means I
| shouldn't care about it.
|
| What I do like is doing something like: diff
| -aui <(xxd binary1) <(xxd binary2)
| charcircuit wrote:
| I don't agree with this argument. You can make the same argument
| against cat
|
| >cat access.log | head -n 500 | grep mail | perl -e ...
|
| >we find that cat performs two responsibilities:
|
| >1. Printing an error to stderr if the file doesn't exist
|
| >2. Copying a file to stdout
| TacticalCoder wrote:
| What's really useless are all the "useless use of cat" comments
| (and shellcheck warnings although I take it for a shell script
| there could be cases where one less process may be justified
| [although really if you're at that point you've got other things
| to worry than cat, sadly]).
|
| I use "cat ... | ... | ... " like in TFA and just like many in
| this thread because it simply makes sense. It's more intuitive.
| It's easier to read. It requires less braincycles to remember how
| this or that command wants its parameter passed, etc.
|
| I think the "useless use of cat" movement made its time: it
| failed. Many of us are never going to give up our use(less|ful)
| of cat (you decide). So stop wasting your time complaining about
| it.
| psim1 wrote:
| $ killall cat | cat | cat | cat | cat | cat
|
| Terminated: 15
|
| Which cat died first?
| yesiamyourdad wrote:
| I can't be the only one who clicked on this hoping for an
| explanation of why they owned a cat.
| VSpike wrote:
| I definitely think if you want to use `cat` then just go ahead,
| it's fine. Sometimes these things are a power play, a way to
| distinguish between people who know the social codes and those
| who don't. In this case, it probably had a reasonable origin even
| if it's now more of a way to beat on newcomers. On old systems,
| memory was limited, disk was slow and forking was expensive.
| Saving a process in a script or one liner was a noticeable
| improvement performance-wise.
|
| I learned some bash from an old-timer who would write an
| infinite-loop like this: while :; do #
| loop body here done
|
| This works because the `:` is a way to set a label, and it
| implicitly returns 0. It's just a weird wrinkle of the language.
| So, why not do `while true`? On old systems, `true` was not a
| builtin and would call `/usr/bin/true`. Writing the loop this way
| saves a process fork on each iteration.
|
| On a modern system, you'd be hard pushed to measure the
| difference, so it really doesn't matter which style you prefer.
___________________________________________________________________
(page generated 2024-02-21 23:00 UTC)