Leo*_*sky 90 shell command-line cat
这可能是许多常见问题解答 - 而不是使用:
cat file | command
Run Code Online (Sandbox Code Playgroud)
(这被称为无用的猫),正确的方式应该是:
command < file
Run Code Online (Sandbox Code Playgroud)
在第二,"正确"的方式 - 操作系统不必产生额外的过程.
尽管知道这一点,我继续使用无用的猫有两个原因.
更美观 - 我喜欢数据仅从左到右均匀移动.而且它更容易更换cat别的东西(gzcat,echo,...),添加第二个文件或插入新的过滤器(pv,mbuffer,grep...).
我"觉得"在某些情况下可能会更快.更快,因为有2个进程,1st(cat)执行读取而第二个执行任何操作.它们可以并行运行,这意味着有时可以更快地执行.
我的逻辑是否正确(第二个原因)?
nec*_*cer 73
直到今天,我还没有意识到这个奖项,当时有些菜鸟试图将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,而你期待同时包含的行数foo和bar.在使用之前必须在管道中更改参数的参数很容易出错.除此之外,它还存在无声失败的可能性,并成为一种特别阴险的做法.
前一个原因并不重要,因为许多" 好品味 "仅仅是一种直观的潜意识理论,例如上面的沉默失败,当你需要教育的人说"但不是那只猫无用".
但是,我会尽量让我意识到前面提到的"好味道"的原因.这个原因与Unix的正交设计精神有关.grep没有cut,ls没有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>删除了错误的注释是一个有效的无复制工具,可以将文件吐出到stdout,你可以将其定位在管道的开头,这样Unix设计人员就可以为此专门设置一些内容.<<</edit>
接下来的问题是,为什么在没有任何进一步处理的情况下将命令仅仅吐出文件或将多个文件串联到stdout很重要?一个原因是避免让每个运行在标准输入上的Unix命令知道如何解析至少一个命令行文件参数,并将其用作输入(如果存在).第二个原因是避免用户必须记住:(a)文件名参数的去向; (b)避免如上所述的无声管道虫.
这让我们知道为什么grep有额外的逻辑.其基本原理是允许用户流畅地使用频繁且独立使用的命令(而不是作为管道).对于可用性的显着增加,这是对正交性的轻微折衷.并非所有命令都应该以这种方式设计,并且不经常使用的命令应该完全避免文件参数的额外逻辑(记住额外的逻辑会导致不必要的脆弱性(bug的可能性)).例外情况是允许文件参数,例如grep.(顺便说一句,请注意,ls有一个完全不同的理由不仅接受,而且几乎需要文件参数)
最后,如果标准输入在指定文件参数时也可用,那么可以做得更好的是,如果grep(但不一定ls)这样的异常命令会产生错误.
koj*_*iro 57
不!
首先,重定向发生在命令的哪个位置并不重要.所以如果你喜欢你的命令左边的重定向,那很好:
< somefile command
Run Code Online (Sandbox Code Playgroud)
是相同的
command < somefile
Run Code Online (Sandbox Code Playgroud)
其次,使用管道时会发生n + 1个进程和子shell.它最明显更慢.在某些情况下,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)
Run Code Online (Sandbox Code Playgroud)
随意编辑以添加更多示例.
cas*_*cas 28
我不同意过度自负的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
}
Run Code Online (Sandbox Code Playgroud)
UUOC的学生会说这是一个UUOC,因为很容易将$filter默认设置为空字符串并且if语句可以做filter='| grep -v "^$"'但IMO,通过不嵌入管道字符$filter,这个"无用" cat用于自我记录事实的非常有用的目的这$filter对printf线不只是一个参数sqlplus,它是一个可选的用户可选择的输出滤波器.
如果有任何需要有多个可选的输出滤波器,可以选择处理可能只是追加| whatever到$filter经常需要-一个额外cat的管道是不会伤害任何东西或导致任何性能上明显的损失.
Jon*_*ler 26
随着UUOC版本,cat具有对文件读入内存,然后将它写出来的管道,命令必须从管道中读取数据,所以内核具有整个文件复制3在重定向的情况下,次而,内核只需复制一次文件.做一次比做三次更快.
使用:
cat "$@" | command
Run Code Online (Sandbox Code Playgroud)
是完全不同的,不一定无用的cat.如果命令是接受零个或多个文件名参数的标准过滤器并依次处理它们,那么它仍然是无用的.考虑以下tr命令:它是一个忽略或拒绝文件名参数的纯过滤器.要向其提供多个文件,您必须使用cat如图所示.(当然,有一个单独的讨论,设计tr不是很好;没有真正的理由它不能被设计为标准过滤器.)如果你想要命令将所有输入视为一个,这也可能是有效的.单个文件而不是多个单独的文件,即使该命令会接受多个单独的文件:例如,wc这样的命令.
这是cat single-file是无条件无用的情况下.
use*_*own 23
为了防御猫:
是,
< input process > output
Run Code Online (Sandbox Code Playgroud)
要么
process < input > output
Run Code Online (Sandbox Code Playgroud)
效率更高,但许多调用没有性能问题,所以你不在乎.
我们习惯从左到右阅读,所以命令就像
cat infile | process1 | process2 > outfile
Run Code Online (Sandbox Code Playgroud)
理解是微不足道的.
process1 < infile | process2 > outfile
Run Code Online (Sandbox Code Playgroud)
必须跳过process1,然后从左到右阅读.这可以通过以下方式治愈:
< infile process1 | process2 > outfile
Run Code Online (Sandbox Code Playgroud)
看起来不知何故,好像有一个箭头指向左边,没有任何东西.更令人困惑,看起来像花哨的引用是:
process1 > outfile < infile
Run Code Online (Sandbox Code Playgroud)
并且生成脚本通常是一个迭代过程,
cat file
cat file | process1
cat file | process1 | process2
cat file | process1 | process2 > outfile
Run Code Online (Sandbox Code Playgroud)
你在哪里逐步看到你的进步
< file
Run Code Online (Sandbox Code Playgroud)
甚至不起作用.简单的方法不易出错,符合人体工程学的命令连接对于cat来说很简单.
另一个话题是,大多数人在使用计算机之前和作为程序员使用计算机之前,都接触过>和<作为比较运算符,因此更多地接触到这些.
并且将两个操作数与<和>进行比较是对比交换,这意味着
(a > b) == (b < a)
Run Code Online (Sandbox Code Playgroud)
我记得第一次使用<输入重定向时,我担心
a.sh < file
Run Code Online (Sandbox Code Playgroud)
可能意味着相同
file > a.sh
Run Code Online (Sandbox Code Playgroud)
并以某种方式覆盖我的a.sh脚本.对许多初学者来说,这可能是一个问题.
wc -c journal.txt
15666 journal.txt
cat journal.txt | wc -c
15666
Run Code Online (Sandbox Code Playgroud)
后者可以直接用于计算.
factor $(cat journal.txt | wc -c)
Run Code Online (Sandbox Code Playgroud)
当然<也可以在这里使用,而不是文件参数:
< journal.txt wc -c
15666
wc -c < journal.txt
15666
Run Code Online (Sandbox Code Playgroud)
但是谁在乎 - 15k?
如果我偶尔会遇到问题,我肯定会改变调用猫的习惯.
当使用非常大或许多文件时,避免使用cat就可以了.对于大多数问题,猫的使用是正交的,偏离主题,而不是问题.
在每个第二个shell主题上开始这些无用的无用的cat讨论只会令人烦恼和无聊.在处理表演问题时,获得生命,等待一分钟的成名.
che*_*ner 18
另一个问题是管道可以静默掩盖子壳.对于这个例子,我将替换cat为echo,但存在同样的问题.
echo "foo" | while read line; do
x=$line
done
echo "$x"
Run Code Online (Sandbox Code Playgroud)
您可能期望x包含foo但不包含.在x您设定的是在子shell催生执行while循环.x在启动管道的shell中具有不相关的值,或者根本没有设置.
在bash4中,您可以配置一些shell选项,以便管道的最后一个命令在与启动管道的shell相同的shell中执行,但是您可以尝试这个
echo "foo" | while read line; do
x=$line
done | awk '...'
Run Code Online (Sandbox Code Playgroud)
并且x再一次是本地while的子弹.
tri*_*eee 14
作为经常指出这个和其他一些shell编程反模式的人,我觉得有必要,姗姗来迟,权衡.
Shell脚本是一种复制/粘贴语言.对于大多数编写shell脚本的人来说,他们不在其中学习语言; 这只是他们必须克服的一个障碍,以便继续用他们实际上熟悉的语言来做事.
在这种情况下,我认为传播各种shell脚本反模式具有破坏性,甚至可能具有破坏性.理想情况下,有人在Stack Overflow上找到的代码可以通过最少的更改和不完整的理解来复制/粘贴到他们的环境中.
在网络上的众多shell脚本资源中,Stack Overflow很不寻常,因为用户可以通过编辑网站上的问题和答案来帮助塑造网站的质量.但是,代码编辑可能会有问题,因为很容易进行代码作者不想要的更改.因此,我们倾向于留下评论来建议对代码的更改.
UUCA和相关的反模式评论不仅适用于我们评论的代码的作者; 他们同样是一个警告性的经纪人,以帮助网站的读者了解他们在这里找到的代码中的问题.
我们无法希望实现Stack Overflow上没有答案推荐无用的cats(或未引用的变量,或者chmod 777其他各种反模式瘟疫)的情况,但我们至少可以帮助教育即将复制的用户/将此代码粘贴到其脚本的最内层紧密循环中,该循环执行数百万次.
就技术原因而言,传统观点认为我们应尽量减少外部流程的数量; 在编写shell脚本时,这仍然是一个很好的一般指导.
我经常cat file | myprogram在示例中使用。有时我被指控无用的使用猫(http://porkmail.org/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是文件,则该程序可以工作,但是如果输入是管道,则该程序将失败,因为它使用了seek:
# 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)做额外的事情是有代价的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
Run Code Online (Sandbox Code Playgroud)
这些测试在低端系统(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
Run Code Online (Sandbox Code Playgroud)
结果表明,对于中低吞吐量,成本约为1%。这完全在测量的不确定性之内,因此在实践中没有区别。
对于高吞吐量,差异更大,并且两者之间存在明显差异。
得出结论:您应该使用if <代替cat |:
否则,无论您使用<还是都没有关系cat |。
因此,仅在以下情况下,才应授予UUoC奖:
| 归档时间: |
|
| 查看次数: |
22771 次 |
| 最近记录: |