关于“无用的猫”的一般共识是什么?


39

当我管道多个unix命令,如grep,sed,tr等时,我倾向于使用cat指定正在处理的输入文件。所以像cat file | grep ... | awk ... | sed ...

但是最近在我的答案留下了几条评论,表明这是对猫的无用之后,我想我会问这里的问题。

我查阅了这个问题,并且看到了维基百科关于UUOC无用猫奖的文章,在我看来,所提出的论点都是从效率的角度出发的。

我在这里遇到的最接近的问题就是这个问题:给猫打电话是否浪费? - 但这不是我要问的。

我猜UUOC阵营的建议是使用cmd1 args < file | cmd2 args | cmd3 ..或者命令是否有选项从文件读取然后传入文件作为参数。

但对我cat file | cmd1 ... | cmd2来说,阅读和理解似乎更容易。我不必记住将输入文件发送到不同命令的不同方式,并且该过程从左向右逻辑流动。首先输入,然后是第一个进程......依此类推。

我无法理解对猫的无用使用有何争论?我明白,如果我正在运行一个每2秒运行一次并执行大量处理的cron作业,那么在这种情况下,cat可能会浪费。但是否则对使用猫的一般共识是什么?


14
我同意,在这里,对cat的调用可能效率低下,但它使得命令更容易理解和编辑更晚,并且(重要的是,IMO)将每个不同的命令分离到只有一个工作,使整个事情更容易处理用。
Phoshi

3
普遍的共识是没有达成共识。
jwg 2015年

2
这在很大程度上重复了stackoverflow.com/questions/11710552/useless-use-of-cat(尽管它早于它)。
Tripleee

1
不要忘记它< file cmd1 args | cmd2 args ...也有效......所以你从“ 从左到右 ”的论点是无效的。为了清楚起见,我经常使用它 - 我展示的顺序会导致人们暂停,这并不好。随着更高的线程数量成为常态,这变得不那么成为IMO的问题......
Attie

Answers:


21

在某种意义上说,使用它并没有完成任何其他任何事情,可能更有效的选择不能(即产生适当的结果),这是无用的。

但是cat比正义更强大cat somefile。咨询man cat或阅读我在这个答案中写的内容。但是如果你绝对肯定只需要单个文件的内容,那么你可能会因为不使用cat文件内容而获得一些性能优势。

关于可读性,这取决于您的个人品味。cat出于同样的原因,我喜欢将文件放入其他命令,特别是如果性能方面可以忽略不计。

它还取决于你的脚本。如果它是您自己的桌面机器的外壳和便利方法,除了您之外没有人会关心。如果您偶然发现链中的下一个工具最好能够寻找,并将其作为经常使用的软件分发到低性能路由器或类似设备上的一些最小Linux系统上,并且具有实际限制处理能力,这是不同的。它总是取决于上下文。


1
性能成本是否可以忽略不计?在许多情况下,它们是:oletange.blogspot.dk/2013/10/useless-use-of-cat.html
Ole Tange

15

在每天的命令行使用它并没有太大的不同。你特别不会注意到任何速度差异,因为不使用CPU避免了时间cat,你的CPU只是空闲。即使您在所有实际情况中循环了数百或数千(甚至数十万)个项目,它也不会产生太大的影响,除非您使用的是非常负载的系统(Load Average / N CPU> 1)。

橡胶与道路交汇的地方就是要养成良好的习惯,不要让不好的习惯变得沮丧。为了拖出一个发霉的陈词滥调,魔鬼在细节中。这样的细节将平庸与伟大的分开。

这就像驾驶汽车时,为什么在你只能做三个权利时左转呢?当然你可以,它完美无缺。但如果你理解了左转的力量,那么三种权利似乎很愚蠢。

这不是关于保存一个文件句柄,17k的RAM和0.004秒的CPU时间。这是关于使用UNIX的整个理念。在我的插图中,“左转的力量”不仅仅是重定向输入,而是UNIX哲学。完全了解这将使你比周围的人更好,并且你会从那些明白的人那里获得尊重。


