没用的猫?


101

这可能在许多常见问题解答中-而不是使用:

cat file | command

(这被称为对猫的无用),正确的方法应该是:

command < file

在第二种“正确”的方式中-操作系统不必产生额外的进程。
尽管知道这一点,我仍然出于两个原因继续使用无用的猫。

  1. 更美观-我喜欢数据仅从左向右均匀移动时。而且它更容易更换cat别的东西(gzcatecho,...),添加第二个文件或插入新的过滤器(pvmbuffergrep...)。

  2. 我“觉得”在某些情况下可能会更快。因为有2个过程,所以速度更快,第一个(cat)执行读取,第二个执行任何操作。它们可以并行运行,这意味着有时可以更快地执行。

我的逻辑是正确的(出于第二个原因)吗?


22
cat是一个身份管道。它仅将其输入流式传输到其输出。如果链中的第二个程序可以从传递给您的同一个参数中获取输入cat(或从标准输入(如果不传递任何参数)中获取输入),则绝对cat没有用的,只会导致派生一个额外的进程并产生一个额外的管道创建。
弗雷德里克·哈米迪

11
@FrédéricHamidi当cat没有自变量或自变量是时-,这是一个身份管道。但是,当它具有多个非破折号文件名参数时,它不仅可以用作标识管道,而且还可以用于实际用途。
kojiro 2012年

3
不幸的是,以前流行的partmaps.org链接已死。内容现在位于Porkmail.org/era/unix/award.html
Tripleee


2
我观察到,如果您想显示向右的数据流(原因1),可以通过在命令之前放置文件重定向来做到这一点,如中所示<file command1 | command2,尽管会在美观方面存在分歧。
holdenweb '19

Answers:


81

直到今天,当某位新秀试图将UUOC钉在我身上作为我的答案之一时,我才知道这个奖项。这是一个cat file.txt | grep foo | cut ... | cut ...。我给了他一个主意,只有在访问了链接之后,他才给我提到了奖项的由来和做法。进一步的搜索使我想到了这个问题。不幸的是,尽管有意识地进行了某种考虑,但没有一个答案包含了我的理由。

我本来不想对他做出回应。毕竟,在我年轻的时候,我之所以编写该命令是grep foo file.txt | cut ... | cut ...因为每当您执行频繁的单曲操作时,您都将grep学习file参数的位置,并且已经知道第一个是模式,而后一个是文件名。

cat当我回答问题时,这是一种有意识的选择,部分原因是出于“品味”的原因(用莱纳斯·托瓦尔兹(Linus Torvalds)的话说),但主要是出于功能方面的令人信服的原因。

后一个原因更为重要,因此我将首先介绍它。当我提供管道作为解决方案时,我希望它可以重用。很可能会在另一个管道的末尾添加一个管道或将其拼接成另一个管道。在那种情况下,使用grep的file参数可以提高可重用性,并且如果file参数存在,很可能在没有错误消息的情况下静默地这样做。即 当您期望同时包含和的行数时,grep foo xyz | grep bar xyz | wc它将给您xyz包含多少行barfoobar。在使用前必须在管道中更改命令的参数很容易出错。加上无声故障的可能性,这成为特别隐蔽的做法。

前一个原因也不重要,因为很多“ 好品味 ”只是一种直观的潜意识原理,对于诸如上述无声失败之类的事情,当某些需要教育的人说“但不是那只猫没用”。

但是,我将尽力使我提到的以前的“好口味”原因变得自觉。这个原因与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 的精神。好吧,如果是这样的话,那么Unix将需要一个不同的命令来读取一个文件的输出并将其吐到stdout(而不是分页或者只是单纯地吐到stdout)。所以,你必须在那里你说的情况cat file1 file2和你说的dog file1,认真记,以避免cat file1就避开了奖,同时还避免了dog file1 file2因为有希望的设计dog,如果指定了多个文件,将抛出一个错误。

希望在这一点上,您对Unix设计器感到同情,因为它不包含将文件吐到stdout的单独命令,同时还cat为串联命名,而不是给它起其他名字。<edit>删除了关于的错误注释<,实际上,这<是一种有效的无复制功能,可将文件吐到stdout,您可以将其放置在管道的开头,因此Unix设计人员确实为此提供了一些特别的功能。</edit>

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

