“猫的无用之用”的普遍共识是什么?

aru*_*mar 43 unix command-line cat pipe

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

但是最近在我的答案留下了一些评论表明这是 cat 的无用用法之后,我想我会在这里问这个问题。

我查了一下这个问题,看到了维基百科关于 UUOCThe Useless Use of Cat Award的文章,在我看来,提出的论点是从效率的角度来看的。

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

我猜 UUOC 阵营建议使用什么,cmd1 args < file | cmd2 args | cmd3 ..或者如果该命令具有从文件读取的选项,然后将文件作为参数传入。

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

我是否无法理解关于 cat 的无用使用的争论?我知道如果我正在运行一个每 2 秒运行一次并进行大量处理的 cron 作业,那么在这种情况下 cat 可能会很浪费。但除此之外,使用 cat 的普遍共识是什么?

Dan*_*eck 22

从某种意义上说,像这样使用它并不能完成其他任何事情,可能更有效的选项不能(即产生正确的结果),这是无用的。

cat远不止cat somefile. 查阅man cat或阅读我在此答案中所写的内容。但是,如果您绝对肯定只需要单个文件的内容,那么您可能会因不使用cat来获取文件内容而获得一些性能优势。

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

这也取决于你正在编写什么脚本。如果这是您自己的台式机的外壳和便利方法,除了您之外没有人会关心。如果您偶然发现链中的下一个工具最好能够寻找,并将其作为经常使用的软件分发到低性能路由器或类似设备上的低性能路由器或类似设备上的常用软件处理能力,那就不一样了。它总是取决于上下文。

  • 性能成本是否可以忽略不计?在许多情况下,它们是:http://oletange.blogspot.dk/2013/10/useless-use-of-cat.html (3认同)

Ole*_*nge 20

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

很容易理解发生了什么。

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

    cat foo | program1 -o option -b option | program2
Run Code Online (Sandbox Code Playgroud)

比阅读更容易

    program1 -o option -b option < foo | program2
Run Code Online (Sandbox Code Playgroud)

如果您将重定向移动到开头,您会使不习惯此语法的人感到困惑:

    < foo program1 -o option -b option | program2
Run Code Online (Sandbox Code Playgroud)

和例子应该很容易理解。

很容易改变。

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

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

假设如果program1 < foo有效,那么cat foo | program1也将有效是不安全的。然而,在实践中假设相反安全的。如果 STDIN 是一个文件,这个程序可以工作,但如果输入是一个管道,这个程序就会失败,因为它使用了搜索:

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

    # fails
    cat foo | perl -e 'seek(STDIN,1,1) || die;print <STDIN>'
Run Code Online (Sandbox Code Playgroud)

性能损失通常无法衡量。

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

这是一个| cat 性能提高 50%的示例:https : //unix.stackexchange.com/questions/614154/useless-use-of-cat-increases-performance-why

  • 最后给出了实际基准的答案。我还注意到我在这里的评论“有时”猫可以更快。我唯一能想象“无用使用 cat”真正造成的损害是,如果您正在对 huuuge 文件进行琐碎的处理(或者如果该进程可以像 tail 命令一样特殊使用 stdin)... https:// unix.stackexchange.com/a/225608/8337 (2认同)
  • 我对基准不太满意,因为它忽略了“cat”和“&lt;”之间存在巨大差异的情况。例如,如果可用,自动使用“seek”的程序(“tail -c”可以直接查找相关部分,而不是读取所有内容;“wc -c”归结为“stat -c%s”;“sort”自动切换到多线程)或重复调用“cat”/“&lt;”的脚本,因此会增加启动另一个进程所需的时间。尝试 `time for i in {1..9999}; 做猫f | 真的; 完成`。与“猫”31s。与“&lt;”0.07s。 (2认同)

bah*_*mat 17

在日常命令行使用中,它并没有太大的不同。您尤其不会注意到任何速度差异,因为不使用可避免 CPU 上的时间cat,您的 CPU 只会闲置。即使您在所有实际操作中循环浏览成百上千(甚至成百上千)个项目,也不会产生太大差异,除非您在一个非常负载的系统上(平均负载/N CPU > 1)。

橡胶与道路相遇的地方是养成良好的习惯并阻止坏习惯。要拖出一个发霉的陈词滥调,魔鬼在细节中。正是像这样的细节将平庸与伟大分开。

就像在开车时,为什么要左转,而您只能向右转三个?当然可以,而且效果很好。但是,如果您了解左转的力量,那么三个右转似乎很愚蠢。