如果您正考虑在没有红绿灯的情况下左转进入6车道繁忙的高速公路,那么您可能需要考虑向右转,或采取不同的路线。* nix为您提供多种路线选择。这是个人偏好和可读性的问题。如果你想“cat file | cmd1 | cat | cmd2 | more”,请继续。(如果cmd1分页,有时候这很有用 - 猫会消除它。)$ CPU时间<< $脑时间。
MikeP 2016年

1
@MikeP将cat不会消除任何分页,虽然管道到的东西可能会被某些应用程序消除分页。
Tripleee

12

我经常cat file | myprogram在例子中使用。有时我被指控无用猫(http://www.iki.fi/era/unix/award.html)。我不同意以下原因:

  • 很容易理解发生了什么。

    在读取UNIX命令时,您需要一个命令,后跟参数,然后是重定向。这可能把重定向任何地方,但它是罕见-这样人们就会有一个更难的时间阅读的例子。我相信

    cat foo | program1 -o option -b option | program2
    

    比阅读更容易阅读

    program1 -o option -b option < foo | program2
    

    如果将重定向移至开头,则会让那些不熟悉此语法的人感到困惑:

    < foo program1 -o option -b option | program2
    

    示例应该易于理解。

  • 它很容易改变。

    如果您知道该程序可以从cat读取,您通常可以假设它可以读取输出到STDOUT的任何程序的输出,因此您可以根据自己的需要调整它并获得可预测的结果。

  • 它强调如果STDIN不是文件,程序不会失败。

    假设如果program1 < foo工作那么cat foo | program1也是有效的是不安全的。但是,它在实践中安全的假设相反。如果STDIN是文件,则此程序有效,但如果输入是管道,则该程序失败,因为它使用seek:

    # works
    < foo perl -e 'seek(STDIN,1,1) || die;print <STDIN>'
    
    # fails
    cat foo | perl -e 'seek(STDIN,1,1) || die;print <STDIN>'
    

我已经查看了http://oletange.blogspot.dk/2013/10/useless-use-of-cat.html上的性能损失。cat file |如果处理的复杂性类似于简单的grep,则结论是不使用的而且性能比可读性更重要。对于其他情况cat file |很好。


11

我认为一些评论UUOC的人所采取的立场是,如果一个人真正了解Unix和shell语法,那么就不会在那种情况下使用cat。它被看作是使用不好的语法:我可以用不好的语法写一个句子,但仍然得到了我的观点,但我也证明了我对语言的不良理解,以及我的教育状况不佳。所以说某些东西是UUOC是说某人不理解他们正在做什么的另一种方式。

就效率而言,如果从命令行执行管道,则机器执行所需的时间cat somefile |比考虑使用它是否更有效的时间要短< somefile。这没关系。


6
很长一段时间我都知道还有其他方法可以cat somefile | prog在没有猫的情况下在shell中表达,prog < somefile但是它们似乎对我来说总是错误的顺序,特别是在一起连接命令链的情况下。现在我看到一些优雅的东西< somefile prog,谢谢你。我已经没有使用猫的借口了。
亚历克斯

4

直到今天,我还没有意识到这个奖项,当时有些菜鸟试图将UUOC钉在我身上,以获得我的一个答案。这是一个cat file.txt | grep foo | cut ... | cut ...。我给了他一个想法,只有在这样做之后,我才能看到他给我的链接,指的是奖项的起源和这样做的实践。进一步的搜索引导我这个问题。有点不幸的是,尽管有意识地考虑,但没有一个答案包括我的理由。

在教育他时,我并不打算采取防御措施。毕竟,在我年轻的时候,我会编写命令,grep foo file.txt | cut ... | cut ...因为无论何时你经常使用单个greps,你都会学习文件参数的位置,并且知道第一个是模式,后者是文件名。

当我用cat前缀回答问题时,这是一个有意识的选择,部分是因为“良好品味”的原因(用Linus Torvalds的话说),但主要是出于功能的令人信服的理由。

后一个原因更重要,所以我会先说出来。当我提供管道作为解决方案时,我希望它可以重复使用。管道很可能会在最后添加或拼接到另一个管道中。在这种情况下,如果文件参数存在,那么grep的文件参数会破坏可重用性,并且很可能在没有错误消息的情况下静默执行。I. e。grep foo xyz | grep bar xyz | wc会给你多少行中xyz含有bar,而你期待同时包含的行数foobar。在使用之前必须在管道中更改参数的参数很容易出错。除此之外,它还存在无声失败的可能性,并成为一种特别阴险的做法。

前一个原因并不重要,因为许多“好品味”仅仅是一种直觉的潜意识理论,就像上面的沉默失败一样,当一些需要教育的人说“但不是那只猫无用“。

但是,我会尽量让我意识到前面提到的“好味道”的原因。这个原因与Unix的正交设计精神有关。grep没有cutls没有grep。因此至少grep foo file1 file2 file3违背了设计精神。正确的做法是cat file1 file2 file3 | grep foo。现在,grep foo file1仅仅是一个特殊情况grep foo file1 file2 file3,如果你不同心对待它,你至少会耗尽脑时钟周期试图避免无用的猫奖励。

这导致了我们连接的论证grep foo file1 file2 file3,并且cat连接所以它是正确的,cat file1 file2 file3但因为cat没有连接cat file1 | grep foo因此我们违反cat了全能的Unix 的精神。那么,如果是这种情况那么Unix就需要一个不同的命令来读取一个文件的输出并将其吐出到stdout(不是将它分页或只是纯粹的spit到stdout)。所以你会遇到你说cat file1 file2或你说的情况dog file1并且认真记得避免cat file1避免获得奖励,同时也避免dog file1 file2因为dog如果指定了多个文件,设计将会引发错误。

希望在这一点上你同情Unix设计者不包括一个单独的命令将文件吐出到stdout,同时也命名cat连接而不是给它一些其他名称。<edit>有这样一只狗,不幸的<经营者。不幸的是,它放置在管道末端阻止了易于组合。在开始时没有语法或美学上清洁的方式。不幸的是,不够通用,所以你从狗开始,但只是添加另一个文件名,如果你也希望它在前一个之后处理。(>另一方面,它不是坏的一半。它在最后几乎是完美的位置。它通常不是管道的可重复使用的部分,因此它具有象征性的区别。)</edit>

接下来的问题是,为什么在没有任何进一步处理的情况下将命令仅仅吐出文件或将多个文件串联到stdout很重要?一个原因是避免让每个运行在标准输入上的Unix命令知道如何解析至少一个命令行文件参数,并将其用作输入(如果存在)。第二个原因是避免用户必须记住:(a)文件名参数的去向; (b)避免如上所述的无声管道错误。

这让我们知道为什么grep有额外的逻辑。其基本原理是允许用户流畅地使用频繁且独立使用的命令(而不是作为管道)。对于可用性的显着增加,这是对正交性的轻微折衷。并非所有命令都应该以这种方式设计,并且不经常使用的命令应该完全避免文件参数的额外逻辑(记住额外的逻辑会导致不必要的脆弱性(bug的可能性))。例外情况是允许文件参数,例如grep。(顺便说一下,ls有一个完全不同的理由不仅接受,而且几乎需要文件参数)

最后,可以做得更好的是,如果标准输入可用,如果grep(但不一定ls)这样的例外命令会产生错误。这是合理的,因为命令包含违反全能Unix的正交精神的逻辑,以方便用户。为了进一步方便用户,即为了防止由无声故障引起的痛苦,这些命令应该通过警告用户是否存在无声故障的可能性来毫不犹豫地违反他们自己的违规行为。


1
正如在这个答案和问题的跨站点副本上所讨论的那样,grep pattern f1 f2 f3并不是简单的连接grep了解文件,并打印文件名(以及可选的行号,以及其他任何内容)。 grep . /sys/kernel/mm/transparent_hugepage/*是一个很好的黑客打印文件名:文件内容与许多单行文件。经典的Unix设计是大多数实用程序在*.txt不需要的情况下工作catcat用于将多个文件展平为一个流。
Peter Cordes

@PeterCordes我没有写这么多关于grep的文章。我观察到有关错误/复制粘贴的稳健性的实质性问题; 正确与性能,你方便地选择忽略,以支持一些小/外围的关注。
randomstring 2002年

您确实提出了一些有趣且有效的要点,尤其是关于复制/粘贴管道时可能出现的错误。我建议grep用一个cut没有任何理由关心多个文件的程序替换你的例子,并且总是可以从它的stdin中获取。有些实用程序,tr根本不接受文件args,只能作为过滤器使用,所以选择在cat和之间<
Peter Cordes

1
你的帖子我最大的问题是你打折输入重定向。 <file cmd1 | cmd2 >out我承认,这不是很好,但完全有可能习惯它。你继续以嘲弄的方式继续谈论“全能的Unix的精神”,这对我来说完全不合适,因为听起来你要么不想要或者不想得到Unix设计师真正想到的方式。如果你不喜欢Unix设计,这很好,但它本身并不愚蠢。我不确定操作系统的设计是否早于shell语法,以及它是如何演变的,但cat1970年的额外值得避免!
Peter Cordes

1
@PeterCordes事后来说,我的答案相当啰嗦,这有损于最重要的一点 - 首先是正确性,第二是优化。额外的cat帮助重用和拼接管道,而没有它你可以得到沉默的失败(在我的答案中搜索“默默地”)。
randomstring

2

为无用的猫的使用辩护

(一些段落有助于平衡海啸对这种做法的唠叨评论)

多年来我一直在使用bash作为shell和小脚本的脚本语言(对于不那么小的脚本而言,有时令人遗憾)。很久以前,我已经了解了“无用的猫”(UUoC)。我至少每周都会对此感到内疚,但坦率地说,我甚至很少想要避免它。我认为使用catvs < file更多的是关于品味而不是技术差异,我写了这个答案来保护那些与我有共同兴趣的Linux新手cat认为他们的方式存在严重错误(并注意到有几次存在的情况)。像Linus Torvalds一样,我也相信经常品尝比技巧更重要。这并不意味着我的口味比你的好,但它确实意味着,如果某些东西味道不好,我不会在没有获得有价值的东西的情况下做到这一点。

很明显,像问题作者一样,我觉得在使用像bash这样的REPL时,使用cat非常自然,我正在通过逐步构建复杂命令来探索问题。这是一个非常典型的例子:我有一个文本文件,并不太了解它。我会打字cat file来品尝一下内容。如果输出实在是太多了,我会打我的向上箭头,并根据具体情况,我将添加| head| grep foo| what_ever通过附加的处理步骤延长我以前的命令。通过在另一个之后添加一个处理步骤逐渐从一个简单的命令转变为一个更复杂的命令对我来说非常自然(我在ipython上做同样的事情,我喜欢这种方式)pyfunctional和类似的编程工具包含这种风格)。因此,在bash shell中工作时, “M信心,打断我的流程去除cat再多也没用不是让它成为受苦......以及没有结果,在所有的情况下,99.9%。

当然,在编写脚本时,事情可能会改变。但即使在编写脚本时,我认为嘲笑UUoC的人会忽视这一重要课程:“过早优化是万恶之源”。如果你没有做非典型的事情,那么UUoC很难成为需要优化的地方。当然,你肯定需要知道什么是低效的(这是额外的进程调用BTW,因为很少有人提到它)。拥有这些知识,如果您碰巧在那些调用流程的稀有系统(例如某些嵌入式系统或CygWin在较小程度上)工作,您将知道如果特殊情况需要它该怎么做。例如,如果你发现自己在打电话cat循环中多次一秒钟(顺便说一句,如果你发现自己处于那个位置,问问自己bash是否适合这项工作)。尽管如此:“首先使其正常工作然后在必要时进行优化”

你怎么解释有关UUoC尼克的抱怨海啸?

除了不是每个人都有我的品味之外,我相信很多人抱怨UUoC的主要原因不是技术而是人类:大多数Unix新人都不会知道这个< file command成语,因此很有经验的人玩“老大师”很有吸引力给他们。他还将有机会使用花哨的词语(“过程调用”)并触及“优化”的亲爱主题。保证良好的印象,因此很难抗拒。然后,新人将以面值的形式接受大师的建议,并且很长一段时间将其作为“唯一真理”重播给其他人(并且将这个答案向下投票:-)。有趣的是:它可能很容易修复bash以避免UUoC的任何低效率,人们不禁要问为什么没有人添加此功能或制作< filename经过这么多年的努力。一个黑暗的灵魂会暗示一些灰胡子猛击黑客喜欢留下嘲笑我们的机会;-)


+1爱最后一段关于为什么人类学因素传播这种做法:-)
randomstring

1

真正好看的是支持语法的shell:

< filename cmd | cmd2 cmd2arg1... | cmd3

与此同时,我认为cat filename | realcmd1...是可以接受的,因为它使用需要文件名作为参数的初始命令来保持语法标准化。


17
Bash和类似的shell支持< filename cmd | cmd2 ...。那够近吗?
garyjohn

@garyjohn:我认为你应该将其作为答案发布。
Kevin Reid

14
强烈的老贝壳黑客评论:< file command ...至少在80年代中期以来,伯恩式的炮弹已经支持了它,并且可能早在70年代原始sh书写时就已经支持了。更一般地,i / o重定向从左到右解析,并且可以在命令行中以任何顺序散布。所以,cmd <file arg arg...也是有效的。
Dale Hagglund

3
是的,这部分是因为我发明了UUOC是多么容易打字。
Randal Schwartz

2
一个移位的角色与四个未移位的角色并没有那么大的区别,我宁愿产生一个额外的过程,甚至我的手机几乎没有注意到,而不是将文件输入我的提示,这让我每次看到它都很头疼。
Aaron Miller

0

对于那些说猫可以接受使用因为它“闻起来”更好或“更具可读性”的人我只会这样说:

也许......但不是其他可能阅读或试图理解您的代码的人。如果您永远不会尝试用您的示例指导他人或分享您的代码,那么请务必在您自己的闲暇时间使用它。

我也会添加这个评论,作为很长时间的Linux用户和管理员/工程师...(我们中有很多人)它让我们的眼睛流血看到这一点。为什么?因为它使用我们紧密控制资源的系统上的资源。cat命令和管道本身使用完全无用的额外内存和文件句柄。您已经将我的系统需要的资源捆绑在一起,您已经获得了可以解释这些资源使用情况的NOTHING。这是一个巨大的不。

现在,我可以坐在这里与任何人一起讨论代码嗅觉或可读性等问题,但在一天结束时,这是写或错的问题,任何时候你在系统上使用资源并获得任何东西......这是错误的。

作为家庭用户,您可以从我的建议中学习并学习更好的做事方式,或者您可以选择被猫的“气味”,您的选择蒙蔽......但要知道如果您公开使用这种做法,您将被称为在这种做法上,你一定会默默地承认他们是对的,你是固执的,因为这是真的。:-)


我也是一个长期的Linux(和早期的* nix)用户和软件开发人员。当你说“让我们的眼睛流血”时,你不会替我说话cat foo.txt | ...。另一个答案解释了为什么它可以是一个很好的用法。这个案例的简单总结是:“$ CPU time << $ Brain time”(如上文@MikeP评论)。
Jim DeLaHunt

首先,这是2017年的答案(恢复死去的大声笑的方式)。其次,请注意,作为管理员或开发人员,我们应始终尽可能减少资源使用。在编写应用程序和服务时,您会尝试查看对应用程序/服务提供很少或没有任何好处的内存或CPU漏洞吗?UUOC就是这样。现在,有完全有效的使用猫我肯定涉及管道指挥...只是不经常。所以我可能不会像你说的那样为你说话...但如果你是一名专业人士,我会想知道为什么你不会更容易同意(鉴于真正的UUOC情景)。
David Dreggors

问题是内存或CPU耗尽并不是开发人员优化的唯一成本。人工时间的成本,理解问题以及编写和调试实现,也是权衡的一部分。这里的一些答案是基于人类时间比内存或CPU更加稀缺和昂贵的判断。。......但这正在成为一种违反规则的讨论。让对答案的投票说明哪些观点得到了超级用户的认可。
Jim DeLaHunt
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.