这使我们明白为什么grep要有额外的逻辑。这样做的基本原理是使用户能够流畅使用频繁且独立运行的命令(而不是作为管道使用)。对于可用性的显着提高,这是正交性的略微折衷。并非所有命令都应采用这种方式设计,并且不经常使用的命令应完全避免文件参数的额外逻辑(请记住,额外的逻辑会导致不必要的脆弱性(错误的可能性))。例外是允许文件参数,例如grep。(顺便说一下,请注意,ls有一个完全不同的原因,不仅接受而且几乎需要文件参数)

最后,如果在指定文件参数时标准输入也可用的情况下,grep(但不一定是ls)这样的特殊命令会产生错误,则可以做得更好。


53
请注意,当grep使用多个文件名调用时,它将在找到的行之前添加在其中找到文件的名称(除非您关闭该行为)。它还可以报告单个文件中的行号。如果仅用于catfeed grep,则会丢失文件名,并且行号在所有文件(而不是每个文件)上都是连续的。因此,存在grep处理多个cat无法处理的文件的原因。单文件和零文件大小写只是常规多文件使用的特殊情况grep
Jonathan Leffler

38
正如指出的答案小次郎,这是完全可能的,法律开始与管道< file command1 ...。尽管I / O重定向操作符的常规位置在命令名称及其参数之后,但这仅是约定而非强制性放置。将<确实有先于文件名。因此,>output<input重定向之间存在接近完美的对称性:<input command1 -opt 1 | command2 -o | command3 >output
Jonathan Leffler

15
我认为人们(包括我)扔掉UUoC石头的原因之一是首先进行教育。有时人们会处理数千兆字节的巨大文本文件,在这种情况下,最小化管道(UUoC,将连续的垃圾收集成一个,aso)至关重要,并且通常可以基于以下事实可以安全地假定它:OP确实不知道可能会有小的调整巨大的性能影响。我完全同意您关于脑循环的观点,这就是为什么即使在不需要时我也会定期使用猫。但重要的是要知道它是不需要的。
2013年

13
请理解; 我绝不cat是说那没用。这不是没有cat用的。只是特定的构造不需要使用cat。如果愿意,请注意,它是UUoC(对的无用cat),而不是 UoUC(对Useless的使用cat)。在许多情况下,什么时候cat才是正确使用的工具?当它是正确使用的工具时,我没有问题(实际上,在我的回答中提到了一个案例)。
乔纳森·莱夫勒

6
@randomstring我听到了,但是我认为这确实取决于用例。在命令行上使用时,根据数据的不同,cat管道中可能没有什么大不了的,但是当用作编程环境时,绝对有必要实现这些对性能至关重要的事情。特别是在处理bash性能方面就像矩形齿轮的情况(ksh无论如何,相对而言。我在这里说的要慢10倍-没在开玩笑)。在处理较大的脚本或巨大的循环时,您确实想优化分叉(而不仅限于此)。
AdrianFrühwirth'13

58

不!

首先,在命令中的何处重定向都无关紧要。因此,如果您希望重定向到命令的左侧,就可以了:

< somefile command

是相同的

command < somefile

其次,使用管道时,将发生n + 1个进程和一个子外壳。它绝对是较慢的。在某些情况下,n可能为零(例如,当您重定向到内置的Shell时),因此使用cat完全不必要地添加了新进程。

概括地说,每当您发现自己使用管道时,值得花费30秒的时间来查看是否可以消除它。(但是花费不超过30秒的时间可能不值得。)以下是一些不必要地频繁使用管道和进程的示例:

for word in $(cat somefile);  # for word in $(<somefile); … (or better yet, while read < somefile)

grep something | awk stuff; # awk '/something/ stuff' (similar for sed)

echo something | command; # command <<< something (although echo would be necessary for pure POSIX)

随时进行编辑以添加更多示例。


2
好吧,速度提升不会太大。
达卡龙

9
从技术上讲,将“ <somefile”放在“ command”之前可以使您从左到右,但是由于没有语法上的界线,它使模棱两可:这< cat grep dog是一个精心设计的示例,表明您无法轻松区分输入文件和命令接收输入和命令的参数。
死灵法师