这不是关于保存一个文件句柄、17k 的 RAM 和 0.004 秒的 CPU 时间。它是关于使用 UNIX 的整个哲学。我的插图中的“左转的力量”不仅仅是重定向输入,而是 UNIX 哲学。完全理解这一点将使您比周围的人表现出色,并且您将获得理解的人的尊重。

  • 如果您正在考虑在没有红绿灯的情况下左转进入 6 车道繁忙的高速公路,那么您可能应该考虑右转或采取不同的路线。*nix 为您提供了多种路线的选择。这是个人喜好和可读性的问题。如果您想“cat file | cmd1 | cat | cmd2 |more”,请继续。(有时如果 cmd1 分页很有用 - cat 会消除它。) $CPU time &lt;&lt; $Brain time。 (4认同)

gar*_*ohn 13

我认为一些评论 UUOC 的人所采取的立场是,如果一个人真正了解 Unix 和 shell 语法,那么在这种情况下就不会使用 cat。这被视为使用糟糕的语法:我可以使用糟糕的语法写一个句子,但仍然能表达我的意思,但我也表现出我对语言的理解很差,进而,我的教育也很差。所以说某物是 UUOC 是另一种说法,表示某人不明白他们在做什么。

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

  • 很长一段时间以来,我都知道还有其他方法可以表达 `cat somefile | 没有 cat 的 shell 中的 prog`,就像 `prog &lt; somefile`,但它们对我来说似乎总是错误的顺序,特别是用管道连接在一起的命令链。现在我看到像 `&lt; somefile prog` 这样优雅的东西可以解决问题,谢谢。我已经没有了使用 cat 的借口了。 (6认同)

ran*_*ing 5

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

我在教育他的时候并不是要防御。毕竟,在我年轻的时候,我会写这个命令,grep foo file.txt | cut ... | cut ...因为每当你执行频繁的单greps 时,你就会学习文件参数的位置,并且已经知道第一个是模式,后面的是文件名。

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

后一个原因更重要,所以我先说出来。当我提供管道作为解决方案时,我希望它是可重用的。很可能在另一条管道的末尾添加或拼接一条管道。在这种情况下,grep 的文件参数会破坏可重用性,并且很可能在文件参数存在的情况下静默执行而不会出现错误消息。IE。grep foo xyz | grep bar xyz | wc将给您xyz包含多少行,bar而您期望包含foo和的行数bar。在使用管道之前必须更改命令的参数很容易出错。再加上无声失败的可能性,它就变成了一种特别阴险的做法。

前一个原因也不是不重要,因为很多“好品味”只是一种直觉的潜意识基本原理,例如上面的无声失败,当某些需要教育的人说“但不是那只猫没用”。

但是,我也会尽量注意我提到的前一个“好品味”的原因。这个原因与 Unix 的正交设计精神有关。grepcutlsgrep。因此,至少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 将需要一个不同的命令来读取一个文件的输出并将其吐出到标准输出(而不是对其进行分页或任何纯粹的吐出到标准输出)。所以你会遇到这样的情况,你说cat file1 file2或者你说dog file1并认真记住避免cat file1避免获得奖励,同时也避免dog file1 file2因为dog如果指定多个文件,设计会抛出错误。

希望此时您对 Unix 设计者表示同情,因为他们没有包含一个单独的命令来将文件输出到标准输出,同时还命名cat为 concatenate 而不是给它一些其他名称。<edit>有这样一只狗,不幸的<经营者。不幸的是,它被放置在管道的末端,阻碍了简单的可组合性。没有在句法或美学上干净的方式将它放在开头。不幸的是,不够通用,所以您从狗开始,但如果您还希望在前一个文件名之后处理它,只需添加另一个文件名。(>另一方面,它也没有那么糟糕。它在最后的位置几乎完美。它通常不是管道的可重用部分,因此它具有象征意义。)</edit>

下一个问题是为什么让命令只将一个文件或几个文件的串联输出到标准输出而不进行任何进一步处理很重要?一个原因是避免让对标准输入进行操作的每个 Unix 命令都知道如何解析至少一个命令行文件参数并将其用作输入(如果存在)。第二个原因是为了避免用户必须记住:(a) 文件名参数在哪里;(b) 避免上面提到的静默管道错误。

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

最后,本可以做得更好的是,如果标准输入可用,诸如grep(但不一定ls)之类的异常命令会产生错误。这是合理的,因为命令包含违反全能 Unix 的正交精神以方便用户的逻辑。为了进一步方便用户,即为了防止静默故障造成的痛苦,如果存在静默故障的可能性,这些命令应该通过警告用户来毫不犹豫地违反它们自己的违规。