2
我的决定STDIN重定向飞到哪里,通过经验法则是做什么的最小化外观歧义/的潜在的惊喜。教条式地说它先于死灵法师的问题,但教条式地说它就可以做同样的事情。考虑:stdout=$(foo bar -exec baz <qux | ENV=VAR quux)。问:不<qux适用foo,或者baz,这是-exec由“d foo?答:它适用于foo,但可能看起来不明确。在这种情况下,放在<qux 前面 foo更清晰,尽管不太常见,并且类似于尾随ENV=VAR quux
Mark G.

3
@necromancer,<"cat" grep dog在那里更容易阅读。(我通常是赞成空白的,但是这种特殊情况非常例外)。
查尔斯·达菲

1
@kojiro“绝对慢。” 如果没有数字备份,就无法编写。我的电话在这里:oletange.blogspot.com/2013/10/useless-use-of-cat.html(它们显示只有在高通量的情况下速度才会变慢),您的位置在哪里?
Ole Tange

30

我不同意UUOC奖过于自负的大多数情况,因为在教别人时,它cat是任何命令或棘手的复杂命令管道的方便占位符,这些命令或管道产生适合于所讨论的问题或任务的输出。

在Stack Overflow,ServerFault,Unix&Linux等站点或任何SE站点上尤其如此。

如果有人特别询问优化问题,或者您想添加更多有关优化的信息,那就很好地谈论使用cat效率低下的问题。但是不要嘲笑人们,因为他们选择在示例中追求简单和易于理解,而不是看我如何酷我!复杂。

简而言之,因为猫并不总是猫。

另外,因为大多数喜欢奖励UUOC的人都这样做,是因为他们更关心炫耀自己的“聪明”,而不是帮助或教导人们。实际上,他们证明了他们可能只是另一个新手,他们找到了一个击败对手的小棒。


更新资料

这是我在https://unix.stackexchange.com/a/301194/7696答复中发布的另一个UUOC :

sqlq() {
  local filter
  filter='cat'

  # very primitive, use getopts for real option handling.
  if [ "$1" == "--delete-blank-lines" ] ; then
    filter='grep -v "^$"'
    shift
  fi

  # each arg is piped into sqlplus as a separate command
  printf "%s\n" "$@" | sqlplus -S sss/eee@sid | $filter
}

UUOC的学徒会说这是一个UUOC,因为很容易将$filter默认值设置为空字符串并执行该if语句,filter='| grep -v "^$"'但是IMO通过不将管道字符嵌入到该IMO中$filter,此“无用” cat用于自我记录事实的极其有用的目的这$filterprintf线不只是一个参数sqlplus,它是一个可选的用户可选择的输出滤波器。

如果有任何需要有多个可选的输出滤波器,选择处理可以只追加| whatever$filter经常需要-一个额外cat的管道是不会伤害任何东西或导致任何性能上明显的损失。


11
顺便说一句- ==内部[ ]不是POSIX指定的,并不是所有的实现都接受它。标准化算子是=
Charles Duffy

27

对于UUoC版本,cat必须将文件读取到内存中,然后将其写到管道中,并且命令必须从管道中读取数据,因此内核必须将整个文件复制3次,而在重定向的情况下,内核只需要复制一次文件。做某件事比做三遍更快。

使用:

cat "$@" | command

与完全不同,不一定是无用的cat。如果该命令是一个接受零个或多个文件名参数并依次处理它们的标准过滤器,它仍然没有用。考虑以下tr命令:它是一个纯过滤器,它忽略或拒绝文件名参数。要向其提供多个文件,您必须cat如图所示使用。(当然,还有一个单独的讨论,即的设计tr不是很好;没有任何真正的理由不能将其设计为标准过滤器。)如果您希望命令将所有输入都视为一个输入,这也可能是有效的。即使该命令可以接受多个单独的文件,也可以将单个文件而不是多个单独的文件:例如,wc这样的命令。

这是cat single-file无条件地无用的情况。


26

防御猫:

是,

   < input process > output 

要么

   process < input > output 

效率更高,但是许多调用都没有性能问题,因此您不必在意。

人体工程学原因:

我们习惯于从左到右阅读,所以像

    cat infile | process1 | process2 > outfile

很容易理解。

    process1 < infile | process2 > outfile

必须跳过process1,然后从左到右阅读。可以通过以下方法治愈:

    < infile process1 | process2 > outfile

看起来有点像是有一个箭头指向左边,什么也没有。更令人困惑,看起来像是花哨的报价是:

    process1 > outfile < infile

生成脚本通常是一个反复的过程,

    cat file 
    cat file | process1
    cat file | process1 | process2 
    cat file | process1 | process2 > outfile

您可以逐步看到进度

    < file 

甚至行不通。简单的方法不容易出错,而cat则符合人体工程学的命令分类非常简单。

另一个主题是,大多数人在使用计算机之前和使用计算机作为程序员时就已经接触了>和<作为比较运算符,而这些人通常更容易接触到这些。

将两个操作数与<和>进行比较是可逆的,这意味着

(a > b) == (b < a)

我记得我第一次使用<进行输入重定向

a.sh < file 

可能与

file > a.sh

并以某种方式覆盖我的a.sh脚本。也许这对许多初学者来说都是一个问题。

罕见的差异

wc -c journal.txt
15666 journal.txt
cat journal.txt | wc -c 
15666

后者可以直接用于计算。

factor $(cat journal.txt | wc -c)

当然,<也可以在这里代替文件参数使用:

< journal.txt wc -c 
15666
wc -c < journal.txt
15666
    

但是谁在乎-15k?

如果我偶尔遇到问题,那我肯定会改变我养猫的习惯。

当使用很大或很多文件时,避免使用cat是可以的。对于大多数问题,猫的使用是正交的,没有话题,不是问题。

在每个第二个shell主题上开始对cat讨论的这些无用的无用的使用只会令人讨厌和无聊。处理绩效问题时,请活出自己的生活,并等待自己的成名。


5
+11111 ..作为当前接受的答案的作者,我强烈推荐这种令人愉快的补充。具体的例子阐明了我经常抽象和冗长的论点,而您从作者早期的恐惧中获得的笑声file > a.sh值得阅读:)感谢分享!
necromancer

在此调用中cat file | wc -cwc需要读取stdin直到EOF,再计算字节。但是在此,wc -c < file它只是统计stdin,发现它是一个常规文件并打印st_size而不是读取任何输入。对于大文件,性能差异将清晰可见。
oguz ismail

18

另一个问题是管道可以静默屏蔽子外壳。对于此示例,我将替换catecho,但存在相同的问题。

echo "foo" | while read line; do
    x=$line
done

echo "$x"

您可能希望x包含foo,但事实并非如此。在x您设定的是在子shell催生执行while循环。x在启动管道的外壳程序中,它的值无关或根本没有设置。

在bash4中,您可以配置一些shell选项,以便管道的最后一个命令与启动管道的命令在同一shell中执行,但是您可以尝试执行此操作

echo "foo" | while read line; do
    x=$line
done | awk '...'

并且x再次位于while的子外壳中。


5
在严格的POSIX shell中,这可能是一个棘手的问题,因为这里没有字符串或进程替换来避免管道。即使在这种情况下,BashFAQ 24也提供了一些有用的解决方案。
kojiro 2012年

4
某些外壳中,图示的管道不会创建子外壳。例子包括Korn和Z。它们还支持进程替换以及此处的字符串。当然,它们并不是严格意义上的 POSIX。Bash 4必须shopt -s lastpipe避免创建子外壳。
暂停,直到另行通知。

13

正如经常指出这一点以及许多其他shell编程反模式的人一样,我不得不迟来了。

Shell脚本非常是一种复制/粘贴语言。对于大多数编写shell脚本的人来说,他们并不是在学习该语言。这只是他们要继续以他们实际上有点熟悉的语言来做事情所必须克服的障碍。

在这种情况下,我认为传播各种shell脚本反模式具有破坏性,甚至可能具有破坏性。理想情况下,人们应该可以在堆栈溢出中找到的代码将更改复制/粘贴到他们的环境中,而只需进行最小的更改,并且不完整。

在网络上许多Shell脚本资源中,Stack Overflow并不常见,因为用户可以通过编辑站点上的问题和答案来帮助改善站点的质量。但是,代码编辑可能会出现问题,因为很容易进行代码作者不希望的更改。因此,我们倾向于留下评论以建议对代码进行更改。

UUCA和相关的反模式注释不仅适用于我们所注释的代码的作者;它们是一个警告购买者,可以帮助网站的读者了解他们在此处找到的代码中的问题。

我们不希望出现这样的情况,即在Stack Overflow上没有答案推荐无用的cats(或未加引号的变量,或chmod 777,或各种各样的其他反模式灾难),但我们至少可以帮助教育要复制/的用户。将此代码粘贴到其脚本的最内层紧密循环中,该循环执行数百万次。

就技术原因而言,传统观点认为我们应该尽量减少外部流程的数量;在编写Shell脚本时,这仍然是很好的一般指导。


2
同样,对于大型文件,管道传输cat会带来大量额外的上下文切换和内存带宽(以及cat读取缓冲区和管道缓冲区中多余的数据副本会污染L3缓存)。特别是在大型多核计算机(如许多主机设置)上,缓存/内存带宽是共享资源。
彼得·科德斯

1
@PeterCordes请发布您的尺寸。所以我们可以在实践中确定它是否真的很重要。我的经验是,这通常并不重要:oletange.blogspot.com/2013/10/useless-use-of-cat.html
Ole Tange

1
您自己的博客显示高吞吐量下降了50%,您甚至没有关注对总吞吐量的影响(如果您有让其他内核忙碌的东西)。如果可以解决,我可能会在x264或x265使用所有核心对视频进行编码的同时运行测试,并查看它会减慢视频编码的速度。 bzip2与单独增加的开销量(否则机器处于空闲状态)gzip相比,压缩和压缩都非常慢cat。很难读取表格(数字中间的换行?)。sys时间增加了很多,但是相对于用户还是真实的还是很小的?
彼得·科德斯

8

我经常cat file | myprogram在示例中使用。有时我被指控对猫的无用使用(http://porkmail.org/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>'

绩效成本

做额外的事情是有代价的cat。为了让我大致了解一下,我进行了一些测试来模拟基线(cat),低吞吐量(bzip2),中吞吐量(gzip)和高吞吐量(grep)。

cat $ISO | cat
< $ISO cat
cat $ISO | bzip2
< $ISO | bzip2
cat $ISO | gzip
< $ISO gzip
cat $ISO | grep no_such_string
< $ISO grep no_such_string

测试是在低端系统(0.6 GHz)和普通笔记本电脑(2.2 GHz)上运行的。它们在每个系统上运行了10次,并且选择了最佳时机来模拟每次测试的最佳情况。$ ISO是ubuntu-11.04-desktop-i386.iso。(此处为更漂亮的表格:http : //oletange.blogspot.com/2013/10/useless-use-of-cat.html

CPU                       0.6 GHz ARM
Command                   cat $ISO|                        <$ISO                            Diff                             Diff (pct)
Throughput \ Time (ms)    User       Sys        Real       User       Sys        Real       User       Sys        Real       User       Sys        Real
Baseline (cat)                     55      14453      33090         23       6937      33126         32       7516        -36        239        208         99
Low (bzip2)                   1945148      16094    1973754    1941727       5664    1959982       3420      10430      13772        100        284        100
Medium (gzip)                  413914      13383     431812     407016       5477     416760       6898       7906      15052        101        244        103
High (grep no_such_string)      80656      15133      99049      79180       4336      86885       1476      10797      12164        101        349        114

CPU                       Core i7 2.2 GHz
Command                   cat $ISO|           <$ISO             Diff          Diff (pct)
Throughput \ Time (ms)    User     Sys Real   User   Sys Real   User Sys Real User       Sys Real
Baseline (cat)                    0 356    215      1  84     88    0 272  127          0 423  244
Low (bzip2)                  136184 896 136765 136728 160 137131 -545 736 -366         99 560   99
Medium (gzip)                 26564 788  26791  26332 108  26492  232 680  298        100 729  101
High (grep no_such_string)      264 392    483    216  84    304   48 308  179        122 466  158

结果表明,对于中低吞吐量,成本约为1%。这完全在测量的不确定性之内,因此在实践中没有区别。

对于高吞吐量,差异更大,并且两者之间存在明显差异。

得出结论:您应该使用if <代替cat |

  • 处理的复杂性类似于简单的grep
  • 性能比可读性更重要。

否则,无论您使用<还是都没有关系cat |

因此,仅在以下情况下才应授予UUoC奖:

  • 您可以衡量绩效上的显着差异(授予奖项时发布测量结果)
  • 性能比可读性更重要。

-3

我认为(传统方式)使用管道要快一些;在我的盒子上,我使用strace命令来查看发生了什么:

没有管道:

toc@UnixServer:~$ strace wc -l < wrong_output.c
execve("/usr/bin/wc", ["wc", "-l"], [/* 18 vars */]) = 0
brk(0)                                  = 0x8b50000
access("/etc/ld.so.nohwcap", F_OK)      = -1 ENOENT (No such file or directory)
mmap2(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb77ad000
access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY)      = 3
fstat64(3, {st_mode=S_IFREG|0644, st_size=29107, ...}) = 0
mmap2(NULL, 29107, PROT_READ, MAP_PRIVATE, 3, 0) = 0xb77a5000
close(3)                                = 0
access("/etc/ld.so.nohwcap", F_OK)      = -1 ENOENT (No such file or directory)
open("/lib/i386-linux-gnu/libc.so.6", O_RDONLY) = 3
read(3, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0p\222\1\0004\0\0\0"..., 512) = 512
fstat64(3, {st_mode=S_IFREG|0755, st_size=1552584, ...}) = 0
mmap2(NULL, 1563160, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0xb7627000
mmap2(0xb779f000, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x178) = 0xb779f000
mmap2(0xb77a2000, 10776, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0xb77a2000
close(3)                                = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb7626000
set_thread_area({entry_number:-1 -> 6, base_addr:0xb76268d0, limit:1048575, seg_32bit:1, contents:0, read_exec_only:0, limit_in_pages:1, seg_not_present:0, useable:1}) = 0
mprotect(0xb779f000, 8192, PROT_READ)   = 0
mprotect(0x804f000, 4096, PROT_READ)    = 0
mprotect(0xb77ce000, 4096, PROT_READ)   = 0
munmap(0xb77a5000, 29107)               = 0
brk(0)                                  = 0x8b50000
brk(0x8b71000)                          = 0x8b71000
open("/usr/lib/locale/locale-archive", O_RDONLY|O_LARGEFILE) = 3
fstat64(3, {st_mode=S_IFREG|0644, st_size=5540198, ...}) = 0
mmap2(NULL, 2097152, PROT_READ, MAP_PRIVATE, 3, 0) = 0xb7426000
mmap2(NULL, 1507328, PROT_READ, MAP_PRIVATE, 3, 0x2a8) = 0xb72b6000
close(3)                                = 0
open("/usr/share/locale/locale.alias", O_RDONLY) = 3
fstat64(3, {st_mode=S_IFREG|0644, st_size=2570, ...}) = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb77ac000
read(3, "# Locale name alias data base.\n#"..., 4096) = 2570
read(3, "", 4096)                       = 0
close(3)                                = 0
munmap(0xb77ac000, 4096)                = 0
open("/usr/share/locale/fr_FR.UTF-8/LC_MESSAGES/coreutils.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/usr/share/locale/fr_FR.utf8/LC_MESSAGES/coreutils.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/usr/share/locale/fr_FR/LC_MESSAGES/coreutils.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/usr/share/locale/fr.UTF-8/LC_MESSAGES/coreutils.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/usr/share/locale/fr.utf8/LC_MESSAGES/coreutils.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/usr/share/locale/fr/LC_MESSAGES/coreutils.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/usr/share/locale-langpack/fr_FR.UTF-8/LC_MESSAGES/coreutils.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/usr/share/locale-langpack/fr_FR.utf8/LC_MESSAGES/coreutils.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/usr/share/locale-langpack/fr_FR/LC_MESSAGES/coreutils.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/usr/share/locale-langpack/fr.UTF-8/LC_MESSAGES/coreutils.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/usr/share/locale-langpack/fr.utf8/LC_MESSAGES/coreutils.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/usr/share/locale-langpack/fr/LC_MESSAGES/coreutils.mo", O_RDONLY) = 3
fstat64(3, {st_mode=S_IFREG|0644, st_size=316721, ...}) = 0
mmap2(NULL, 316721, PROT_READ, MAP_PRIVATE, 3, 0) = 0xb7268000
close(3)                                = 0
open("/usr/lib/i386-linux-gnu/gconv/gconv-modules.cache", O_RDONLY) = 3
fstat64(3, {st_mode=S_IFREG|0644, st_size=26064, ...}) = 0
mmap2(NULL, 26064, PROT_READ, MAP_SHARED, 3, 0) = 0xb7261000
close(3)                                = 0
read(0, "#include<stdio.h>\n\nint main(int "..., 16384) = 180
read(0, "", 16384)                      = 0
fstat64(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 2), ...}) = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb7260000
write(1, "13\n", 313
)                     = 3
close(0)                                = 0
close(1)                                = 0
munmap(0xb7260000, 4096)                = 0
close(2)                                = 0
exit_group(0)                           = ?

和管道:

toc@UnixServer:~$ strace cat wrong_output.c | wc -l
execve("/bin/cat", ["cat", "wrong_output.c"], [/* 18 vars */]) = 0
brk(0)                                  = 0xa017000
access("/etc/ld.so.nohwcap", F_OK)      = -1 ENOENT (No such file or directory)
mmap2(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb774b000
access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY)      = 3
fstat64(3, {st_mode=S_IFREG|0644, st_size=29107, ...}) = 0
mmap2(NULL, 29107, PROT_READ, MAP_PRIVATE, 3, 0) = 0xb7743000
close(3)                                = 0
access("/etc/ld.so.nohwcap", F_OK)      = -1 ENOENT (No such file or directory)
open("/lib/i386-linux-gnu/libc.so.6", O_RDONLY) = 3
read(3, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0p\222\1\0004\0\0\0"..., 512) = 512
fstat64(3, {st_mode=S_IFREG|0755, st_size=1552584, ...}) = 0
mmap2(NULL, 1563160, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0xb75c5000
mmap2(0xb773d000, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x178) = 0xb773d000
mmap2(0xb7740000, 10776, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0xb7740000
close(3)                                = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb75c4000
set_thread_area({entry_number:-1 -> 6, base_addr:0xb75c48d0, limit:1048575, seg_32bit:1, contents:0, read_exec_only:0, limit_in_pages:1, seg_not_present:0, useable:1}) = 0
mprotect(0xb773d000, 8192, PROT_READ)   = 0
mprotect(0x8051000, 4096, PROT_READ)    = 0
mprotect(0xb776c000, 4096, PROT_READ)   = 0
munmap(0xb7743000, 29107)               = 0
brk(0)                                  = 0xa017000
brk(0xa038000)                          = 0xa038000
open("/usr/lib/locale/locale-archive", O_RDONLY|O_LARGEFILE) = 3
fstat64(3, {st_mode=S_IFREG|0644, st_size=5540198, ...}) = 0
mmap2(NULL, 2097152, PROT_READ, MAP_PRIVATE, 3, 0) = 0xb73c4000
mmap2(NULL, 1507328, PROT_READ, MAP_PRIVATE, 3, 0x2a8) = 0xb7254000
close(3)                                = 0
fstat64(1, {st_mode=S_IFIFO|0600, st_size=0, ...}) = 0
open("wrong_output.c", O_RDONLY|O_LARGEFILE) = 3
fstat64(3, {st_mode=S_IFREG|0664, st_size=180, ...}) = 0
read(3, "#include<stdio.h>\n\nint main(int "..., 32768) = 180
write(1, "#include<stdio.h>\n\nint main(int "..., 180) = 180
read(3, "", 32768)                      = 0
close(3)                                = 0
close(1)                                = 0
close(2)                                = 0
exit_group(0)                           = ?
13

你可以做一些测试与stracetime命令与良好的标杆更多和更长的命令。


9
我不理解您使用pipe(传统方式)是什么意思,或者您为什么认为这strace表明它更快— 在第二种情况下strace没有跟踪wc -l执行。它仅在此处跟踪管道的第一个命令。
kojiro 2012年

@kojiro:我的意思是传统方式=最常用的方式(我认为我们使用管道多于间接方式),我无法确认它是否更快,在我的追踪中,我看到了更多的间接方式系统调用。您可以使用一个ac程序和一个循环来查看,这会消耗更多时间。如果您有兴趣,我们可以放在这里:)
TOC 2012年

3
一个苹果对苹果的比较会放strace -f sh -c 'wc -l < wrong_output.c'一起strace -f sh -c 'cat wrong_output.c | wc -l'
Charles Duffy

5
以下是ideone.com结果,目前这显然有利于无catideone.com/2w1W42#stderr
tripleee

1
@CharlesDuffy:mkfifo创建一个命名管道。先建立一个匿名管道pipe(2),然后进行分叉,并让父级和子级关闭管道的不同末端。但是,是的,这个答案完全是胡说八道,甚至都没有试图计算系统调用或用于strace -O衡量开销,也没有试图为-r每个调用
打上
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.