<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Unix | 酷 壳 - CoolShell</title>
	<atom:link href="https://coolshell.cn/tag/unix/feed" rel="self" type="application/rss+xml" />
	<link>https://coolshell.cn</link>
	<description>享受编程和技术所带来的快乐 - Coding Your Ambition</description>
	<lastBuildDate>Sun, 03 Nov 2019 15:57:20 +0000</lastBuildDate>
	<language>zh-CN</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.2</generator>
	<item>
		<title>Unix 50 年：Ken Thompson 的密码</title>
		<link>https://coolshell.cn/articles/19996.html</link>
					<comments>https://coolshell.cn/articles/19996.html#comments</comments>
		
		<dc:creator><![CDATA[陈皓]]></dc:creator>
		<pubDate>Sun, 03 Nov 2019 06:12:54 +0000</pubDate>
				<category><![CDATA[Unix/Linux]]></category>
		<category><![CDATA[技术读物]]></category>
		<category><![CDATA[操作系统]]></category>
		<category><![CDATA[杂项资源]]></category>
		<category><![CDATA[BSD]]></category>
		<category><![CDATA[Dennis Ritchie]]></category>
		<category><![CDATA[Ken Thompson]]></category>
		<category><![CDATA[Unix]]></category>
		<guid isPermaLink="false">https://coolshell.cn/?p=19996</guid>

					<description><![CDATA[<p>50年前，除了Apollo上天之外，还有一个大事的发生，就是Unix操作系统的诞生，若干年前我写过《Unix的传奇，上篇，下篇》，Unix是我入行前十年伴我成长...</p>
<p class="read-more"><a class="btn btn-default" href="https://coolshell.cn/articles/19996.html"> Read More<span class="screen-reader-text">  Read More</span></a></p>
The post <a href="https://coolshell.cn/articles/19996.html">Unix 50 年：Ken Thompson 的密码</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></description>
										<content:encoded><![CDATA[<p><script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158"
     crossorigin="anonymous"></script><img decoding="async" loading="lazy" class="alignright size-medium" src="https://coolshell.cn/wp-content/uploads/2019/11/ken.dennis-300x186.jpeg" alt="" width="300" height="186" />50年前，除了Apollo上天之外，还有一个大事的发生，就是Unix操作系统的诞生，若干年前我写过《Unix的传奇，<a href="https://coolshell.cn/articles/2322.html" target="_blank" rel="noopener noreferrer">上篇</a>，<a href="https://coolshell.cn/articles/2324.html" target="_blank" rel="noopener noreferrer">下篇</a>》，Unix是我入行前十年伴我成长的操作系统，虽然现在Linux早已接过了Unix的时代交接棒，但是，Unix文化对我个人的技术观影响是非常大的（注：《<a href="https://book.douban.com/subject/1467587/" target="_blank" rel="noopener noreferrer">Unix编程艺术</a>》是一本对影响我很深的书），而对于 <a href="https://en.wikipedia.org/wiki/Ken_Thompson">Ken Thompson</a> 和 <a href="https://en.wikipedia.org/wiki/Dennis_Ritchie" target="_blank" rel="noopener noreferrer">Dennis Ritchie</a> 这两位 Unix 的缔造者，也是计算机圈中的神一般的人物。今天，Dennis已经去逝，Ken在Google里跟 Rob Pike和 Robert Griesemer 这两位大神在开发Go语言。</p>
<p>P.S. 今年，我一直想写篇Unix 50周年纪念的文章，但一直无从下手，因为不想写过大的命题，如果能写个轶事最好不过。正好过完国庆节，技术圈里有个“热搜”——Ken Thompson的密码。但一直没有时间，所以拖到今天才写下来。</p>
<p>正文开始，2014年，有个叫Leah Neukirchen的程序员（<a href="https://leahneukirchen.org/blog/" target="_blank" rel="noopener noreferrer">blog</a>）在 BSD 3 的源代码中的 <code><a href="https://leahneukirchen.org/blog/archive/2019/10/ken-thompson-s-unix-password.html" target="_blank" rel="noopener noreferrer">/etc/passwd</a></code> 看到了早年Unix黑客们的被 hash了的密码，该文件如下所示：</p>
<p><span id="more-19996"></span></p>
<pre>root:OVCPatZ8RFmFY:0:10:Ernie Co-vax,4156427925:/:
daemon:*:1:1:The devil himself:/:
bill:.2xvLVqGHJm8M:8:10:&amp; Joy,4156424948:/usr/bill:/bin/csh
ozalp:m5syt3.lB5LAE:40:10:&amp; Babaoglu,4156423806:/usr/ozalp:/bin/csh
sklower:8PYh/dUBQT9Ss:2:10:Keith &amp;,4156424972:/usr/staff/sklower:/bin/csh
kridle:4BkcEieEtjWXI:3:10:Bob &amp;,4156426744:/usr/staff/kridle:/bin/csh
kurt:olqH1vDqH38aw:4:10:&amp; Shoens,4156420572:/usr/staff/kurt:/bin/csh
schmidt:FH83PFo4z55cU:7:10:Eric &amp;,4156424951:/usr/staff/schmidt:/bin/csh
hpk:9ycwM8mmmcp4Q:9:10:Howard Katseff,2019495337:/usr/staff/hpk:/bin/csh
tbl:cBWEbG59spEmM:10:10:Tom London,2019492006:/usr/staff/tbl:
jfr:X.ZNnZrciWauE:11:10:John Reiser:/usr/staff/jfr:
mark:Pb1AmSpsVPG0Y:12:10:&amp; Horton,4156428311:/usr/staff/mark:/bin/csh
dmr:gfVwhuAMF0Trw:42:10:Dennis Ritchie:/usr/staff/dmr:
ken:ZghOT0eRm4U9s:52:10:&amp; Thompson:/usr/staff/ken:
sif:IIVxQSvq1V9R2:53:10:Stuart Feldman:/usr/staff/sif:
scj:IL2bmGECQJgbk:60:10:Steve Johnson:/usr/staff/scj:
pjw:N33.MCNcTh5Qw:61:10:Peter J. Weinberger,2015827214:/usr/staff/pjw:/bin/csh
bwk:ymVglQZjbWYDE:62:10:Brian W. Kernighan,2015826021:/usr/staff/bwk:
uucp:P0CHBwE/mB51k:66:10:UNIX-to-UNIX Copy:/usr/spool/uucp:/usr/lib/uucp/uucico
srb:c8UdIntIZCUIA:68:10:Steve Bourne,2015825829:/usr/staff/srb:
finger::199:199:The &amp; Program:/usr/ucb:/usr/ucb/finger
who::199:199:The &amp; Program:/usr/ucb:/bin/who
w::199:199:The &amp; Program:/usr/ucb:/usr/ucb/w
mckusick:AAZk9Aj5/Ue0E:201:10:Kirk &amp;,4156424948:/usr/staff/mckusick:/bin/csh
peter:Nc3IkFJyW2u7E:202:10:&amp; Kessler,4156424948:/usr/staff/peter:/bin/csh
henry:lj1vXnxTAPnDc:203:10:Robert &amp;,4156424948:/usr/staff/henry:/bin/csh
jkf:9ULn5cWTc0b9E:209:10:John Foderaro,4156424972:/usr/staff/jkf:/bin/csh
fateman:E9i8fWghn1p/I:300:10:Richard &amp;,4156421879:/usr/staff/fateman:/bin/csh
fabry:d9B17PTU2RTlM:305:10:Bob &amp;,4156422714:/usr/staff/fabry:/bin/csh
network:9EZLtSYjeEABE:501:50:*:/usr/net/network:/usr/net/network/nsh
tty::504:50::/:/bin/tty我</pre>
<p>（注，以前Unix是一个服务器，所有人都用一个终端到服务器上进行操作，于是，这个服务上的 <code>/etc/passwd</code> 下保存着所有的人的登录密码，能让所有的人都能读到，为了不让别人猜到，这个文件中的密码保存（第二列）被做过哈希处理）</p>
<p>这位程序员一看，这些个用户不就是<a href="https://en.wikipedia.org/wiki/Dennis_Ritchie" target="_blank" rel="noopener noreferrer">Dennis Ritchie</a>, <a href="https://en.wikipedia.org/wiki/Ken_Thompson">Ken Thompson</a>, <a href="https://en.wikipedia.org/wiki/Brian_Kernighan" target="_blank" rel="noopener noreferrer">Brian W. Kernighan</a>, <a href="https://en.wikipedia.org/wiki/Stephen_R._Bourne" target="_blank" rel="noopener noreferrer">Steve Bourne</a>, <a href="https://en.wikipedia.org/wiki/Bill_Joy" target="_blank" rel="noopener noreferrer">Bill Joy</a> 这些神人的密码吗？！于是，他想看看这些人用什么样的密码。考虑到当时的加密算法用的是基于DES的 <a href="https://minnie.tuhs.org/cgi-bin/utree.pl?file=V7/usr/man/man3/crypt.3">crypt(3)</a> 算法（这个算法今天还在用，像Perl/PHP/Python/Ruby都提供<code>crypt()</code> 函数），而且当时的密码最长只支持8个长度，所以，感觉还是很容易暴力破解的。</p>
<p>一般来说，暴力破解的这种hash密码的工具主要是用<a href="https://hashcat.net/" target="_blank" rel="noopener noreferrer">hashcat</a> 或 <a href="https://www.openwall.com/john/" target="_blank" rel="noopener noreferrer">john</a> ，很快，Leah 破解了大多数人的密码，因为大多数都使用的是比较弱的密码，比如： <a href="https://en.wikipedia.org/wiki/Brian_Kernighan" target="_blank" rel="noopener noreferrer">Brian W. Kernighan</a> （<code>bwk</code>）使用了 <code>/.,/.,</code> 这样的密码，而 <a href="https://en.wikipedia.org/wiki/Dennis_Ritchie" target="_blank" rel="noopener noreferrer">Dennis Ritchie</a> （<code>dmr</code>）则使用了 <code>dmac</code> 这样的密码。然后，在破解到 Ken Thompson的密码时，搞不定了，花了好几天穷举完了所有的小写字母+数字都没有找到。</p>
<p>因为这个<code>crypt</code>的算法也是Ken Thompson 和 Robert Morris 写的，他们在40年前就发现，原来的hash算法太快了，这样很容易被暴力穷举，于是在第七版的Unix（1979年发布），他们把算法改成DES的算法，就是要让这个算法变慢。详细地说，用户密码被截断为八个字符，每个字符仅被压缩为7位。这形成56位DES密钥。然后，该密钥用于加密全零位块，然后再次使用相同的密钥对密文进行加密，依此类推，总共进行了25次DES加密。感觉跟区块链的“挖矿”有点像。<strong>在最早的Unix计算机上，这个算法需要花了整整一秒钟的时间来计算密码哈希</strong>。</p>
<p>这几十年来，计算机的计算速度根据摩尔定律至少double了20次，所以，DES算法已经很容被攻击了，然而，对于Ken Thompson的密码，在2014年还是很不容易被破解的，因为，<strong>如果要加上所有的大小写字符数字和其它特殊字符，那么，在2014年，就算用最快的GPU来穷举所有的8位长度的密码，也需要花上至少2年以上的时间</strong>。</p>
<p>在2019年10月份，在 <a href="https://www.tuhs.org/">The Unix Heritage Society</a> 这个社区中，<a href="https://inbox.vuxu.org/tuhs/6dceffe228804a76de1e12f18d1fc0dc@inventati.org/" target="_blank" rel="noopener noreferrer">这个事又被人问起来</a>，说以前有个人破解这些密码，不知道有没有全破解出来了？于是Leah看到了，就回应说，那个人是我，但是还是没干出来……于是好些人进来留言。</p>
<p>5天后，2019年10月08日，一个来自澳大利亚的程序员Nigel Williams说，<a href="https://inbox.vuxu.org/tuhs/CACCFpdx_6oeyNkgH_5jgfxbxWbZ6VtOXQNKOsonHPF2=747ZOw@mail.gmail.com/" target="_blank" rel="noopener noreferrer">Ken的密码我破解出来了</a>，哈希串<code>ZghOT0eRm4U9s</code> 明文是 <code>p/q2-q4!</code>（果然是有数字有特殊字符），小伙说，我在 AMD Radeon Vega 64 的 GPU上运行了 <code>hashcat</code> 这个命令，干了我 4天多，每秒钟的“配速”是930MH/s （每秒钟9亿3千万次hash运算）。然后，<a href="https://inbox.vuxu.org/tuhs/CAG=a+rj8VcXjS-ftaj8P2_duLFSUpmNgB4-dYwnTsY_8g5WdEA@mail.gmail.com/" target="_blank" rel="noopener noreferrer">Ken Thompson 也留言到 “恭喜”</a> ，这样，Ken 的密码在40年后被破解了……</p>
<p>马上，就有人问到，这个密码是不是国际象棋的走棋？嗯，很像中国象棋中的“车五进一”，“马三退一”，这个密码中的 <code>p</code> 代表 <code>pawn</code> 小兵，从 <code>q2</code> 的位置走到 <code>q4</code>，这个看来是国际象棋中的开局进兵——用来做登录密码，非常合适。而且，Ken Thompson 在 Unix中写下的一个国际象棋的程序 <a href="https://en.wikipedia.org/wiki/Belle_(chess_machine)" target="_blank" rel="noopener noreferrer">Belle</a>，在1978年首次参加<a href="https://en.wikipedia.org/wiki/North_American_Computer_Chess_Championship">计算机协会的北美计算机国际象棋锦标赛</a>时，它获得了第一个冠军头衔，其搜索深度为八层。之后又赢得了四次冠军。1983年，它也成为第一台获得国际象棋“大师”称号的计算机。所以，Ken用这个做密码相当make sense!</p>
<p style="text-align: center;"><img decoding="async" loading="lazy" class="aligncenter size-full" src="https://coolshell.cn/wp-content/uploads/2019/11/ken.chess_.jpg" alt="" width="600" height="800" />Ken在贝尔实验室调程序（图片来源：<a href="https://spectrum.ieee.org/tech-history/silicon-revolution/in-1983-this-bell-labs-computer-was-the-first-machine-to-become-a-chess-master" target="_blank" rel="noopener noreferrer">IEEE SPECTRUM</a>）</p>
<p>当然，还有一个人的密码是所有人里最难破解的，这个人就是<a href="https://en.wikipedia.org/wiki/Bill_Joy" target="_blank" rel="noopener noreferrer">Bill Joy</a>，他最初作为加州大学伯克利分校的研究生，在校期间着手改进Unix 内核，并管理BSD发行版。他最著名的贡献是ex和vi编辑器以及C shell。在Sun公司成立6个月后，他正式成为公司的联合创始人，他在Sun公司的推动了NFS，SPARC处理器，以及Java语言。他还是一个风险投资人员。</p>
<p>在Ken的密被破解后两周（2019年10月19日），有人号称已经破解了Bill的密码，他在<a href="https://minnie.tuhs.org/pipermail/tuhs/2019-October/019124.html" target="_blank" rel="noopener noreferrer">邮件组中这样写到</a>：</p>
<blockquote><p>一开始，我使用了大小写字符和数字，8位长度来破解所有的组合，花了我6天的时间，失败了。然后，我开始尝试只用小写字母和控制字符，结果在40分钟内就破解了。但是因为Bill现健在，所以，只要bill同意他才公布这个密码。</p></blockquote>
<p>在密码里存控制字符？这脑洞，Ctrl+C么？破解者还说，他在一个有三个结点的DELL 的HPC集群上完成这个工作，每个结点包括两个 Tesla V100 nVidia GPU 的显卡，一共30720个CUDA核…… 关于这个显卡多少钱，你可以上网搜吧…… 相当于一块劳力士吧……（我估计这组机器平时是用来挖矿的……[狗头]）</p>
<p>好了，我们来看一下这个 <code>/etc/passwd</code> 中的这些人的密码是什么样的，<strong>但最主要的是向这些为人类做过巨大贡献的程序员科学家们致敬</strong>！</p>
<ul>
<li><strong><a href="https://en.wikipedia.org/wiki/Ken_Thompson" target="_blank" rel="noopener noreferrer">Ken Thompson</a></strong><br />
除了是Unix、B语言和Go语言作者之外，他还贡献过正则表达式，QED/ed编辑器，UTF-8编码定义，以及计算机国际象棋Belle……</p>
<table>
<tbody>
<tr>
<td colspan="1" rowspan="1">登录名</td>
<td colspan="1" rowspan="1"><b>哈希串</b></td>
<td colspan="1" rowspan="1"><b>密码</b></td>
</tr>
<tr>
<td colspan="1" rowspan="1"><code>ken</code></td>
<td colspan="1" rowspan="1"><code>ZghOT0eRm4U9s</code></td>
<td colspan="1" rowspan="1"><code>p/q2-q4!</code></td>
</tr>
</tbody>
</table>
</li>
<li><strong><a href="https://en.wikipedia.org/wiki/Dennis_Ritchie" target="_blank" rel="noopener noreferrer">Dennis Ritchie</a></strong><br />
Unix和C语言之父，与Ken于1983年获图灵奖，1990年美国国家海明奖章，于2011年去世。</p>
<table>
<tbody>
<tr>
<td colspan="1" rowspan="1">登录名</td>
<td colspan="1" rowspan="1"><b>哈希串</b></td>
<td colspan="1" rowspan="1"><b>密码</b></td>
</tr>
<tr>
<td colspan="1" rowspan="1"><code>dmr</code></td>
<td colspan="1" rowspan="1"><code>gfVwhuAMF0Trw</code></td>
<td colspan="1" rowspan="1"><code>dmac</code></td>
</tr>
</tbody>
</table>
</li>
<li><strong><a href="https://en.wikipedia.org/wiki/Brian_Kernighan" target="_blank" rel="noopener noreferrer">Brian W. Kernighan</a></strong><br />
AWK的作者，是AWK中的“K”，也是与Dennis写的K&amp;C的C语言编程书中的“K”，他还编写了很多Unix的其它程序，如：<code>ditroff</code>，而且，设计了著名的<a href="https://en.wikipedia.org/wiki/Heuristic" target="_blank" rel="noopener noreferrer">启发式算法</a>。</p>
<table>
<tbody>
<tr>
<td colspan="1" rowspan="1">登录名</td>
<td colspan="1" rowspan="1"><b>哈希串</b></td>
<td colspan="1" rowspan="1"><b>密码</b></td>
</tr>
<tr>
<td colspan="1" rowspan="1"><code>bwk</code></td>
<td colspan="1" rowspan="1"><code>ymVglQZjbWYDE</code></td>
<td colspan="1" rowspan="1"><code>/.,/.,</code></td>
</tr>
</tbody>
</table>
</li>
<li><strong><a href="https://en.wikipedia.org/wiki/Stephen_R._Bourne" target="_blank" rel="noopener noreferrer">Stephen R. Bourne</a></strong><br />
Bourne shell（<code>sh</code>）的作者，Unix Shell作者，同时也是Unix调试器的作者。</p>
<table>
<tbody>
<tr>
<td colspan="1" rowspan="1">登录名</td>
<td colspan="1" rowspan="1"><b>哈希串</b></td>
<td colspan="1" rowspan="1"><b>密码</b></td>
</tr>
<tr>
<td colspan="1" rowspan="1"><code>srb</code></td>
<td colspan="1" rowspan="1"><code>c8UdIntIZCUIA</code></td>
<td colspan="1" rowspan="1"><code>bourne</code></td>
</tr>
</tbody>
</table>
</li>
<li><strong><a href="https://en.wikipedia.org/wiki/Eric_Schmidt" target="_blank" rel="noopener noreferrer">Eric Schmidt</a></strong><br />
你可能知道他是Google的CEO，苹果的董事，但是你可能不知道，他当年是是贝尔实施室的实习生，他对Unix的词法分析器 Lex 进行为了完全的重写。他的密码是中的wendy应该是他的妻子。</p>
<table>
<tbody>
<tr>
<td colspan="1" rowspan="1">登录名</td>
<td colspan="1" rowspan="1"><b>哈希串</b></td>
<td colspan="1" rowspan="1"><b>密码</b></td>
</tr>
<tr>
<td colspan="1" rowspan="1"><code>schmidt</code></td>
<td colspan="1" rowspan="1"><code>FH83PFo4z55cU</code></td>
<td colspan="1" rowspan="1"><code>wendy!!!</code></td>
</tr>
</tbody>
</table>
</li>
<li><strong><a href="https://en.wikipedia.org/wiki/Stuart_Feldman" target="_blank" rel="noopener noreferrer">Stuart Feldman</a></strong><br />
他除了是Unix系统小组的成员，他还是第一个Fortran 77 编译器的作者，也是 <code>make</code> 的作者。他还是楼上Shmidt慈善基金会的科学负责人，在Google/IBM Research任过职，也担任过ACM的主席。</p>
<table>
<tbody>
<tr>
<td colspan="1" rowspan="1">登录名</td>
<td colspan="1" rowspan="1"><b>哈希串</b></td>
<td colspan="1" rowspan="1"><b>密码</b></td>
</tr>
<tr>
<td colspan="1" rowspan="1"><code>sif</code></td>
<td colspan="1" rowspan="1"><code>IIVxQSvq1V9R2</code></td>
<td colspan="1" rowspan="1"><code>axolotl</code></td>
</tr>
</tbody>
</table>
</li>
<li><strong><a href="https://en.wikipedia.org/wiki/Mary_Ann_Horton" target="_blank" rel="noopener noreferrer">Mark Horton</a></strong><br />
Unix贡献者，包括vi和curses，后来变性为女性，新的名字叫Mary Ann Horton。原来的照片在<a href="http://www.ugu.com/sui/ugu/show?I=info.Mark_R._Horton" target="_blank" rel="noopener noreferrer">Unix Guru Universe</a></p>
<table>
<tbody>
<tr>
<td colspan="1" rowspan="1">登录名</td>
<td colspan="1" rowspan="1"><b>哈希串</b></td>
<td colspan="1" rowspan="1"><b>密码</b></td>
</tr>
<tr>
<td colspan="1" rowspan="1"><code>mark</code></td>
<td colspan="1" rowspan="1"><code>Pb1AmSpsVPG0Y</code></td>
<td colspan="1" rowspan="1"><code>uio</code></td>
</tr>
</tbody>
</table>
</li>
<li><strong><a href="https://en.wikipedia.org/wiki/Marshall_Kirk_McKusick" target="_blank" rel="noopener noreferrer">Kirk McKusick</a></strong><br />
BSD贡献者，主要负责文件系统UFS以及fsck命令，同时也是<code>gprof</code>的贡献者，公开的同性恋者。</p>
<table>
<tbody>
<tr>
<td colspan="1" rowspan="1">登录名</td>
<td colspan="1" rowspan="1"><b>哈希串</b></td>
<td colspan="1" rowspan="1"><b>密码</b></td>
</tr>
<tr>
<td colspan="1" rowspan="1"><code>mckusick</code></td>
<td colspan="1" rowspan="1"><code>AAZk9Aj5/Ue0E</code></td>
<td colspan="1" rowspan="1"><code>foobar</code></td>
</tr>
</tbody>
</table>
</li>
<li><strong><a href="https://en.wikipedia.org/wiki/Richard_Fateman" target="_blank" rel="noopener noreferrer">Richard Fateman</a></strong><br />
他在伯克利的VAX UNIX系统的开发工作中发挥了重要作用，以及开发了<a href="https://en.wikipedia.org/wiki/Franz_Lisp" target="_blank" rel="noopener noreferrer"> Franz Lisp</a>。</p>
<table>
<tbody>
<tr>
<td colspan="1" rowspan="1">登录名</td>
<td colspan="1" rowspan="1"><b>哈希串</b></td>
<td colspan="1" rowspan="1"><b>密码</b></td>
</tr>
<tr>
<td colspan="1" rowspan="1"><code>fateman</code></td>
<td colspan="1" rowspan="1"><code>E9i8fWghn1p/I</code></td>
<td colspan="1" rowspan="1"><code>apr1744</code></td>
</tr>
</tbody>
</table>
</li>
<li><strong>Peter Kessler</strong><br />
这位老兄能在网上查到的资料基本没有，可以查到他是 <code>gprof</code> 的贡献者，以及有名字的<a href="https://web.eecs.umich.edu/~weimerw/2009-4610/reading/graham-gprof.pdf" target="_blank" rel="noopener noreferrer">gprof的一篇论文</a></p>
<table>
<tbody>
<tr>
<td colspan="1" rowspan="1">登录名</td>
<td colspan="1" rowspan="1"><b>哈希串</b></td>
<td colspan="1" rowspan="1"><b>密码</b></td>
</tr>
<tr>
<td colspan="1" rowspan="1"><code>peter</code></td>
<td colspan="1" rowspan="1"><code>Nc3IkFJyW2u7E</code></td>
<td colspan="1" rowspan="1"><code>...hello</code></td>
</tr>
</tbody>
</table>
</li>
<li><strong>Kurt Shoens</strong><br />
BSD电子邮件开发者。Unix早期版本中使用 <code>uux</code> 和 <code>sendmail</code> 来进行远程消息传递，1978年，Kurt为Unix编写了一个邮件用户代理 Berkeley Mail。相关的历史可以参看<a href="http://heirloom.sourceforge.net/mailx_history.html" target="_blank" rel="noopener noreferrer">这篇文章</a>。</p>
<table>
<tbody>
<tr>
<td colspan="1" rowspan="1">登录名</td>
<td colspan="1" rowspan="1"><b>哈希串</b></td>
<td colspan="1" rowspan="1"><b>密码</b></td>
</tr>
<tr>
<td colspan="1" rowspan="1"><code>kurt</code></td>
<td colspan="1" rowspan="1"><code>olqH1vDqH38aw</code></td>
<td colspan="1" rowspan="1"><code>sacristy</code></td>
</tr>
</tbody>
</table>
</li>
<li><strong><a href="https://franz.com/about/press_room/foderaro_2-2-2015.lhtml" target="_blank" rel="noopener noreferrer">John Foderaro</a></strong><br />
他为Berkeley的Lisp语言编写原始的编译器，Lisp语言是一种类似于数据代数的语言，在计算机历史上有和C语言一样的作用。后来他成立了Franz公司，主要开发和部署图形搜索解决方案。</p>
<table>
<tbody>
<tr>
<td colspan="1" rowspan="1">登录名</td>
<td colspan="1" rowspan="1"><b>哈希串</b></td>
<td colspan="1" rowspan="1"><b>密码</b></td>
</tr>
<tr>
<td colspan="1" rowspan="1"><code>jkf</code></td>
<td colspan="1" rowspan="1"><code>9ULn5cWTc0b9E</code></td>
<td colspan="1" rowspan="1"><code>sherril.</code></td>
</tr>
</tbody>
</table>
</li>
<li><strong><a href="https://en.wikipedia.org/wiki/Peter_J._Weinberger" target="_blank" rel="noopener noreferrer">Peter J. Weinberger</a></strong><br />
他就是AWK中的那个“W”，同时也是Fortan编译器f77的贡献者，后来是<a title="" href="https://en.wikipedia.org/wiki/Renaissance_Technologies">Renaissance Technologies</a> （一家对冲基金）的CTO，现在在Google工作，</p>
<table>
<tbody>
<tr>
<td colspan="1" rowspan="1">登录名</td>
<td colspan="1" rowspan="1"><b>哈希串</b></td>
<td colspan="1" rowspan="1"><b>密码</b></td>
</tr>
<tr>
<td colspan="1" rowspan="1"><code>pjw</code></td>
<td colspan="1" rowspan="1"><code>N33.MCNcTh5Qw</code></td>
<td colspan="1" rowspan="1"><code>uucpuucp</code></td>
</tr>
</tbody>
</table>
</li>
<li><strong>John Reiser</strong><br />
他主要工作是将Unix和C移植到了DEC VAX上，这个机器在学术界相当流行（陈皓注：我在1994年上大学的时候，就是在这个机器上学习的C语言）。这扩大了Unix和C的影响力。</p>
<table>
<tbody>
<tr>
<td colspan="1" rowspan="1">登录名</td>
<td colspan="1" rowspan="1"><b>哈希串</b></td>
<td colspan="1" rowspan="1"><b>密码</b></td>
</tr>
<tr>
<td colspan="1" rowspan="1"><code>jfr</code></td>
<td colspan="1" rowspan="1"><code>X.ZNnZrciWauE</code></td>
<td colspan="1" rowspan="1"><code>5%ghj</code></td>
</tr>
</tbody>
</table>
</li>
<li><strong><a href="https://en.wikipedia.org/wiki/Stephen_C._Johnson" target="_blank" rel="noopener noreferrer">Steve Johnson</a></strong><br />
曾在贝尔实验室和AT＆T工作近20年。他以Yacc，Lint，spell和Portable C编译器而闻名。后来他去了硅谷，加入了一些创业公司，主要从事编译器的工作，以及2D和3D图形，大规模并行系统和嵌入式系统的开发工作。现在他在Wave Computing从事机器学习的工作。</p>
<table>
<tbody>
<tr>
<td colspan="1" rowspan="1">登录名</td>
<td colspan="1" rowspan="1"><b>哈希串</b></td>
<td colspan="1" rowspan="1"><b>密码</b></td>
</tr>
<tr>
<td colspan="1" rowspan="1"><code>scj</code></td>
<td colspan="1" rowspan="1"><code>IL2bmGECQJgbk</code></td>
<td colspan="1" rowspan="1"><code>pdq;dq</code></td>
</tr>
</tbody>
</table>
</li>
<li><strong>Bob Kridle</strong><br />
这位老兄的资料在没有太多，只能在 <a href="https://www.oreilly.com/openbook/opensources/book/kirkmck.html_original" target="_blank" rel="noopener noreferrer">Berkeley Unix 20 年</a> 上看到他跟Ken Thompson混过一段时间。</p>
<table>
<tbody>
<tr>
<td colspan="1" rowspan="1">登录名</td>
<td colspan="1" rowspan="1"><b>哈希串</b></td>
<td colspan="1" rowspan="1"><b>密码</b></td>
</tr>
<tr>
<td colspan="1" rowspan="1"><code>kridle</code></td>
<td colspan="1" rowspan="1"><code>4BkcEieEtjWXI</code></td>
<td colspan="1" rowspan="1"><code>jilland1</code></td>
</tr>
</tbody>
</table>
</li>
<li><strong><a href="https://people.eecs.berkeley.edu/~sklower/" target="_blank" rel="noopener noreferrer">Keith Sklower</a></strong><br />
BSD 的一个程序员。从他的主页上可以看到他目前在Berkeley大学，信息分析师，主要研究一些网络通信相关的技术。</p>
<table>
<tbody>
<tr>
<td colspan="1" rowspan="1">登录名</td>
<td colspan="1" rowspan="1"><b>哈希串</b></td>
<td colspan="1" rowspan="1"><b>密码</b></td>
</tr>
<tr>
<td colspan="1" rowspan="1"><code>sklower</code></td>
<td colspan="1" rowspan="1"><code>8PYh/dUBQT9Ss</code></td>
<td colspan="1" rowspan="1"><code>theik!!!</code></td>
</tr>
</tbody>
</table>
</li>
<li><strong>Robert Henry</strong><br />
网上的资料不多，只在<a href="https://www.tuhs.org/Archive/Documentation/Books/Life_with_Unix.pdf" target="_blank" rel="noopener noreferrer">Life with Unix</a>这本电子书中查到，他写了 <code>error</code></p>
<table>
<tbody>
<tr>
<td colspan="1" rowspan="1">登录名</td>
<td colspan="1" rowspan="1"><b>哈希串</b></td>
<td colspan="1" rowspan="1"><b>密码</b></td>
</tr>
<tr>
<td colspan="1" rowspan="1"><code>henry</code></td>
<td colspan="1" rowspan="1"><code>lj1vXnxTAPnDc</code></td>
<td colspan="1" rowspan="1"><code>sn74193n</code></td>
</tr>
</tbody>
</table>
</li>
<li><strong>Howard Katseff</strong><br />
网上的资料不多，只在<a href="https://www.tuhs.org/Archive/Documentation/Books/Life_with_Unix.pdf" target="_blank" rel="noopener noreferrer">Life with Unix</a>这本电子书中查到，他写了 <code>sdb</code> 和 <code>last</code></p>
<table>
<tbody>
<tr>
<td colspan="1" rowspan="1">登录名</td>
<td colspan="1" rowspan="1"><b>哈希串</b></td>
<td colspan="1" rowspan="1"><b>密码</b></td>
</tr>
<tr>
<td colspan="1" rowspan="1"><code>hpk</code></td>
<td colspan="1" rowspan="1"><code>9ycwM8mmmcp4Q</code></td>
<td colspan="1" rowspan="1"><code>graduat;</code></td>
</tr>
</tbody>
</table>
</li>
<li><strong><a href="https://en.wikipedia.org/wiki/%C3%96zalp_Babao%C4%9Flu" target="_blank" rel="noopener noreferrer">Özalp Babaoğlu</a></strong><br />
土耳其计算机科学家，1981年在Berkeley担任 BSD Unix的首席设计师，曾经与Sun的创造人Bill Joy在BSD上实现了虚拟内存。</p>
<table>
<tbody>
<tr>
<td colspan="1" rowspan="1">登录名</td>
<td colspan="1" rowspan="1"><b>哈希串</b></td>
<td colspan="1" rowspan="1"><b>密码</b></td>
</tr>
<tr>
<td colspan="1" rowspan="1"><code>ozalp</code></td>
<td colspan="1" rowspan="1"><code>m5syt3.lB5LAE</code></td>
<td colspan="1" rowspan="1"><code>12ucdort</code></td>
</tr>
</tbody>
</table>
</li>
<li><strong><a href="https://en.wikipedia.org/wiki/Bob_Fabry" target="_blank" rel="noopener noreferrer">Bob Fabry</a></strong><br />
他主要推动美国国防部高级研究计划局DARPA采用了Unix系统</p>
<table>
<tbody>
<tr>
<td colspan="1" rowspan="1">登录名</td>
<td colspan="1" rowspan="1"><b>哈希串</b></td>
<td colspan="1" rowspan="1"><b>密码</b></td>
</tr>
<tr>
<td colspan="1" rowspan="1"><code>fabry</code></td>
<td colspan="1" rowspan="1"><code>d9B17PTU2RTlM</code></td>
<td colspan="1" rowspan="1"><code>561cml..</code></td>
</tr>
</tbody>
</table>
</li>
<li><strong>Tom London</strong><br />
他和John Reiser在把Unix移植到了VAX-11机上。</p>
<table>
<tbody>
<tr>
<td colspan="1" rowspan="1">登录名</td>
<td colspan="1" rowspan="1"><b>哈希串</b></td>
<td colspan="1" rowspan="1"><b>密码</b></td>
</tr>
<tr>
<td colspan="1" rowspan="1"><code>tbl</code></td>
<td colspan="1" rowspan="1"><code>cBWEbG59spEmM</code></td>
<td colspan="1" rowspan="1"><code>..pnn521</code></td>
</tr>
</tbody>
</table>
</li>
</ul>
<p>最后，再首尾呼应一下，在我的技术生涯中，Unix文化对我个人的技术观影响是非常大的，<strong>我个人认为 Unix 就像摇滚乐一样，上世纪60年代-80年代，是整个人类最经典最光亮的时代，值得我们每个人向那个时代的人和事致敬！</strong></p>
<p>————————————————————————</p>
<p>P.S.</p>
<p>你可以浏览 Github 的 <a href="https://github.com/dspinellis/unix-history-repo/tree/BSD-3-Snapshot-Development" target="_blank" rel="noopener noreferrer">unix-history-repo</a> 目录（注：本文给的这个链接不在master分支上），这个repo是40年前的代码，涵盖了从1970年创建时的2.5万行内核和26条命令到2017年为止广泛使用的2700万行系统。1.1GB的存储库包含大约一百万次提交和两千多次合并。通过<a href="http://www.dmst.aueb.gr/dds/pubs/jrnl/2016-EMPSE-unix-history/html/unix-history.html" target="_blank" rel="noopener noreferrer">这个链接</a>你可以了解一下这个代码的历史！</p>
<p>下载这些代码需要你的1.5GB的硬盘空间，你可以查看各个大神写的代码，包括 Ken Thompson 和 Dennis的，以及相关的注释。</p>
<p>根据这些，你还可以找到 Ken Thompson的 Github账号 <a href="https://github.com/ken" target="_blank" rel="noopener noreferrer">https://github.com/ken</a> 以及别人为dmr建的github帐号 <a href="https://github.com/dmr-1941-2011">https://github.com/dmr-1941-2011</a></p>
<p>P.S.S</p>
<p>下面是一些和Unix相关的维基百科资料</p>
<ul>
<li><a href="https://en.wikipedia.org/wiki/History_of_Unix" target="_blank" rel="nofollow noopener noreferrer">History of Unix</a></li>
<li><a href="https://en.wikipedia.org/wiki/List_of_Unix_systems" target="_blank" rel="nofollow noopener noreferrer">List of Unix systems</a></li>
<li><a href="https://en.wikipedia.org/wiki/List_of_Unix_commands" target="_blank" rel="nofollow noopener noreferrer">List of Unix commands</a></li>
<li><a href="https://en.wikipedia.org/wiki/List_of_Unix_daemons" target="_blank" rel="nofollow noopener noreferrer">List of Unix daemons</a></li>
<li><a href="https://en.wikipedia.org/wiki/Research_Unix" target="_blank" rel="nofollow noopener noreferrer">Research Unix</a></li>
<li><a href="http://en.wikipedia.org/wiki/BSD_Unix" target="_blank" rel="nofollow noopener noreferrer">Berkeley Software Distribution</a></li>
<li><a href="https://en.wikipedia.org/wiki/Unix_philosophy" target="_blank" rel="nofollow noopener noreferrer">Unix philosophy</a></li>
</ul>
<p>还有Unix的社区：TUHS: The Unix Heritage Society &#8211; <a href="http://minnie.tuhs.org/cgi-bin/utree.pl" rel="nofollow">The Unix Tree</a></p>
<p>（全文完）<!--



<p align="center"><a href= target=_blank><img decoding="async" src=""></a></p>





<p align="center"><img decoding="async" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg"> <img decoding="async" loading="lazy" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg" width="300" height="300"> <br />关注CoolShell微信公众账号和微信小程序</p>

 

--></p>
<div style="margin-top: 15px; font-size: 16px;color: #cc0000;">
<p align="center"><strong>（转载本站文章请注明作者和出处 <a href="https://coolshell.cn/">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>
</div>

<div class="wp_rp_wrap  wp_rp_vertical_m" id="wp_rp_first"><div class="wp_rp_content"><h3 class="related_post_title">相关文章</h3><ul class="related_post wp_rp"><li ><a href="https://coolshell.cn/articles/2322.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2010/04/o_unixrichiethompson-150x150.jpg" alt="Unix传奇(上篇)" width="150" height="150" /></a><a href="https://coolshell.cn/articles/2322.html" class="wp_rp_title">Unix传奇(上篇)</a></li><li ><a href="https://coolshell.cn/articles/9410.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2013/04/figure1-150x150.gif" alt="Unix考古记：一个“遗失”的shell" width="150" height="150" /></a><a href="https://coolshell.cn/articles/9410.html" class="wp_rp_title">Unix考古记：一个“遗失”的shell</a></li><li ><a href="https://coolshell.cn/articles/1761.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2009/11/spell_it_with_e-150x150.jpg" alt="Go语言源码的一个改动" width="150" height="150" /></a><a href="https://coolshell.cn/articles/1761.html" class="wp_rp_title">Go语言源码的一个改动</a></li><li ><a href="https://coolshell.cn/articles/18360.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg" alt="程序员练级攻略（2018)  与我的专栏" width="150" height="150" /></a><a href="https://coolshell.cn/articles/18360.html" class="wp_rp_title">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href="https://coolshell.cn/articles/17998.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2017/07/systemd-1-150x150.jpeg" alt="Linux PID 1 和 Systemd" width="150" height="150" /></a><a href="https://coolshell.cn/articles/17998.html" class="wp_rp_title">Linux PID 1 和 Systemd</a></li><li ><a href="https://coolshell.cn/articles/12103.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2014/11/tux-fork-150x150.gif" alt="vfork 挂掉的一个问题" width="150" height="150" /></a><a href="https://coolshell.cn/articles/12103.html" class="wp_rp_title">vfork 挂掉的一个问题</a></li></ul></div></div>The post <a href="https://coolshell.cn/articles/19996.html">Unix 50 年：Ken Thompson 的密码</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></content:encoded>
					
					<wfw:commentRss>https://coolshell.cn/articles/19996.html/feed</wfw:commentRss>
			<slash:comments>30</slash:comments>
		
		
			</item>
		<item>
		<title>程序员练级攻略（2018)  与我的专栏</title>
		<link>https://coolshell.cn/articles/18360.html</link>
					<comments>https://coolshell.cn/articles/18360.html#comments</comments>
		
		<dc:creator><![CDATA[陈皓]]></dc:creator>
		<pubDate>Tue, 29 May 2018 04:38:23 +0000</pubDate>
				<category><![CDATA[技术读物]]></category>
		<category><![CDATA[杂项资源]]></category>
		<category><![CDATA[C++]]></category>
		<category><![CDATA[Java]]></category>
		<category><![CDATA[Javascript]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[Programmer]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[React]]></category>
		<category><![CDATA[Unix]]></category>
		<category><![CDATA[Web]]></category>
		<category><![CDATA[分布式]]></category>
		<category><![CDATA[程序员]]></category>
		<guid isPermaLink="false">https://coolshell.cn/?p=18360</guid>

					<description><![CDATA[<p>写极客时间8个月了，我的专栏现在有一定的积累了，今天想自己推荐一下。因为最新的系列《程序员练级攻略（2018）版》正在连载中，而且文章积累量到了我也有比较足的自...</p>
<p class="read-more"><a class="btn btn-default" href="https://coolshell.cn/articles/18360.html"> Read More<span class="screen-reader-text">  Read More</span></a></p>
The post <a href="https://coolshell.cn/articles/18360.html">程序员练级攻略（2018)  与我的专栏</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></description>
										<content:encoded><![CDATA[<p><script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158"
     crossorigin="anonymous"></script><img decoding="async" loading="lazy" class="alignright size-medium" src="https://coolshell.cn/wp-content/uploads/2018/05/专栏-300x262.jpg" alt="" width="300" height="262" />写极客时间8个月了，我的专栏现在有一定的积累了，今天想自己推荐一下。因为最新的系列《程序员练级攻略（2018）版》正在连载中，而且文章积累量到了我也有比较足的自信向大家推荐我的这个专栏了。推荐就从最新的这一系统的文章开始。</p>
<p>2011年，我在 <a href="https://coolshell.cn/">CoolShell</a> 上发表了 《<a href="https://coolshell.cn/articles/4990.html">程序员技术练级攻略</a>》一文，得到了很多人的好评（转载的不算，在我的网站上都有近1000W的访问量了）。并且陆续收到了一些人的反馈，说跟着这篇文章找到了不错的工作。几年过去，也收到了好些邮件和私信，希望我把这篇文章更新一下，因为他们觉得有点落伍了。是的，<strong>老实说，抛开这几年技术的更新迭代不说，那篇文章写得也不算特别系统，同时标准也有点低，当时是给一个想要入门的朋友写的，所以，非常有必要从头更新一下《程序员练级攻略》这一主题</strong>。</p>
<p>目前，我在我极客时间的专栏上更新《程序员练级攻略（2018版）》。升级版的《程序员练级攻略》会比Coolshell上的内容更多，也更专业。这篇文章有【入门篇】、【修养篇】、【专业基础篇】、【软件设计篇】、【高手成长篇】五大篇章，它们会帮助你从零开始，一步步地，系统地，从陌生到熟悉，到理解掌握，从编码到设计再到架构，从码农到程序员再到工程师再到架构师的一步一步进阶，完成从普通到精通到卓越的完美转身……</p>
<p>这篇文章是我写得最累也是最痛苦的文章，原因如下：</p>
<ul>
<li> <strong>学习路径的梳理</strong>。这是一份计算编程相关知识地图，也是一份成长和学习路径。所以有太多的推敲了，知识的路径，体，地图……这让我费了很多工夫，感觉像在编写一本教材一样，即不能太高大上，也不能误人子弟。</li>
<li><strong>新旧知识的取舍。</strong>另外，因为我的成长经历中很多技术都成了过去时，所以对于新时代的程序员应该学习新的技术，然后，很多基础技术在今天依然管用，所以，在这点上，哪些要那些不要，也花了我很多的工夫。</li>
<li><strong>文章书籍的推荐</strong>。为了推荐最好的学习资料和资源，老实说，我几乎翻遍了整个互联网，进行了大量的阅读和比较。这个过程让我也受益非浅。一开始，这篇文章的大小居然在500K左右，太多的信息就是没有信息，所以在信息的筛选上我花费了很多的工夫，删掉了60%的内容。但是，依然很宠大。</li>
</ul>
<p><strong>总之，你一定会被这篇文章的内容所吓到的，是的，我就是故意这样做的，因为，这本来就没有什么捷径，也不可能速成，很多知识都是硬骨头，你只能一口一口的啃，我故意这样做就是为了让你不要有“速成”的幻想，也可以轻而一举的吓退那些不想用功不想努力的人</strong>。</p>
<p>但是，我们也要知道《易经》有云：“<strong>取法其上，得乎其中，取法其中，得乎其下，取法其下，法不得也</strong>”。所以，我这里会给你立个比较高标准，你要努力达到，相信我，就算是达不到，也会比你一开始期望的要高很多……</p>
<p>下面是这份练级攻略的目录，目前只在极客时间上发布，你需要付费阅读（在本文最后有相关的二维码）。</p>
<p><span id="more-18360"></span></p>
<p><img decoding="async" loading="lazy" class="alignnone size-full aligncenter" src="https://coolshell.cn/wp-content/uploads/2018/05/程序员练级攻略.png" alt="" width="290" height="1937" /></p>
<p>&nbsp;</p>
<p>那么，除程序员练级攻略外，我还写了哪些内容？下面是迄今为止我所有的文章的目录。你可以在下面看一下相关的目录。这也算是我开收费专栏来8个月给大家的一份答卷吧。我也没有想到，我居然写了这么多的文章，而且对很多人都很有用。</p>
<p>首先是个人成长和经验之谈的东西，在这里的文章还没有完全更新完，未来要更新什么我也不清楚，但是可以呈现出来的内容和方向如下所示，供你参考。对于个人成长中的内容，都是我多年来的心得和体会，从读者的反馈来看是非常不错的，你一定要要阅读的。</p>
<p><img decoding="async" loading="lazy" class="alignnone size-large aligncenter" src="https://coolshell.cn/wp-content/uploads/2018/05/个人成长和经验之谈-319x1024.png" alt="" width="319" height="1024" /></p>
<p>分布式系统架构，我一共出了两个系列，一个是分布式系统架构的本质，另一个是设计模式。前者偏概念，后者偏技术。这里旨在让你看到整个分布式系统设计的一个非常系统的蓝图，但是因为在手机端上，不可能写得非常细，所以，会缺失一些细节，这些细节我是故意缺失的，主要是有几方面的原因，</p>
<ul>
<li>一方面，这是为了阅读的效果，手机上的文章不过长，所以，不能有太多的细节。</li>
<li>另一方面，也是是想留给大家自行学习，而不是一定要我把饭喂到你的嘴里，你才能吃得着。<strong>学习不只是为要答案，而是学方法</strong></li>
<li>最后是我的私心，因为我也在创业，所以，技术细节上东西正是我在做的产品，所以，如果你想了解得更细，你需要和我有更商业合作。</li>
</ul>
<p><img decoding="async" loading="lazy" class="alignnone size-full aligncenter" src="https://coolshell.cn/wp-content/uploads/2018/05/分布式架构的本质.png" alt="" width="321" height="689" /></p>
<p>&nbsp;</p>
<p><img decoding="async" loading="lazy" class="alignnone  size-full aligncenter" src="https://coolshell.cn/wp-content/uploads/2018/05/分布式架构设计模式-弹力篇.png" alt="" width="331" height="1065" /></p>
<p><img decoding="async" loading="lazy" class="alignnone  size-full aligncenter" src="https://coolshell.cn/wp-content/uploads/2018/05/分布式架构设计模式-管理篇.png" alt="" width="353" height="669" /></p>
<p><img decoding="async" loading="lazy" class="alignnone  size-full aligncenter" src="https://coolshell.cn/wp-content/uploads/2018/05/分布式架构设计模式-性能篇.png" alt="" width="328" height="592" /></p>
<p>区块链的技术专栏本来不在我的写作计划中的，但是因为来问我这方面的技术人太多了，所以，就被问了一系列的文章，这里的文章除了一些技术上的科普，同样有有很多我的观点，你不但可以学到技术，还可以了解一些金融知识和相关的逻辑，我个人觉得这篇文章是让你有独立思考的文章。</p>
<p><img decoding="async" loading="lazy" class=" size-full alignnone aligncenter" src="https://coolshell.cn/wp-content/uploads/2018/05/区块链技术.png" alt="" width="304" height="771" /></p>
<p>我的专栏还在继续，接下来还有一个系列的文章——《从技术到管理》，欢迎关注，也欢迎扫码订阅。</p>
<p><strong>最后友情提示一下：在手机上学习并不是最好的学习方式，也不要在我的专栏上进行学习，把我的专栏当成一个你的助手，当成一个向导，当成一个跳板，真正的学习还是要在线下，专心的，系统地、有讨论地、不断实践地学习，这点希望大家切记！</strong></p>
<p>&nbsp;</p>
<p><img decoding="async" loading="lazy" class="aligncenter size-full " src="https://coolshell.cn/wp-content/uploads/2018/05/专栏.jpg" alt="" width="665" height="580" /></p>
<p>（全文完）<!--



<p align="center"><a href= target=_blank><img decoding="async" src=""></a></p>





<p align="center"><img decoding="async" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg"> <img decoding="async" loading="lazy" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg" width="300" height="300"> <br />关注CoolShell微信公众账号和微信小程序</p>

 

--></p>
<div style="margin-top: 15px; font-size: 16px;color: #cc0000;">
<p align="center"><strong>（转载本站文章请注明作者和出处 <a href="https://coolshell.cn/">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>
</div>

<div class="wp_rp_wrap  wp_rp_vertical_m" ><div class="wp_rp_content"><h3 class="related_post_title">相关文章</h3><ul class="related_post wp_rp"><li ><a href="https://coolshell.cn/articles/4990.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2011/07/programmer-150x150.png" alt="程序员技术练级攻略" width="150" height="150" /></a><a href="https://coolshell.cn/articles/4990.html" class="wp_rp_title">程序员技术练级攻略</a></li><li ><a href="https://coolshell.cn/articles/8088.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg" alt="对技术的态度" width="150" height="150" /></a><a href="https://coolshell.cn/articles/8088.html" class="wp_rp_title">对技术的态度</a></li><li ><a href="https://coolshell.cn/articles/4102.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/25.jpg" alt="如何学好C语言" width="150" height="150" /></a><a href="https://coolshell.cn/articles/4102.html" class="wp_rp_title">如何学好C语言</a></li><li ><a href="https://coolshell.cn/articles/4758.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg" alt="如何写出无法维护的代码" width="150" height="150" /></a><a href="https://coolshell.cn/articles/4758.html" class="wp_rp_title">如何写出无法维护的代码</a></li><li ><a href="https://coolshell.cn/articles/1992.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2009/12/language-fanboys-150x150.jpg" alt="程序员眼中的编程语言" width="150" height="150" /></a><a href="https://coolshell.cn/articles/1992.html" class="wp_rp_title">程序员眼中的编程语言</a></li><li ><a href="https://coolshell.cn/articles/19219.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2019/03/linux.ninja_-150x150.png" alt="打造高效的工作环境 &#8211; Shell 篇" width="150" height="150" /></a><a href="https://coolshell.cn/articles/19219.html" class="wp_rp_title">打造高效的工作环境 &#8211; Shell 篇</a></li></ul></div></div>The post <a href="https://coolshell.cn/articles/18360.html">程序员练级攻略（2018)  与我的专栏</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></content:encoded>
					
					<wfw:commentRss>https://coolshell.cn/articles/18360.html/feed</wfw:commentRss>
			<slash:comments>62</slash:comments>
		
		
			</item>
		<item>
		<title>Linux PID 1 和 Systemd</title>
		<link>https://coolshell.cn/articles/17998.html</link>
					<comments>https://coolshell.cn/articles/17998.html#comments</comments>
		
		<dc:creator><![CDATA[陈皓]]></dc:creator>
		<pubDate>Sun, 16 Jul 2017 13:40:55 +0000</pubDate>
				<category><![CDATA[Unix/Linux]]></category>
		<category><![CDATA[操作系统]]></category>
		<category><![CDATA[程序设计]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[Systemd]]></category>
		<category><![CDATA[Unix]]></category>
		<category><![CDATA[Upstart]]></category>
		<guid isPermaLink="false">http://coolshell.cn/?p=17998</guid>

					<description><![CDATA[<p>要说清 Systemd，得先从Linux操作系统的启动说起。Linux 操作系统的启动首先从 BIOS 开始，然后由 Boot Loader 载入内核，并初始化...</p>
<p class="read-more"><a class="btn btn-default" href="https://coolshell.cn/articles/17998.html"> Read More<span class="screen-reader-text">  Read More</span></a></p>
The post <a href="https://coolshell.cn/articles/17998.html">Linux PID 1 和 Systemd</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></description>
										<content:encoded><![CDATA[<p><script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158"
     crossorigin="anonymous"></script><img decoding="async" loading="lazy" class="alignright size-full" src="https://coolshell.cn/wp-content/uploads/2017/07/systemd.jpeg" alt="" width="275" height="183" />要说清 Systemd，得先从Linux操作系统的启动说起。Linux 操作系统的启动首先从 BIOS 开始，然后由 Boot Loader 载入内核，并初始化内核。内核初始化的最后一步就是启动 init 进程。这个进程是系统的第一个进程，PID 为 1，又叫超级进程，也叫根进程。它负责产生其他所有用户进程。所有的进程都会被挂在这个进程下，如果这个进程退出了，那么所有的进程都被 kill 。如果一个子进程的父进程退了，那么这个子进程会被挂到 PID 1 下面。（注：PID 0 是内核的一部分，主要用于内进换页，参看：<a href="http://en.wikipedia.org/wiki/Process_identifier" target="_blank" rel="noopener noreferrer">Process identifier</a>）</p>
<h4>SysV Init</h4>
<p>PID 1 这个进程非常特殊，其主要就任务是把整个操作系统带入可操作的状态。比如：启动 UI &#8211; Shell 以便进行人机交互，或者进入 X 图形窗口。传统上，PID 1 和传统的 Unix System V 相兼容的，所以也叫 <code>sysvinit</code>，这是使用得最悠久的 init 实现。Unix System V 于1983年 release。</p>
<p>在 <code>sysvint</code> 下，有好几个运行模式，又叫 <code>runlevel</code>。比如：常见的 3 级别指定启动到多用户的字符命令行界面，5 级别指定启起到图形界面，0 表示关机，6 表示重启。其配置在 <code>/etc/inittab</code> 文件中。</p>
<p><span id="more-17998"></span></p>
<p>与此配套的还有 <code>/etc/init.d/</code> 和 <code>/etc/rc[X].d</code>，前者存放各种进程的启停脚本（需要按照规范支持 <code>start</code>，<code>stop</code>子命令），后者的 X 表示不同的 runlevel 下相应的后台进程服务，如：<code>/etc/rc3.d</code> 是 runlevel=3 的。 里面的文件主要是 link 到  <code>/etc/init.d/</code> 里的启停脚本。其中也有一定的命名规范：S 或 K 打头的，后面跟一个数字，然后再跟一个自定义的名字，如：<code>S01rsyslog</code>，<code>S02ssh</code>。S 表示启动，K表示停止，数字表示执行的顺序。</p>
<h4>UpStart</h4>
<p>Unix 和 Linux 在 <code>sysvint</code> 运作多年后，大约到了2006年的时候，Linux内核进入2.6时代，Linux有了很多更新。并且，Linux开始进入桌面系统，而桌面系统和服务器系统不一样的是，桌面系统面临频繁重启，而且，用户会非常频繁的使用硬件的热插拔技术。于是，这些新的场景，让 <code>sysvint</code> 受到了很多挑战。</p>
<p>比如，打印机需要CUPS等服务进程，但是如果用户没有打机印，启动这个服务完全是一种浪费，而如果不启动，如果要用打印机了，就无法使用，因为<code>sysvint</code> 没有自动检测的机制，它只能一次性启动所有的服务。另外，还有网络盘挂载的问题。在 <code>/etc/fstab</code> 中，负责硬盘挂载，有时候还有网络硬盘（NFS 或 iSCSI）在其中，但是在桌面机上，有很可能开机的时候是没有网络的， 于是网络硬盘都不可以访问，也无法挂载，这会极大的影响启动速度。<code>sysvinit</code> 采用 <code>netdev</code> 的方式来解决这个问题，也就是说，需要用户自己在 <code>/etc/fstab</code> 中给相应的硬盘配置上 <code>netdev</code> 属性，于是 <code>sysvint</code> 启动时不会挂载它，只有在网络可用后，由专门的 <code>netfs</code> 服务进程来挂载。这种管理方式比较难以管理，也很容易让人掉坑。</p>
<p>所以，Ubuntu 开发人员在评估了当时几个可选的 init 系统后，决定重新设计这个系统，于是，这就是我们后面看到的 <code>upstart</code> 。 <code>upstart</code> 基于事件驱动的机制，把之前的完全串行的同步启动服务的方式改成了由事件驱动的异步的方式。比如：如果有U盘插入，<code>udev</code> 得到通知，<code>upstart</code> 感知到这个事件后触发相应的服务程序，比如挂载文件系统等等。因为使用一个事件驱动的玩法，所以，启动操作系统时，很多不必要的服务可以不用启动，而是等待通知，lazy 启动。而且事件驱动的好处是，可以并行启动服务，他们之间的依赖关系，由相应的事件通知完成。</p>
<p>upstart 有着很不错的设计，其中最重要的两个概念是 Job 和 Event。</p>
<p><strong>Job</strong> 有一般的Job，也有service的Job，并且，<code>upstart</code> 管理了整个 Job 的生命周期，比如：Waiting, Starting, pre-Start, Spawned, post-Start, Running, pre-Stop, Stopping, Killed, post-Stop等等，并维护着这个生命周期的状态机。</p>
<p><strong>Event</strong> 分成三类，<code>signal</code>, <code>method</code> 和 <code>hooks</code>。<code>signal</code> 就是异步消息，<code>method</code> 是同步阻塞的。<code>hooks</code> 也是同步的，但介于前面两者之间，发出hook事件的进程必须等到事件完成，但不检查是否成功。</p>
<p>但是，<code>upstart</code> 的事件非常复杂，也非常纷乱，各种各样的事件（事件没有归好类）导致有点凌乱。不过因为整个事件驱动的设计比之前的 <code>sysvinit</code> 来说好太多，所以，也深得欢迎。</p>
<h4>Systemd</h4>
<p>直到2010的有一天，一个在 RedHat工作的工程师 <a title="Lennart Poettering" href="https://en.wikipedia.org/wiki/Lennart_Poettering" target="_blank" rel="noopener noreferrer">Lennart Poettering</a> 和 <a title="Kay Sievers" href="https://en.wikipedia.org/wiki/Kay_Sievers">Kay Sievers</a> ，开始引入了一个新的 <code>init</code> 系统—— <code>systemd</code>。这是一个非常非常有野心的项目，这个项目几乎改变了所有的东西，<code>systemd</code> 不但想取代已有的 init 系统，而且还想干更多的东西。</p>
<p>Lennart 同意 <code>upstart</code> 干的不错，代码质量很好，基于事件的设计也很好。但是他觉得 <code>upstart</code> 也有问题，其中最大的问题还是不够快，虽然 <code>upstart</code> 用事件可以达到一定的启动并行度，但是，本质上来说，这些事件还是会让启动过程串行在一起。  如：<code>NetworkManager</code> 在等 <code>D-Bus</code> 的启动事件，而 <code>D-Bus</code> 在等 <code>syslog</code> 的启动事件。</p>
<p>Lennart 认为，实现上来说，<code>upstart</code> 其实是在管理一个逻辑上的服务依赖树，但是这个服务依赖树在表现形式上比较简单，你只需要配置——“启动 B好了就启动A”或是“停止了A后就停止B”这样的规则。但是，Lennart 说，这种简单其实是有害的（this simplification is actually detrimental）。他认为，</p>
<ul>
<li>从一个系统管理的角度出来，他一开始会设定好整个系统启动的服务依赖树，但是这个系统管理员要人肉的把这个本来就非常干净的服务依整树给翻译成计算机看的懂的 Event/Action 形式，而且 Event/Action 这种配置方式是运行时的，所以，你需要运行起来才知道是什么样的。</li>
</ul>
<ul>
<li>Event逻辑从头到脚到处都是，这个事件扩大了运维的复杂度，还不如之前的 <code>sysvint</code>。 也就是说，当用户配置了 “启动 <code>D-Bus</code> 后请启动 <code>NetworkManager</code>”， 这个 <code>upstart</code> 可以干，但是反过来，如果，用户启动 <code>NetworkManager</code>，我们应该先去启动他的前置依赖 <code>D-Bus</code>，然而你还要配置相应的反向 Event。本来，我只需要配置一条依赖的，结果现在我要配置很多很多情况下的Event。</li>
</ul>
<ul>
<li>最后，<code>upstart</code> 里的 Event 的并不标准，很混乱，没有良好的定义。比如：既有，进程启动，运行，停止的事件，也有USB设备插入、可用、拔出的事件，还有文件系统设备being mounted、 mounted 和 umounted 的事件，还有AC电源线连接和断开的事件。你会发现，这进程启停的、USB的、文件系统的、电源线的事件，看上去长得很像， 但是没有被标准化抽像出来掉，因为绝大多数的事件都是三元组：start, condition, stop 。这种概念设计模型并没有在 <code>upstart</code> 中出现。因为 <code>upstart</code> 被设计为单一的事件，而忽略了逻辑依赖。</li>
</ul>
<p>当然，如果 <code>systemd</code> 只是解决 <code>upstart</code> 的问题，他就改造 <code>upstart</code> 就好了，但是 Lennart 的野心不只是想干个这样的事，他想干的更多。</p>
<p>首先，<code>systemd</code> 清醒的认识到了 init 进程的首要目标是要让用户快速的进入可以操作OS的环境，所以，这个速度一定要快，越快越好，所以，<code>systemd</code> 的设计理念就是两条：</p>
<ul>
<li>To start <b>less</b>.</li>
<li>And to start <b>more</b> in <i>parallel</i>.</li>
</ul>
<p>也就是说，按需启动，能不启动就不启动，如果要启动，能并行启动就并行启动，包括你们之间有依赖，我也并行启动。按需启动还好理解，那么，有依赖关系的并行启动，它是怎么做到的？这里，<code>systemd</code> 借鉴了 MacOS 的 <code>Launchd</code> 的玩法（在Youtube上有一个分享——<a href="https://www.youtube.com/watch?v=SjrtySM9Dns" target="_blank" rel="noopener noreferrer">Launchd: One Program to Rule them All</a>，在苹果的开源网站上也有相关的设计文档——<a href="https://developer.apple.com/library/content/documentation/MacOSX/Conceptual/BPSystemStartup/Chapters/Introduction.html" target="_blank" rel="noopener noreferrer">About Daemons and Services</a>）</p>
<p>要解决这些依赖性，systemd 需要解决好三种底层依赖—— Socket， D-Bus ，文件系统。</p>
<ul>
<li><strong>Socket依赖</strong>。如果服务C依赖于服务S的socket，那么就要先启动S，然后再启动C，因为如果C启动时找不到S的Socket，那么C就会失败。<code>systemd</code> 可以帮你在S还没有启动好的时候，建立一个socket，用来接收所有的C的请求和数据，并缓存之，一旦S全部启动完成，把systemd替换好的这个缓存的数据和Socket描述符替换过去。</li>
</ul>
<p>&nbsp;</p>
<ul>
<li><strong>D-Bus依赖</strong>。<code>D-Bus</code> 全称 Desktop Bus，是一个用来在进程间通信的服务。除了用于用户态进程和内核态进程通信，也用于用户态的进程之前。现在，很多的现在的服务进程都用 <code>D-Bus</code> 而不是Socket来通信。比如：<code>NetworkManager</code> 就是通过 <code>D-Bus</code> 和其它服务进程通讯的，也就是说，如果一个进程需要知道网络的状态，那么就必需要通过 <code>D-Bus</code> 通信。<code>D-Bus</code> 支持 “Bus Activation”的特性。也就是说，A要通过 <code>D-Bus</code> 服务和B通讯，但是B没有启动，那么 <code>D-Bus</code> 可以把B起来，在B启动的过程中，<code>D-Bus</code> 帮你缓存数据。<code>systemd</code> 可以帮你利用好这个特性来并行启动 A 和 B。</li>
</ul>
<p>&nbsp;</p>
<ul>
<li><strong>文件系统依赖</strong>。系统启动过程中，文件系统相关的活动是最耗时的，比如挂载文件系统，对文件系统进行磁盘检查（fsck），磁盘配额检查等都是非常耗时的操作。在等待这些工作完成的同时，系统处于空闲状态。那些想使用文件系统的服务似乎必须等待文件系统初始化完成才可以启动。<code>systemd</code> 参考了 <code>autofs</code> 的设计思路，使得依赖文件系统的服务和文件系统本身初始化两者可以并发工作。<code>autofs</code> 可以监测到某个文件系统挂载点真正被访问到的时候才触发挂载操作，这是通过内核 <code>automounter</code> 模块的支持而实现的。比如一个 <code>open()</code> 系统调用作用在某个文件系统上的时候，而这个文件系统尚未执行挂载，此时 <code>open()</code> 调用被内核挂起等待，等到挂载完成后，控制权返回给 <code>open()</code> 系统调用，并正常打开文件。这个过程和 <code>autofs</code> 是相似的。</li>
</ul>
<p>&nbsp;</p>
<p>下图来自 Lennart 的演讲里的一页PPT，展示了不同 init 系统的启动。</p>
<p><img decoding="async" loading="lazy" class="aligncenter size-full" src="https://coolshell.cn/wp-content/uploads/2017/07/boot.png" alt="" width="467" height="308" /></p>
<p>除此之外，systemd 还在启动时管理好了一些下面的事。</p>
<p>用C语言取代传统的脚本式的启动。前面说过，<code>sysvint</code> 用 <code>/etc/rcX.d</code> 下的各种脚本启动。然而这些脚本中需要使用 <code>awk</code>, <code>sed</code>, <code>grep</code>, <code>find</code>, <code>xargs</code> 等等这些操作系统的命令，这些命令需要生成进程，生成进程的开销很大，关键是生成完这些进程后，这个进程就干了点屁大的事就退了。换句话说就是，我操作系统干了那么多事为你拉个进程起来，结果你就把个字串转成小写就退了，把我操作系统当什么了？</p>
<p>在正常的一个 <code>sysvinit</code> 的脚本里，可能会有成百上千个这样的命令。所以，慢死。因此，<code>systemd</code> 全面用 C 语言全部取代了。一般来说，<code>sysvinit</code> 下，操作系统启动完成后，用 <code>echo $$</code> 可以看到，pid 被分配到了上千的样子，而 <code>systemd</code> 的系统只是上百。</p>
<p>另外，systemd 是真正一个可以管住服务进程的——可以跟踪上服务进程所fork/exec出来的所有进程。</p>
<ul>
<li>我们知道， 传统 Unix/Linux 的 Daemon 服务进程的最佳实践基本上是这个样子的 （具体过程可参看这篇文章“<a href="http://0pointer.de/public/systemd-man/daemon.html#SysV%20Daemons" target="_blank" rel="noopener noreferrer">SysV Daemon</a>”）——
<ol>
<li>进程启动时，关闭所有的打开的文件描述符（除了标准描述符0,1,2），然后重置所有的信号处理。</li>
<li>调用 <code>fork()</code> 创建子进程，在子进程中 <code>setsid()</code>，然后父进程退出（为了后台执行）</li>
<li>在子进程中，再调用一次 <code>fork()</code>，创建孙子进程，确定没有交互终端。然后子进程退出。</li>
<li>在孙子进程中，把标准输入标准输出标准错误都连到 <code>/dev/null</code> 上，还要创建 pid 文件，日志文件，处理相关信号 ……</li>
<li>最后才是真正开始提供服务。</li>
</ol>
</li>
</ul>
<p>&nbsp;</p>
<ul>
<li>在上面的这个过程中，服务进程除了两次 <code>fork</code> 外还会 <code>fork</code> 出很多很多的子进程（比如说一些Web服务进程，会根据用户的请求链接来 <code>fork</code> 子进程），这个进程树是相当难以管理的，因为，一旦父进程退出来了，子进程就会被挂到 PID 1下，所以，基本上来说，你无法通过服务进程自已给定的一个pid文件来找到所有的相关进程（这个对开发者的要求太高了），所以，在传统的方式下用脚本启停服务是相当相当的 Buggy 的，因为无法做对所有的服务生出来的子子孙孙做到监控。</li>
</ul>
<p>&nbsp;</p>
<ul>
<li>为了解决这个问题，<code>upstart</code> 通过变态的 <code>strace</code> 来跟踪进程中的 <code>fork()</code> 和 <code>exec()</code> 或 <code>exit()</code> 等相关的系统调用。这种方法相当笨拙。 <code>systemd</code> 使用了一个非常有意思的玩法来 tracking 服务进程生出来的所有进程，那就是用 <code>cgroup</code> （我在 <a href="https://coolshell.cn/articles/17049.html" target="_blank" rel="noopener noreferrer">Docker 的基础技术“cgroup篇”</a>中讲过这个东西）。cgroup主要是用来管理进程组资源配额的事，所以，无论服务如何启动新的子进程，所有的这些相关进程都会同属于一个 <code>cgroup</code>，所以，<code>systemd</code> 只需要简单的去遍历一下相应的 <code>cgroup</code> 的那个虚文件系统目录下的文件，就可以正确的找到所有的相关进程，并将他们一一停止。</li>
</ul>
<p>&nbsp;</p>
<p>另外，<code>systemd</code> 简化了整个 daemon 开发的过程：</p>
<ul>
<li>不需要两次 <code>fork()</code>，只需要实现服务本身的主逻辑就可以了。</li>
<li>不需要 <code>setsid()</code>，<code>systemd</code> 会帮你干</li>
<li>不需要维护 <code>pid文件</code>，<code>systemd</code> 会帮处理。</li>
<li>不需要管理日志文件或是使用<code>syslog</code>，或是处理<code>HUP</code>的日志reload信号。把日志打到 <code>stderr</code> 上，<code>systemd</code> 帮你管理。</li>
<li>处理 <code>SIGTERM</code> 信号，这个信号就是正确退出当前服务，不要做其他的事。</li>
<li>……</li>
</ul>
<p>除此之外，<code>systemd</code> 还能——</p>
<ul>
<li>自动检测启动的服务间有没有环形依赖。</li>
<li>内建 autofs 自动挂载管理功能。</li>
<li>日志服务。<code>systemd</code> 改造了传统的 syslog 的问题，采用二进制格式保存日志，日志索引更快。</li>
<li>快照和恢复。对当前的系统运行的服务集合做快照，并可以恢复。</li>
<li>……</li>
</ul>
<p>还有好多好多，他接管很多很多东西，于是就让很多人不爽了，因为他在干了很多本不属于 PID 1 的事。</p>
<h4>Systemd 争论和八卦</h4>
<p>于是 <code>systemd</code> 这个东西成了可能是有史以来口水战最多的一个开源软件了。<code>systemd</code> 饱受各种争议，最大的争议就是他破坏了 Unix 的设计哲学（相关的哲学可以读一下《<a href="https://book.douban.com/subject/1467587/" target="_blank" rel="noopener noreferrer">Unix编程艺术</a>》），干了一个大而全而且相当复杂的东西。当然，Lennart 并不同意这样的说法，他后来又写一篇blog “<a href="http://0pointer.de/blog/projects/the-biggest-myths.html" target="_blank" rel="noopener noreferrer">The Biggest Myths</a>”来解释 <code>systemd</code> 并不是这样的，大家可以前往一读。</p>
<p>这个争议大到什么样子呢？2014 年，Debian Linux 因为想准备使用 <code>systemd</code> 来作为标准的 init 守护进程来替换 <code>sysvinit</code> 。而围绕这个事的争论达到了空前的热度，争论中充满着仇恨，<code>systemd</code> 的支持者和反对者都在互相辱骂，导致当时 Debian 阵营开始分裂。还有人给 Lennart 发了死亡威胁的邮件，用比特币雇凶买杀手，扬言要取他的性命，在Youbute上传了侮辱他的歌曲，在IRC和各种社交渠道上给他发下流和侮辱性的消息。这已经不是争议了，而是一种不折不扣的仇恨！</p>
<p><img decoding="async" loading="lazy" class="aligncenter size-full" src="https://coolshell.cn/wp-content/uploads/2017/07/systemd_shewantsit.jpg" alt="" width="1000" height="421" /></p>
<p>于是，Lennart 在 <a href="https://plus.google.com/+LennartPoetteringTheOneAndOnly/posts/J2TZrTvu7vd" target="_blank" rel="noopener noreferrer">Google Plus 上发了贴子</a>，批评整个 Linux 开源社区和 Linus 本人。他大意说，</p>
<blockquote><p>这个社区太病态了，全是 ass holes，你们不停用各种手段在各种地方用不同的语言和方式来侮辱和漫骂我。我还是一个年轻人，我从来没有经历过这样的场面，但是今天我已经对这种场面很熟悉了。我有时候说话可能不准确，但是我不会像他样那样说出那样的话，我也没有被这些事影响，因为我脸皮够厚，所以，为什么我可以在如何大的反对声面前让 <code>systemd</code> 成功，但是，你们 Linux 社区太可怕了。你们里面的有精神病的人太多了。另外，对于Linus Torvalds，你是这个社区的 Role Model，但可惜你是一个 Bad Role Model，你在社区里的刻薄和侮辱性的言行，基本从一定程度上鼓励了其它人跟你一样，当然，并不只是你一个人的问题，而是在你周围聚集了一群和你一样的这样干的人。送你一句话—— A fish rots from the head down ！一条鱼是从头往下腐烂的……</p></blockquote>
<p>这篇契文很长，喜欢八卦的同学可以前往一读。感受一下 Lennart 当时的心态（我觉得能算上是非常平稳了）。</p>
<p>Linus也在被一媒体问起 <code>systemd</code> 这个事来（参看“<a href="https://www.itwire.com/business-it-news/open-source/65402-torvalds-says-he-has-no-strong-opinions-on-systemd" target="_blank" rel="noopener noreferrer">Torvalds says he has no strong opinions on systemd</a>”），Linus在采访里说，</p>
<blockquote><p>我对 <code>systemd</code> 和 Lennart 的贴子没有什么强烈的想法。虽然，传统的 Unix 设计哲学—— “Do one thing and Do it well”，很不错，而且我们大多数人也实践了这么多年，但是这并不代表所有的真实世界。在历史上，也不只有<code>systemd</code> 这么干过。但是，我个人还是 old-fashioned 的人，至少我喜欢文本式的日志，而不是二进制的日志。但是 <code>systemd</code> 没有必要一定要有这样的品味。哦，我说细节了……</p></blockquote>
<p>今天，<code>systemd</code> 占据了几乎所有的主流的 Linux 发行版的默认配置，包括：Arch Linux、CentOS、CoreOS、Debian、Fedora、Megeia、OpenSUSE、RHEL、SUSE企业版和 Ubuntu。而且，对于 CentOS, CoreOS, Fedora, RHEL, SUSE这些发行版来说，不能没有 <code>systemd</code>。（Ubuntu 还有一个不错的wiki &#8211; <a href="https://wiki.ubuntu.com/SystemdForUpstartUsers" target="_blank" rel="noopener noreferrer">Systemd for Upstart Users</a> 阐述了如何在两者间切换）</p>
<p>&nbsp;</p>
<h4>其它</h4>
<p>还记得在《<a href="https://coolshell.cn/articles/17416.html" target="_blank" rel="noopener noreferrer">缓存更新的套路</a>》一文中，我说过，<strong>如果你要做好架构，首先你得把计算机体系结构以及很多老古董的基础技术吃透了</strong>。因为里面会有很多可以借鉴和相通的东西。那么，你是否从这篇文章里看到了一些有分布式架构相似的东西？</p>
<p>比如：从 <code>sysvinit</code> 到 <code>upstart</code> 再到 <code>systemd</code>，像不像是服务治理？Linux系统下的这些服务进程，是不是很像分布式架构中的微服务？还有那个D-Bus，是不是很像SOA里的ESB？而 init 系统是不是很像一个控制系统？甚至像一个服务编排（Service Orchestration）系统？</p>
<p>分布式系统中的服务之间也有很多依赖，所以，在启动一个架构的时候，如果我们可以做到像 systemd 那样并行启动的话，那么是不是就像是一个微服务的玩法了？</p>
<p>嗯，你会发现，技术上的很多东西是相通的，也是互相有对方的影子，所以，其实技术并不多。关键是我们学在了表面还是看到了本质。</p>
<p>&nbsp;</p>
<h4>延伸阅读</h4>
<ul>
<li>Lennert 的博文：<a href="http://0pointer.de/blog/projects/systemd.html" target="_blank" rel="noopener noreferrer">Rethinking PID 1</a></li>
<li>Lennert 的演讲：<a href="https://www.youtube.com/watch?v=TyMLi8QF6sw" target="_blank" rel="noopener noreferrer">systemd, beyond init</a> （ <a href="http://www.linux-kongress.org/2010/slides/systemd-poettering.pdf" target="_blank" rel="noopener noreferrer">PPT</a> ）</li>
<li><a href="https://en.wikipedia.org/wiki/Systemd" target="_blank" rel="noopener noreferrer">Wikipedia：Systemd</a></li>
<li>LinuxVoice：<a href="https://www.linuxvoice.com/interview-lennart-poettering/" target="_blank" rel="noopener noreferrer">Lennart Poettering 专访</a></li>
</ul>
<p>（全文完）<!--



<p align="center"><a href= target=_blank><img decoding="async" src=""></a></p>





<p align="center"><img decoding="async" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg"> <img decoding="async" loading="lazy" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg" width="300" height="300"> <br />关注CoolShell微信公众账号和微信小程序</p>

 

--></p>
<div style="margin-top: 15px; font-size: 16px;color: #cc0000;">
<p align="center"><strong>（转载本站文章请注明作者和出处 <a href="https://coolshell.cn/">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>
</div>

<div class="wp_rp_wrap  wp_rp_vertical_m" ><div class="wp_rp_content"><h3 class="related_post_title">相关文章</h3><ul class="related_post wp_rp"><li ><a href="http://coolshell.cn/articles/12103.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2014/11/tux-fork-150x150.gif" alt="vfork 挂掉的一个问题" width="150" height="150" /></a><a href="http://coolshell.cn/articles/12103.html" class="wp_rp_title">vfork 挂掉的一个问题</a></li><li ><a href="http://coolshell.cn/articles/17061.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2015/08/docker-filesystems-busyboxrw-150x150.png" alt="Docker基础技术：AUFS" width="150" height="150" /></a><a href="http://coolshell.cn/articles/17061.html" class="wp_rp_title">Docker基础技术：AUFS</a></li><li ><a href="http://coolshell.cn/articles/17010.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2015/04/isolation-150x150.jpg" alt="Docker基础技术：Linux Namespace（上）" width="150" height="150" /></a><a href="http://coolshell.cn/articles/17010.html" class="wp_rp_title">Docker基础技术：Linux Namespace（上）</a></li><li ><a href="http://coolshell.cn/articles/2322.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2010/04/o_unixrichiethompson-150x150.jpg" alt="Unix传奇(上篇)" width="150" height="150" /></a><a href="http://coolshell.cn/articles/2322.html" class="wp_rp_title">Unix传奇(上篇)</a></li><li ><a href="http://coolshell.cn/articles/17029.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2015/04/jail_cell-150x150.jpg" alt="Docker基础技术：Linux Namespace（下）" width="150" height="150" /></a><a href="http://coolshell.cn/articles/17029.html" class="wp_rp_title">Docker基础技术：Linux Namespace（下）</a></li><li ><a href="http://coolshell.cn/articles/17049.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2015/04/filter-150x150.png" alt="Docker基础技术：Linux CGroup" width="150" height="150" /></a><a href="http://coolshell.cn/articles/17049.html" class="wp_rp_title">Docker基础技术：Linux CGroup</a></li></ul></div></div>The post <a href="https://coolshell.cn/articles/17998.html">Linux PID 1 和 Systemd</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></content:encoded>
					
					<wfw:commentRss>https://coolshell.cn/articles/17998.html/feed</wfw:commentRss>
			<slash:comments>49</slash:comments>
		
		
			</item>
		<item>
		<title>vfork 挂掉的一个问题</title>
		<link>https://coolshell.cn/articles/12103.html</link>
					<comments>https://coolshell.cn/articles/12103.html#comments</comments>
		
		<dc:creator><![CDATA[陈皓]]></dc:creator>
		<pubDate>Thu, 20 Nov 2014 16:48:27 +0000</pubDate>
				<category><![CDATA[C/C++语言]]></category>
		<category><![CDATA[Unix/Linux]]></category>
		<category><![CDATA[操作系统]]></category>
		<category><![CDATA[fork]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[Unix]]></category>
		<category><![CDATA[vfork]]></category>
		<guid isPermaLink="false">http://coolshell.cn/?p=12103</guid>

					<description><![CDATA[<p>在知乎上，有个人问了这样的一个问题——为什么vfork的子进程里用return，整个程序会挂掉，而且exit()不会？并给出了如下的代码，下面的代码一运行就挂掉...</p>
<p class="read-more"><a class="btn btn-default" href="https://coolshell.cn/articles/12103.html"> Read More<span class="screen-reader-text">  Read More</span></a></p>
The post <a href="https://coolshell.cn/articles/12103.html">vfork 挂掉的一个问题</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></description>
										<content:encoded><![CDATA[<p><script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158"
     crossorigin="anonymous"></script><img decoding="async" loading="lazy" class="alignright wp-image-12105" src="https://coolshell.cn/wp-content/uploads/2014/11/tux-fork-298x300.gif" alt="tux-fork" width="199" height="200" srcset="https://coolshell.cn/wp-content/uploads/2014/11/tux-fork-298x300.gif 298w, https://coolshell.cn/wp-content/uploads/2014/11/tux-fork-150x150.gif 150w, https://coolshell.cn/wp-content/uploads/2014/11/tux-fork-200x200.gif 200w, https://coolshell.cn/wp-content/uploads/2014/11/tux-fork-268x270.gif 268w" sizes="(max-width: 199px) 100vw, 199px" />在知乎上，有个人问了这样的<a href="http://www.zhihu.com/question/26591968" target="_blank">一个问题</a>——为什么vfork的子进程里用return，整个程序会挂掉，而且exit()不会？并给出了如下的代码，下面的代码一运行就挂掉了，但如果把子进程的return改成exit(0)就没事。</p>
<p>我受邀后本来不想回答这个问题的，因为这个问题明显就是RTFM的事，后来，发现这个问题放在那里好长时间，而挂在下面的几个答案又跑偏得比较严重，我觉得可能有些朋友看到那样的答案会被误导，所以就上去回答了一下这个问题。</p>
<p>下面我把问题和我的回答发布在这里，也供更多的人查看。</p>
<pre data-enlighter-language="c" class="EnlighterJSRAW">#include &lt;stdio.h&gt;
#include &lt;stdlib.h&gt;
#include &lt;unistd.h&gt;
int main(void) {
    int var;
    var = 88;
    if ((pid = vfork()) &lt; 0) {
        printf(&quot;vfork error&quot;);
        exit(-1);
    } else if (pid == 0) { /* 子进程 */
        var++;
        return 0;
    }
    printf(&quot;pid=%d, glob=%d, var=%d\n&quot;, getpid(), glob, var);
    return 0;
}
</pre>
<p><span id="more-12103"></span></p>
<h4><b>基础知识</b></h4>
<p>首先说一下fork和vfork的差别：</p>
<ul>
<li>fork 是 创建一个子进程，并把父进程的内存数据copy到子进程中。</li>
<li>vfork是 创建一个子进程，并和父进程的内存数据share一起用。</li>
</ul>
<p>这两个的差别是，一个是copy，一个是share。（关于fork，可以参看酷壳之前的《<a title="一个fork的面试题" href="https://coolshell.cn/articles/7965.html" target="_blank">一道fork的面试题</a>》）</p>
<p>你 man vfork 一下，你可以看到，vfork是这样的工作的，</p>
<p style="padding-left: 30px;">1）保证子进程先执行。<br />
2）当子进程调用exit()或exec()后，父进程往下执行。</p>
<p>那么，为什么要干出一个vfork这个玩意？ 原因在man page也讲得很清楚了：</p>
<blockquote><p><strong>Historic Description</strong></p>
<p>Under Linux, fork(2) is implemented using copy-on-write pages, so the only penalty incurred by fork(2) is the time and memory required to duplicate the parent’s page tables, and to create a unique task structure for the child. <b>However, in the bad old days a fork(2) would require making </b><b>a complete copy of the caller’s data space, often needlessly, since usually immediately afterwards an exec(3) is done. Thus, for greater efficiency, BSD introduced the vfork() system call, which did not fully copy the address space of the parent process, but borrowed the parent’s mem</b><b>ory and thread of control until a call to execve(2) or an exit occurred.</b> The parent process was suspended while the child was using its resources. The use of vfork() was tricky: for example, not modifying data in the parent process depended on knowing which variables are held in a register.</p></blockquote>
<p>意思是这样的—— <b>起初只有fork，但是很多程序在fork一个子进程后就exec一个外部程序，于是fork需要copy父进程的数据这个动作就变得毫无意了，而且这样干还很重</b>（注：后来，fork做了优化，详见本文后面）<b>，所以，BSD搞出了个父子进程共享的 vfork，这样成本比较低。因此，vfork本就是为了exec而生。</b></p>
<h4><b>为什么return会挂掉，exit()不会？</b></h4>
<p>从上面我们知道，<b>结束子进程的调用是exit()而不是return，如果你在vfork中return了，那么，这就意味main()函数return了，注意因为函数栈父子进程共享，所以整个程序的栈就跪了。</b></p>
<p>如果你在子进程中return，那么基本是下面的过程：</p>
<p style="padding-left: 30px;"><b>1）子进程的main() 函数 return了，于是程序的函数栈发生了变化。</b></p>
<p style="padding-left: 30px;"><b>2）而main()函数return后，通常会调用 exit()或相似的函数</b>（如：_exit()，exitgroup()）</p>
<p style="padding-left: 30px;"><b>3）这时，父进程收到子进程exit()，开始从vfork返回，但是尼玛，老子的栈都被你子进程给return干废掉了，你让我怎么执行？</b>（注：栈会返回一个诡异一个栈地址，对于某些内核版本的实现，直接报“栈错误”就给跪了，然而，对于某些内核版本的实现，于是有可能会再次调用main()，于是进入了一个无限循环的结果，直到vfork 调用返回 error）</p>
<p>好了，现在再回到 return 和 exit，return会释放局部变量，并弹栈，回到上级函数执行。exit直接退掉。如果你用c++ 你就知道，return会调用局部对象的析构函数，exit不会。（注：exit不是系统调用，是glibc对系统调用 _exit()或_exitgroup()的封装）</p>
<p>可见，<b>子进程调用exit() 没有修改函数栈，所以，父进程得以顺利执行</b>。</p>
<p><strong>但是！注意！如果你调用 exit() 函数，还是会有问题的，正确的方法应该是调用 _exit() 函数，因为 exit() 函数 会 flush 并 close 所有的 标准 I/O ，这样会导致父进程受到影响。（这个情况在fork下也会受到影响，会导致一些被buffer的数据被flush两次，这里可以参看《<a href="https://coolshell.cn/articles/7965.html" target="_blank">一个fork的面试题</a>》）</strong></p>
<h4>关于fork的优化</h4>
<p>很明显，fork太重，而vfork又太危险，所以，就有人开始优化fork这个系统调用。优化的技术用到了著名的<b>写时拷贝（COW）</b>。</p>
<p>也就是说，<strong>对于fork后并不是马上拷贝内存，而是只有你在需要改变的时候，才会从父进程中拷贝到子进程中，这样fork后立马执行exec的成本就非常小了</strong>。所以，Linux的Man Page中并不鼓励使用vfork() ——</p>
<blockquote><p>“ It is rather unfortunate that Linux revived this specter from the past. The BSD man page states: &#8220;This system call will be eliminated when proper system sharing mechanisms are implemented. Users should not depend on the memory sharing semantics of vfork() as it will, in that case, be made synonymous to fork(2).&#8221;”</p></blockquote>
<p>于是，从BSD4.4开始，他们让vfork和fork变成一样的了</p>
<p>但在后来，NetBSD 1.3 又把传统的vfork给捡了回来，说是vfork的性能在 Pentium Pro 200MHz 的机器（这机器好古董啊）上有可以提高几秒钟的性能。详情见——“<a class=" wrap external" href="http://www.netbsd.org/docs/kernel/vfork.html" target="_blank" rel="nofollow noreferrer">NetBSD Documentation: Why implement traditional vfork()<i class="icon-external"></i></a>”</p>
<p>今天的Linux下，fork和vfork还是各是各的，不过，还是建议你不要用vfork，除非你非常关注性能。</p>
<p>（全文完）<!--



<p align="center"><a href= target=_blank><img decoding="async" src=""></a></p>





<p align="center"><img decoding="async" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg"> <img decoding="async" loading="lazy" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg" width="300" height="300"> <br />关注CoolShell微信公众账号和微信小程序</p>

 

--></p>
<div style="margin-top: 15px; font-size: 16px;color: #cc0000;">
<p align="center"><strong>（转载本站文章请注明作者和出处 <a href="https://coolshell.cn/">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>
</div>

<div class="wp_rp_wrap  wp_rp_vertical_m" ><div class="wp_rp_content"><h3 class="related_post_title">相关文章</h3><ul class="related_post wp_rp"><li ><a href="https://coolshell.cn/articles/18360.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg" alt="程序员练级攻略（2018)  与我的专栏" width="150" height="150" /></a><a href="https://coolshell.cn/articles/18360.html" class="wp_rp_title">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href="https://coolshell.cn/articles/17998.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2017/07/systemd-1-150x150.jpeg" alt="Linux PID 1 和 Systemd" width="150" height="150" /></a><a href="https://coolshell.cn/articles/17998.html" class="wp_rp_title">Linux PID 1 和 Systemd</a></li><li ><a href="https://coolshell.cn/articles/11847.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2014/08/puzzle-150x150.png" alt="谜题的答案和活动的心得体会" width="150" height="150" /></a><a href="https://coolshell.cn/articles/11847.html" class="wp_rp_title">谜题的答案和活动的心得体会</a></li><li ><a href="https://coolshell.cn/articles/9104.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2013/02/sed-superman-150x150.png" alt="sed 简明教程" width="150" height="150" /></a><a href="https://coolshell.cn/articles/9104.html" class="wp_rp_title">sed 简明教程</a></li><li ><a href="https://coolshell.cn/articles/9070.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2013/02/awk-150x150.jpg" alt="AWK 简明教程" width="150" height="150" /></a><a href="https://coolshell.cn/articles/9070.html" class="wp_rp_title">AWK 简明教程</a></li><li ><a href="https://coolshell.cn/articles/8883.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2013/01/linux-bash-300x225-150x150.jpg" alt="应该知道的Linux技巧" width="150" height="150" /></a><a href="https://coolshell.cn/articles/8883.html" class="wp_rp_title">应该知道的Linux技巧</a></li></ul></div></div>The post <a href="https://coolshell.cn/articles/12103.html">vfork 挂掉的一个问题</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></content:encoded>
					
					<wfw:commentRss>https://coolshell.cn/articles/12103.html/feed</wfw:commentRss>
			<slash:comments>46</slash:comments>
		
		
			</item>
		<item>
		<title>谜题的答案和活动的心得体会</title>
		<link>https://coolshell.cn/articles/11847.html</link>
					<comments>https://coolshell.cn/articles/11847.html#comments</comments>
		
		<dc:creator><![CDATA[陈皓]]></dc:creator>
		<pubDate>Tue, 05 Aug 2014 23:47:50 +0000</pubDate>
				<category><![CDATA[杂项资源]]></category>
		<category><![CDATA[趣味问题]]></category>
		<category><![CDATA[Algorithm]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[Puzzle]]></category>
		<category><![CDATA[Unix]]></category>
		<guid isPermaLink="false">http://coolshell.cn/?p=11847</guid>

					<description><![CDATA[<p>我于2014年8月3日周六的上午在微博、twitter、CoolShell上发布了一个和程序员有关的解谜题的活动——【活动】解谜题送礼物。我使用了二级域名fun...</p>
<p class="read-more"><a class="btn btn-default" href="https://coolshell.cn/articles/11847.html"> Read More<span class="screen-reader-text">  Read More</span></a></p>
The post <a href="https://coolshell.cn/articles/11847.html">谜题的答案和活动的心得体会</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></description>
										<content:encoded><![CDATA[<p><script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158"
     crossorigin="anonymous"></script>我于2014年8月3日周六的上午在微博、twitter、CoolShell上发布了一个和程序员有关的解谜题的活动——<a title="【活动】解迷题送礼物" href="https://coolshell.cn/articles/11832.html" target="_blank">【活动】解谜题送礼物</a>。我使用了二级域名fun.coolshell.cn做为这次活动的页面。</p>
<p><img decoding="async" loading="lazy" class="aligncenter size-full wp-image-11848" src="https://coolshell.cn/wp-content/uploads/2014/08/puzzle.png" alt="" width="543" height="206" srcset="https://coolshell.cn/wp-content/uploads/2014/08/puzzle.png 543w, https://coolshell.cn/wp-content/uploads/2014/08/puzzle-300x114.png 300w" sizes="(max-width: 543px) 100vw, 543px" /></p>
<p>截止这篇文章发布的时候，fun.coolshell.cn的访问量UV大约有4万左右，通关人数大约有200人，但因为在活动的第二天网上就出了一些答题攻略，通过分析，实际靠自己能力通过的人数在130人左右。通过率大约不到4‰的样子。</p>
<p>在这里我把整个谜题和做这个活动的东西写一下，算是给自己的一个总结。</p>
<h4>谜题的答案和花絮</h4>
<p>fun.coolshell.cn上一共有十道谜题，<strong>要设计这些东西还真是费尽脑汁，这让我对那些设计谜题式游戏的人相当敬佩</strong>。</p>
<p><span id="more-11847"></span></p>
<p style="padding-left: 30px;"><strong>第0关：</strong>很多人可能一头雾水，完全不知道这是什么，其实只要Google一下，你会知道这是一个叫BrainFuck的语言。在Coolshell.cn上我也介绍了过——《<a title="BT雷人的程序语言" href="https://coolshell.cn/articles/1142.html" target="_blank">BT雷人的程序语言</a>》《<a title="BT雷人的程序语言（大全）" href="https://coolshell.cn/articles/4458.html" target="_blank">BT雷人的程序语言（大全）</a>》，要通过这关，你需要把那段程序编译一下。要编译这段程序其实很简单，Google一个在线的编译器就可以了。（关于其它更多的古怪的编程语言请参看这里：<a href="http://esolangs.org/wiki/Language_list" target="_blank">http://esolangs.org/wiki/Language_list</a>）</p>
<p style="padding-left: 30px;"><strong>第1关：</strong>这一关也是很简单的，你需要在网页上找到两个数，一个是X，一个是Y，然后求得X和Y的乘积。对于X，你可以观察一下那个数列游戏，对于Y，你可以Google一下就知道了（我在Coolshell的《<a title="如何用最有创造力的方式输出42" href="https://coolshell.cn/articles/11170.html" target="_blank">如何用最有创造力的方式输出42</a>》说过这个事）。</p>
<p style="padding-left: 30px;"><strong>第2关：</strong>上面显示了一个不一样的键盘，我给了这个键盘的Wikipedia的链接。这个键盘叫Dvorak键，不同于我们的Qwert键。通过这个两个键盘的布局映射，你可以把下面那段读不懂的文字解出来（其实，你还是可以Google，有在线的转换）。把下面那段文字转成Qwert键的，你就会发现这是一段代码，这段代码非常著名，<span style="color: #000000;">是1987年国际<a href="http://www.di-mgt.com.au/src/korn_ioccc.txt" target="_blank">C语言混乱大赛一等奖的一段代码</a>（你可Google “IOCCC 87 unix”）。（关于IOCCC你可以参看Coolshell之前的《<a title="6个变态的C语言Hello World程序" href="https://coolshell.cn/articles/914.html" target="_blank">6个变态的HelloWorld</a>》、《<a title="如何加密/混乱C源代码" href="https://coolshell.cn/articles/933.html" target="_blank">如何混乱代码</a>》、《<a title="如何写出无法维护的代码" href="https://coolshell.cn/articles/4758.html" target="_blank">如何写出无法维护的代码</a>》这几篇文章）</span></p>
<p style="padding-left: 30px;"><strong>第3关：</strong>扫描二维码以后，你会得到一个码表转换，你可以使用Shell的tr命令来转一下下面的话。转完后你就可以读懂了，读懂了你还需要使用rot13来转一下“shell”（Google一下，你会发现也有在线的转换器，另外还有其它的rot）</p>
<p style="padding-left: 30px;"><strong>第4关</strong>：这是众多同学被卡在的地方。很多同学吐槽这题太坑了，别忘了这是游戏啊。我问了几个早先通关的同学，他们都说还好了，只要静一下心来多观察一下，你就会找出规律的。这个回文的模式是，一个大写字符和一个数字（顺序不限）把一个小字母套起来。于是，写成正则表达式是：</p>
<p><code data-enlighter-language="shell" class="EnlighterJSRAW">([A-Z])([0-9])[a-z]\2\1|([0-9])([A-Z])[a-z]\4\3</code></p>
<p style="padding-left: 30px;">用shell命令可以很快地找到9个匹配，然后，像“cat”一样，取中间的小写字母组成一个单词。写成Shell命令是：</p>
<p><code data-enlighter-language="shell" class="EnlighterJSRAW">grep -o &quot;\([A-Z]\)\([0-9]\)[a-z]\2\1\|\([0-9]\)\([A-Z]\)[a-z]\4\3&quot; cat.txt | sed -E &quot;s/(.)(.)(.)\2\1/\3/g&quot; | awk &#039;{printf(&quot;%s&quot;,$1)}&#039; &amp;&amp; echo &quot;&quot;</code></p>
<p style="padding-left: 30px;">这题主要考的是你的观察能力和正则表达式。</p>
<p style="padding-left: 30px;"><strong>第5关</strong>：如果你点了一下图片后，你就知道，这个连接http://fun.coolshell.cn/n/2014返回了一个数字，如果你把这个数字放到那个URL中，不断地替换其中的数字，你会得到一个新的数字。于是你就会得到最终的答案。</p>
<p style="padding-left: 30px;">这道题本来我是想让大家写程序的，我原来设置了一共512个序列，但是考虑到服务受不了，所以，我把它降到了128个，这样保证你的程序可以在几秒钟内得到结果，而不会对我的服务器造成压力。但是我还是看到好几个同学人肉地copy+paste+回车刷了100多下，得到了最终答案。</p>
<p style="padding-left: 30px;"><strong>第6关：</strong>通过中序和后序遍历还原一棵二叉树，然后再找到其最深的路径，然后得到一个字符串后，把这个字符串做为一个passcode代入那个openssl的命令行中。你就可以解密密文得到下一关的答案。</p>
<p style="padding-left: 30px;">这个题，我本想设计得更隐晦一些，用一个“心脏流血”的图片来暗示openssl，然后用别的东西暗示AES-128-CBC，后来想想算了，主要还是考大家在大学里的二叉树的最基本的算法。并介绍一下openssl的shell命令行加解密的方法。</p>
<p style="padding-left: 30px;">在网上的一些攻略中我看到了大家没有用程序，而是手动地花了一棵树出来。（其实，这设计这道的时候，我本来想设计成随机树，也就每个人看到的答案都不一样，我随机建树并且找最深路径的程序都写好了，但是我最终还是没有这样做，因为这无疑增加我对这个网页游戏的代码复杂度，而我又没有太多的时间，而谜题的各种形式已经够让我花精力的了，你虽然看到了10道题，但是其实我设计了一共有16道题，我反复斟酌，即不想为难大家，又不想太简单和无聊，所以最终release了这十道题）</p>
<p style="padding-left: 30px;"><strong>第7关：</strong>N皇后问题，这个问题也是大学里的题。9皇后一共有352个解，你需要把这352个解代到那个sha1的公式中（需要上一关用于解密的passcode），这样你就会得到一个解。然后这就是通关口令。</p>
<p style="padding-left: 30px;">第6关和第7关的算法题你要是不会写的话，Google一下，反正我们是“大自然的搬运工”，不是吗？呵呵。</p>
<p style="padding-left: 30px;">第7关这题啊，我看到一个同学用穷举的1-9的排序组合的方式来向服务发请求，从123456789开始，我都看SB了，因为这关的通答案是9开头，我勒了个去！你得对我的服务器发多少次请求啊，才能得到一个200的回复啊。TNND。服了。不过这个同学我最终还是给通过了，没有判定成作弊。</p>
<p style="padding-left: 30px;"><strong>第8关：</strong>Excel的列号编程，这一关写成代码其实并不难的。但我看到网上给的好些答案，大家都是用手算。也OK，这题本身就没有什么难度，但是因为这个26进制是从1开始的，写出来的代码并不非常容易，一些边界条件很容易就break掉了。这题完全考的是编码。把COOLSHELL除以SHELL的数转成字符串。然后就进入最后一关了。</p>
<p style="padding-left: 30px;">然后，我又见到有个同学用了穷举的方式，TNND，其实每道题都有人在用穷举的方式，我勒个去。他从AAA开始穷举，不一会就穷举出正确答案了。尼玛！</p>
<p style="padding-left: 30px;"><strong>第9关：</strong>一个猪圈和一个共济会的logo，你Google一下，你就知道答案了。这题纯粹就是介绍知识的。不知道大家有没有去wikipedia上了解了一下这个猪圈密码和共济会是怎么一回事吗？这样的密文叫图片密文，还有很多类似的图片密文的。你知道吗？有相应的字库哦。也有在线的生成器哦。（因为我最近在学各种安全的基础知识，所以了解到了这个东西）</p>
<p style="padding-left: 30px;"><strong>通关：</strong>于是你就通关了。你会发现你得到了一个helloworld，这个字符串，在我一放出来这个谜题的时候，就有很多人在尝试helloworld就是那段brainfuck的代码的输出。我汗啊。还好我做了一个比较复杂的防作弊检查……</p>
<p>总体来说，这些关卡都不难，但是你最少也得用2-3个小时。<a href="http://fun.coolshell.cn/top100.html" target="_blank">Top100页面</a>时统计的平均时间是10个半小时。</p>
<p>再说一个花絮，自从，8月3日上线后，8月4日在网上就有了相关的解答攻略，还是在V2EX上，于是出现了好些只花了几分钟就做完了的人。不过好在事先我就预料到了这个事，事先预备好了“反作弊分析”的脚本，细节不想说太多，反正就是说，我会记录你答案的整个过程和行为，以此来确保TOP100中的人基本都是用自己能力答的，当然，可能会有漏判，但至少也是写过代码的。</p>
<h4>活动心得</h4>
<p>因为是第一次做活动，所以有很多感想，下面写下一些做这个活动的心得，供大家参考：</p>
<p><strong>1）要做好一个这样的解题游戏并不简单</strong>。</p>
<ul>
<li><strong>关卡设计：</strong>最花力气的地方就是设计每个关卡，我不能设计得太过隐晦，也不能设计得太过明显。最好是要符合参与者的能力，但又要高于平均以上水平的能力，最好在90%以上。这样会让大家有挑战感，但是又不会有挫败感。这个度相当难把握。总体而言，本次设计的谜题中还有很多可以改进的地方。但这毕竟是我的第一次，也算是我用其来感受一下应该怎么设计游戏。</li>
</ul>
<ul>
<li><strong>游戏黏性：</strong>除了设计谜题，还需要针对用户可能会答错的地方来给用户一些提示，原因也是为了不让用户有挫败感，虽然用户没有答对，但是需要用这些页面来鼓励用户You made some progress，这个很重要。这会让用户对游戏更有粘性，并且更愿意有更多的投入。找到这些地方也不是一件容易的事，因为做为游戏的设计者来说，很难从一个不知到答案的角度去思考。所以需要试玩，在fun.coolshell.cn正式release之前，我找了几个人比较聪明的人来试玩了一下，对这个游戏的帮助很大。</li>
</ul>
<ul>
<li><strong>游戏管理：</strong>这样的一个在线游戏自然会出一些作弊者，为了游戏的公平性，你需要剔除这些作弊者。所以，我设计了一些比较简单的记录用户所有过程的监测的算法。通过cookie和后台的http log来一同分析。这个部分也比较地花时间。我上周六的时候写这些代码写到了凌晨4点，导致脑子不清楚，出了些bug，导致在大家游戏过程中重置cookie等伤害用户体验的事件。所以说啊，不能赶啊，也不能加班啊。</li>
</ul>
<p><strong>2）关于怎么做一个活动的感想。</strong></p>
<ul>
<li><b>这次活动的背景</b>。首先，想做这个活动的起因是这样的。我一个朋友在微博上做活动——“转发微博或@几个人怎么怎么滴就有机获得什么什么的”，<strong>我在这里把这种活动简称为“转就送”活动</strong>。于是遭到了水军的刷奖品，导致他根本分不清楚哪些是正常人，哪些不是，因为新浪微博上有大量的这要瓣机器人，所以他这次活动最后失败了。我说，你得加点难度啊，要加点智商啊。<strong>而且，我看过太多的活动都是这样的，而且很多公司的活动也是这样的，我觉得太low了</strong>。于是，我就萌生了自己尝试一下的念头。</li>
</ul>
<ul>
<li><strong>我对做活动的理解</strong>。我一直觉得网上那些诸如“转就送”或是“抽奖”这样的活动都比较SB，这些人根本就不知道怎么做活动。这样做活动不需要智商，简单粗暴，效果一点也不好，活动做完了，人就走了，人们马上就忘了。我以为做活动的精髓是这样的：</li>
</ul>
<ul>
<ul>
<li><strong>真正的价值</strong>。其实，好的活动并不只是物品的价格，而是参与这个过程的感觉和体会。如果你让人觉得这是碰运气的，那么这个活动除了用物品价格来吸引人，也就没别的什么了。<strong>如果这个活动的参与过程是让人有成就感的，要有成就感那么就需要有一定难度的挑战，而且这种挑战也是让众人认可和佩服的，那么这个奖品的价格再小，价值也会很大</strong>。比如：Olympic Game，World Cup之流的，世界顶尖，四年一次，来之不易。这才是活动的价值。本次的fun.coolshell.cn上的活动，我希望让大家在做题的过程中学到一些东西，另外也希望做出来的人有一种成就感。</li>
</ul>
</ul>
<ul>
<ul>
<li><strong>让人有回味</strong>。那些简单的“转就送”式的活动不会让人产生任何的回味，只会让人产生很大的反感。就像那些“让你转发，不转就死全家”的东西，相当的让人反感。真正的回味是人们对活动参与过程的讨论和交互。在fun.coolshell.cn上线后，我就看到好几个社区在讨论这些谜题，这就是所谓的回味。<strong>只有人们对过程的回味，对参与的回味，才会让这个活动真正的成功</strong>。</li>
</ul>
</ul>
<ul>
<ul>
<li><strong>暴露活动过程</strong>。有挑战的活动，一定要有一个Who&#8217;s Who的东西，而且是随时动态更新的可以让大家查询的，这样才会从另一个侧面激发大家的热情。因为fun.coolshell.cn一开始说了只给前十个人送东西，结果在过程中，我发现了就半天时间就差不多满了，那时我在想，如果没有奖品了，剩下的人还会不会玩了？于是我飞快地开发了一个TOP100的排行榜，让大家可以看得到这个过程，虽然前十以后就没有奖品了，但是，能上这TOP100也不错。于是乎，在没有奖品情况下，依然在激发着大家的解题热情。<strong>有竞争总是一件有意思的事情，因为成就感总是来自竞争</strong>。（注：为什么top100中会有“xxxxxx”的用户，因为一开始我用的是用户提交的name，但是后来有人告诉我，这个名字可能是真名，所以，我就改成了weibo或twitter的ID，而xxxxx则是没有留下微博或twitter的）</li>
</ul>
</ul>
<p>最后吐个槽，<strong>我真的觉得那些“纯靠运气的活动”相当的SB，我看到好些公司的运营部门招了多少个所谓的高学历和高能力的人，结果干出来的运营活动的水平，其实，也就是个有小学文化水平的人就可以做的了</strong>。那些“转就送式的”、“抽奖式的”的活动，是个人都会干，根本不需要高学历的人。</p>
<h4>其它</h4>
<p>1）<strong>本次活动中，有一个隐藏关卡，还没有人找出来</strong>。要能达到隐藏关卡，需要完成所有的题目。</p>
<p>2）<strong>活动的通关页是HelloWorld，这意味着——这仅仅是个开始</strong>。</p>
<p>最后感谢大家为这个活动付出的时间！</p>
<p>（全文完）</p>
<p>&nbsp;<!--



<p align="center"><a href= target=_blank><img decoding="async" src=""></a></p>





<p align="center"><img decoding="async" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg"> <img decoding="async" loading="lazy" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg" width="300" height="300"> <br />关注CoolShell微信公众账号和微信小程序</p>

 

--></p>
<div style="margin-top: 15px; font-size: 16px;color: #cc0000;">
<p align="center"><strong>（转载本站文章请注明作者和出处 <a href="https://coolshell.cn/">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>
</div>

<div class="wp_rp_wrap  wp_rp_vertical_m" ><div class="wp_rp_content"><h3 class="related_post_title">相关文章</h3><ul class="related_post wp_rp"><li ><a href="https://coolshell.cn/articles/18360.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg" alt="程序员练级攻略（2018)  与我的专栏" width="150" height="150" /></a><a href="https://coolshell.cn/articles/18360.html" class="wp_rp_title">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href="https://coolshell.cn/articles/17998.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2017/07/systemd-1-150x150.jpeg" alt="Linux PID 1 和 Systemd" width="150" height="150" /></a><a href="https://coolshell.cn/articles/17998.html" class="wp_rp_title">Linux PID 1 和 Systemd</a></li><li ><a href="https://coolshell.cn/articles/12103.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2014/11/tux-fork-150x150.gif" alt="vfork 挂掉的一个问题" width="150" height="150" /></a><a href="https://coolshell.cn/articles/12103.html" class="wp_rp_title">vfork 挂掉的一个问题</a></li><li ><a href="https://coolshell.cn/articles/11832.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2014/08/538efefbgw1eiz9cvx78fj20rm0fmdi8-150x150.jpg" alt="【活动】解迷题送礼物" width="150" height="150" /></a><a href="https://coolshell.cn/articles/11832.html" class="wp_rp_title">【活动】解迷题送礼物</a></li><li ><a href="https://coolshell.cn/articles/9104.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2013/02/sed-superman-150x150.png" alt="sed 简明教程" width="150" height="150" /></a><a href="https://coolshell.cn/articles/9104.html" class="wp_rp_title">sed 简明教程</a></li><li ><a href="https://coolshell.cn/articles/9070.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2013/02/awk-150x150.jpg" alt="AWK 简明教程" width="150" height="150" /></a><a href="https://coolshell.cn/articles/9070.html" class="wp_rp_title">AWK 简明教程</a></li></ul></div></div>The post <a href="https://coolshell.cn/articles/11847.html">谜题的答案和活动的心得体会</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></content:encoded>
					
					<wfw:commentRss>https://coolshell.cn/articles/11847.html/feed</wfw:commentRss>
			<slash:comments>98</slash:comments>
		
		
			</item>
		<item>
		<title>Unix考古记：一个“遗失”的shell</title>
		<link>https://coolshell.cn/articles/9410.html</link>
					<comments>https://coolshell.cn/articles/9410.html#comments</comments>
		
		<dc:creator><![CDATA[Leo]]></dc:creator>
		<pubDate>Fri, 26 Apr 2013 14:29:56 +0000</pubDate>
				<category><![CDATA[C/C++语言]]></category>
		<category><![CDATA[Unix/Linux]]></category>
		<category><![CDATA[操作系统]]></category>
		<category><![CDATA[轶事趣闻]]></category>
		<category><![CDATA[Compiler]]></category>
		<category><![CDATA[Interpreter]]></category>
		<category><![CDATA[Ken Thompson]]></category>
		<category><![CDATA[Shell]]></category>
		<category><![CDATA[Unix]]></category>
		<guid isPermaLink="false">http://coolshell.cn/?p=9410</guid>

					<description><![CDATA[<p>(感谢网友Leo投递此文) 谨以此文纪念伟大的计算机科学巨匠Ken Thompson和Dennis Ritchie，并同时向其他所有为Unix发展做出贡献的黑客...</p>
<p class="read-more"><a class="btn btn-default" href="https://coolshell.cn/articles/9410.html"> Read More<span class="screen-reader-text">  Read More</span></a></p>
The post <a href="https://coolshell.cn/articles/9410.html">Unix考古记：一个“遗失”的shell</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></description>
										<content:encoded><![CDATA[<p><script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158"
     crossorigin="anonymous"></script><span style="color: #cc0000;"><strong>(感谢网友Leo投递此文)</strong></span></p>
<p>谨以此文纪念伟大的计算机科学巨匠<a href="http://en.wikipedia.org/wiki/Ken_Thompson" target="_blank">Ken Thompson</a>和<a href="http://en.wikipedia.org/wiki/Dennis_Ritchie" target="_blank">Dennis Ritchie</a>，并同时向其他所有为Unix发展做出贡献的黑客致敬。</p>
<h4>历史的尘埃</h4>
<p>Unix作为一个举世闻名的操作系统已有40余年的历史，围绕着这个古老的操作系统的发展又衍生出了一系列外围软件生态群，其中一个非常重要的组件就是shell。<strong>它是操作系统最外层的接口，负责直接面向用户交互并提供内核服务，</strong>包括命令行接口(CLI)或图形界面接口(GUI)两种形式。以CLI为例，它提供一套命令规范，是一种解释性语言，将用户输入经过解释器(interpreter)输出使其转化成真正的系统调用，实现人机交互的功能。</p>
<p>和操作系统一样，shell也经历了一个漫长的演变史。如今大部分资料讲述最古老的shell都是从1977年的<a href="http://en.wikipedia.org/wiki/Bourne_shell" target="_blank">Bourne Shell</a>说起的，它最初移植到<a href="http://en.wikipedia.org/wiki/Version_7_Unix" target="_blank">Unix V7</a>上，被追认整个shell家族成员的鼻祖，后来的种群都是从其身上分支出来的。</p>
<p><img decoding="async" class="aligncenter" alt="Linux shells since 1977 " src="https://www.ibm.com/developerworks/linux/library/l-linux-shells/figure1.gif" /></p>
<p>对于1977年之前的历史很多资料大多一笔带过或略过不提。事实上，第一个移植到Unix上的shell却不是<a href="http://en.wikipedia.org/wiki/Stephen_Richard_Bourne" target="_blank">Steve Bourne</a>写的，早在1975年5月，贝尔实验室就对外发布了第一个广泛传播的Unix版本——<a href="http://en.wikipedia.org/wiki/UNIX_V6" target="_blank">Unix V6</a>（之前开发的版本只供内部研究之用），其根目录下的/bin/sh是第一个Unix自带的shell，由Ken Thompson写的，因此也被称为<a href="http://en.wikipedia.org/wiki/Thompson_shell" target="_blank">Thompson Shell</a>。甚至，更早可以追溯到1971年的时候，Thompson Shell就作为一个独立于内核的应用程序而实现了，只不过从1975年正式问世到1977年被取代，短短两年的寿命使得它很少为大多数人所认识。</p>
<p><span id="more-9410"></span></p>
<p>关于Thompson Shell被取代的原因在后文中会给出说明，这里着重介绍一下该shell本身的一些技术细节。坦白讲，关于Thompson Shell的资料有点稀缺，但至少还能从网上找到<a href="http://minnie.tuhs.org/Archive/PDP-11/Distributions/research/Dennis_v6/" target="_blank">源代码</a>和<a href="http://minnie.tuhs.org/cgi-bin/utree.pl?file=V6/usr/man" target="_blank">在线文档</a>。Thompson Shell本身是由一个不足900行代码的解释器和一些外部命令工具组件(utilities)构成，用<a href="http://en.wikipedia.org/wiki/K%26R_C#K.26R_C" target="_blank">K&amp;R C</a>写成，下面给出各个组件的相关源码和文档链接。</p>
<ul>
<li><strong>解释器sh</strong>：解析各种shell命令，包括内置命令和外部命令；源码sh.c；安装路径/bin/sh；手册<a href="http://minnie.tuhs.org/cgi-bin/utree.pl?file=V6/usr/man/man1/sh.1" target="_blank">sh(1)</a>。</li>
</ul>
<ul>
<li><strong>内置命令</strong>手册包括<a href="http://minnie.tuhs.org/cgi-bin/utree.pl?file=V6/usr/man/man1/chdir.1" target="_blank">chdir(1)</a>，<a href="http://minnie.tuhs.org/cgi-bin/utree.pl?file=V6/usr/man/man1/login.1" target="_blank">login(1)</a>，<a href="http://minnie.tuhs.org/cgi-bin/utree.pl?file=V6/usr/man/man1/newgrp.1" target="_blank">newgrp(1)</a>，<a href="http://minnie.tuhs.org/cgi-bin/utree.pl?file=V6/usr/man/man1/shift.1" target="_blank">shift(1)</a>，<a href="http://minnie.tuhs.org/cgi-bin/utree.pl?file=V6/usr/man/man1/wait.1" target="_blank">wait(1)</a>。</li>
</ul>
<p>下面是外部命令：</p>
<ul>
<li><strong>exit命令</strong>：退出一个文件；源码exit.c；安装路径/bin/exit；手册<a href="http://minnie.tuhs.org/cgi-bin/utree.pl?file=V6/usr/man/man1/exit.1" target="_blank">exit(1)</a>。</li>
</ul>
<ul>
<li><strong>goto命令</strong>：在一个文件内跳转shell控制流程；源码goto.c；安装路径/bin/goto；手册<a href="http://minnie.tuhs.org/cgi-bin/utree.pl?file=V6/usr/man/man1/goto.1" target="_blank">goto(1)</a>。</li>
</ul>
<ul>
<li><strong>if命令</strong>：条件判断表达式，是test命令的前身；源码if.c；安装路径/bin/if), 手册<a href="http://minnie.tuhs.org/cgi-bin/utree.pl?file=V6/usr/man/man1/if.1" target="_blank">if(1)</a>。</li>
</ul>
<ul>
<li><strong>glob命令</strong>：扩展命令参数通配符；源码glob.c；安装路径/etc/glob；手册<a href="http://minnie.tuhs.org/cgi-bin/utree.pl?file=V6/usr/man/man8/glob.8" target="_blank">glob(8)</a>。</li>
</ul>
<h4>命令结构和规范</h4>
<p>尽管后来遭“埋汰”，Thompson Shell仍有着不容否认的历史地位，其最大的价值在于<strong>它奠定了shell命令语言结构和规范的基础，而且其解释器具有跨平台的可移植性，并影响到了后来包括Bourne Shell在内的各种脚本语言设计实现。</strong>下面我们就以其中5个特性重温一些大家已经耳熟能详的命令规范，你也可以通过<a href="http://minnie.tuhs.org/cgi-bin/utree.pl?file=V6/usr/man/man1/sh.1" target="_blank">sh(1)</a>手册查看原始资料。</p>
<ul>
<li><strong>过滤器/管道线(filter/pipeline)。</strong>这绝对是要载入Unix史册的发明，创立者是<a href="http://en.wikipedia.org/wiki/Douglas_McIlroy" target="_blank">Douglas McIlroy</a>，Thompson Shell引入并实现了这个伟大的概念——一个或多个命令组成一根过滤器的链条，由&#8217;|&#8217;或&#8217;^&#8217;符号分隔。除最后一个命令之外，每个命令的标准输出都被作为下一个命令的标准输入。这样每个命令都作为一个独立的进程来运行，并通过管道与邻近的进程相连接。圆括弧内的命令序列整体上可以替代单个命令作为过滤器实现，比如用户可以输入&#8221;(A;B)|C&#8221;。</li>
</ul>
<ul>
<li><strong>命令序列和后台进程。</strong>分号&#8217;;&#8217;指示多个命令序列化执行。&#8217;&amp;&#8217;符号指示该命令在后台异步执行，使得前面的管道线不必等待其终止，仅仅报告一个进程id，这样用户以后可以通过kill命令与它通信。有益于进程管理。</li>
</ul>
<ul>
<li><strong>I/O重定向。</strong>它利用了Unix设计上的一个重要特性——<strong>一切皆文件</strong>，用三个符号表示：&#8221;重定向输出，如果文件不存在则创建它，如果文件存在则截断它；&#8217;&gt;&gt;&#8217;追加模式重定向输出，如果文件不存在则创建它，如果文件存在则追加输出至末尾处。</li>
</ul>
<ul>
<li><strong>通配符扩展(globbing)。</strong>通配符的概念源自于正则表达式，使得解释器智能地处理用户不完全输入，比如记不清文件名、一次性输入多个文件等。&#8217;?&#8217;匹配任意单一字符；&#8217;*&#8217;匹配任意字符串（包括空串）；成对'[&#8216;和&#8217;]&#8217;定义了字符集合一个类，可匹配方括号内任意成员，用&#8217;-&#8216;两端可指定一系列连续字符匹配范围。</li>
</ul>
<ul>
<li><strong>参数传递。</strong>这里主要引入了位置参数和选项参数的概念：&#8217;$n&#8217;指示shell调用的第n个参数替代；还定义了两个选项参数&#8217;-t&#8217;和&#8217;-c&#8217;，前者用于交互，导致shell从标准输入中读入一行作为用户执行的系统命令，后者指示shell将附带的下一个参数作为命令执行（可正确处理换行符），是对&#8217;-t&#8217;的补充，特别是调用者已经读取了命令其中某些字符的情况下。如果不带选项参数则直接读取文件名</li>
</ul>
<h4>解释器的原理与实现</h4>
<p>接下来马上要进入核心部分了，为了搞懂shell解释器原理，我们要对其整个工作流程做个描述（这里给出一份带注解的sh.c源码剖析）。读过《编译原理》的同学知道，解释器的实现跟编译器差不多，只不过省略了生成目标代码这一步，直接将用户输入（shell命令）转化成输出（系统调用）。<strong>软件前端是一致的，包括预处理、词法扫描、语法分析和语义分析，最后还要附加一个进程管理。</strong>当然相较于现代编译器，Thompson Shell解释器在算法和规模上都要简单得多，不过原理上是相通的，何况年代上要比Lex &amp; Yacc还要早。麻雀虽小，五脏俱全，对于初学者来说，从Thompson Shell去入手编译原理或许不失为一种好选择。</p>
<h4>预处理(preprocessor)</h4>
<p>同C预处理器需要事先将源代码中包含的宏和头文件展开一样，Thompson Shell首先需要处理命令中的<strong>选项参数</strong>和<strong>位置参数</strong>。选项参数有两种&#8217;-t&#8217;和&#8217;-c&#8217;，决定了shell从标准输入还是参数缓存中读取字符（见<a href="http://minnie.tuhs.org/cgi-bin/utree.pl?file=V6/usr/man/man1/sh.1" target="_blank">sh(1)</a>）。此外字符序列中还要处理<strong>反斜杠&#8217;\&#8217;</strong>，判断是转义字符还是行接续符，前者对下一个字符设置引用标识，表明做普通字符处理，后者将紧邻其后换行符过滤掉。</p>
<p>位置参数是<strong>美元符号&#8217;$&#8217;</strong>打头的，后带一个数字，如&#8217;$n&#8217;，预处理器对shell命令参数从头开始计数，返回数字n指定的参数位置。如果遇上double&#8217;$$&#8217;，则表示当前的进程标识，调用getpid()获取。</p>
<p>注意到预处理器需要一次读取多个字符，这样就会多读一个不必要的字符。对此解释器提供了一种<strong>预读(peek)</strong>方式，即每次从输入流读取一个字符时，放入一个预读缓存里（只有一个int大小的堆栈），也叫<strong>回退(push back)</strong>。此后先从预读缓存中读取，如果缓存被读完，则从输入流中读取。</p>
<h4>词法扫描(lexical scanning)</h4>
<p>经过预处理后的字符序列将被切割成为一系列<strong>词法记号(token)</strong>，安置在token列表中，扫描器将对以下几类字符做如下处理。</p>
<ul>
<li><strong>空格和tab</strong>：简单过滤。</li>
</ul>
<ul>
<li><strong>引号</strong>：需要成对出现，字符本身被过滤，一对引号之间所有字符都被设置引用标识，作为一个token。</li>
</ul>
<ul>
<li><strong>元字符</strong>：如&#8217;&amp;&#8217;，&#8217;|&#8217;等，字符本身作为一个单独token。</li>
</ul>
<ul>
<li><strong>其他字符</strong>：一律填充token，直到碰上以上字符分隔为止。</li>
</ul>
<p>举一个例子，当我们输入命令&#8221;(ls; cat tail) &gt;junk&#8221;，那么token列表映像将是这样的：</p>
<p style="text-align: center;"><img decoding="async" loading="lazy" class="aligncenter  wp-image-9537" alt="" src="https://coolshell.cn/wp-content/uploads/2013/04/图1.jpg" width="523" height="176" srcset="https://coolshell.cn/wp-content/uploads/2013/04/图1.jpg 872w, https://coolshell.cn/wp-content/uploads/2013/04/图1-300x101.jpg 300w" sizes="(max-width: 523px) 100vw, 523px" /></p>
<h4>语法分析(syntax parser)</h4>
<p>语法分析就是将token列表中的元素作为<strong>表达式(expression)</strong>并以节点为单位构建语法树，简单命令是一个表达式，而复合命令以及命令序列是多个表达式的组合。Thompson Shell中以简单数组作为语法树的容器，实际上这是结构体的一种变形，只不过每个成员字段大小都一样（都是sizeof int）而已。一个语法树节点最多有6个字段（大小根据类型可变），分别是</p>
<ul>
<li><strong>DTYP（节点类型）</strong>：每个节点都有唯一的类型，又分为四种——TCOM（简单命令）、TPAR（复合命令）、TFIL（过滤器/管道线）、TLST（命令序列）。</li>
</ul>
<ul>
<li><strong>DLEF（左子树节点）</strong>：相当于链表指针，根据DTYP定义有所不同。如过滤器类型左子树节点为前一个命令的输出重定向文件，右子树节点为后一个命令的输入重定向文件。</li>
</ul>
<ul>
<li><strong>DRIG（右子树节点）</strong>：同上。</li>
</ul>
<ul>
<li><strong>DFLG（节点属性）</strong>：这是个标志位(flag)，决定该节点包含命令的属性以及以什么样的状态执行。</li>
</ul>
<ul>
<li><strong>DSPR（子命令）</strong>：两重含义，对于简单命令，该字段为空；对于复合命令，该字段指向子语法树节点。</li>
</ul>
<ul>
<li><strong>DCOM（命令字符）</strong>：引用命令字符序列。</li>
</ul>
<p>语法树节点生成顺序根据token列表中每个元素的<strong>优先级(priority)</strong>而定，首先遍历整个列表，找到优先级最高的token作为根节点，再分别生成左右子树，这是一种最简单的<strong>自顶向下(top-down)</strong>解决方案。各个token优先级视DTYP字段而定</p>
<table class="aligncenter" width="367" border="1" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td valign="top" width="91">
<p align="center">优先级</p>
</td>
<td valign="top" width="180">
<p align="center">Token</p>
</td>
<td valign="top" width="96">
<p align="center">DTYP</p>
</td>
</tr>
<tr>
<td valign="top" width="91">
<p align="center">第一级</p>
</td>
<td valign="top" width="180">
<p align="center">&#8216;&amp;&#8217;  &#8216;;&#8217;  &#8216;\n&#8217;</p>
</td>
<td valign="top" width="96">
<p align="center">TLST</p>
</td>
</tr>
<tr>
<td valign="top" width="91">
<p align="center">第二级</p>
</td>
<td valign="top" width="180">
<p align="center">&#8216;|&#8217;  &#8216;^&#8217;</p>
</td>
<td valign="top" width="96">
<p align="center">TFIL</p>
</td>
</tr>
<tr>
<td valign="top" width="91">
<p align="center">第三级</p>
</td>
<td valign="top" width="180">
<p align="center"> &#8216;(&#8216;  &#8216;)&#8217;</p>
</td>
<td valign="top" width="96">
<p align="center">TPAR</p>
</td>
</tr>
<tr>
<td valign="top" width="91">
<p align="center">第四级</p>
</td>
<td valign="top" width="180">
<p align="center">其它字符</p>
</td>
<td valign="top" width="96">
<p align="center">TCOM</p>
</td>
</tr>
</tbody>
</table>
<p>语法树的构建过程中还使用了一种基于<strong>“有限状态机(finite-state machine)”</strong>的动态规划算法，其实现是将整个逻辑流程划分为四个状态：syntax、syn1、syn2、syn3，对应于上面token优先级，程序在每个状态下都生成一个相应类型的节点，同时还生成四种策略，以决议下一步将转移到何种状态（根据优先级搜索对应的token）。这个四种策略分别是</p>
<ul>
<li><strong>生成左子树</strong>：左边token列表递进到下层状态。</li>
</ul>
<ul>
<li><strong>生成右子树</strong>：右边token列表并回溯到上层状态或递归调用。</li>
</ul>
<ul>
<li><strong>找不到对应token</strong>：保持原有token列表递进到下层状态。</li>
</ul>
<ul>
<li><strong>生成节点</strong>：直接返回节点。</li>
</ul>
<p>当我们遍历完整个token列表后，程序总是能返回最初的调用点，即根节点上，从而生成一棵完整的语法树。这种算法的好处是<strong>程序员不必关注具体实现的每个细枝末节，只要关注相应的状态并制定对应的转移策略即可。</strong>还值得一提的是每个转移策略都是发生在赋值语句或返回语句上，并使用函数实参保存临时变量，这样就避免了调用次数过多导致堆栈溢出。</p>
<p>依旧举两个个例子，比如命令&#8221;A &amp; ; B | C&#8221;对应的语法树</p>
<p style="text-align: center;"><img decoding="async" loading="lazy" class="aligncenter  wp-image-9538" alt="" src="https://coolshell.cn/wp-content/uploads/2013/04/图2.jpg" width="350" height="264" srcset="https://coolshell.cn/wp-content/uploads/2013/04/图2.jpg 546w, https://coolshell.cn/wp-content/uploads/2013/04/图2-300x226.jpg 300w" sizes="(max-width: 350px) 100vw, 350px" /></p>
<p>命令&#8221;(A ; B) | C&#8221;对应的语法树：</p>
<p style="text-align: center;"><img decoding="async" loading="lazy" class="aligncenter  wp-image-9539" alt="" src="https://coolshell.cn/wp-content/uploads/2013/04/图3.jpg" width="350" height="345" srcset="https://coolshell.cn/wp-content/uploads/2013/04/图3.jpg 584w, https://coolshell.cn/wp-content/uploads/2013/04/图3-300x295.jpg 300w" sizes="(max-width: 350px) 100vw, 350px" /></p>
<h4>语义分析(Semantic Analyzer)</h4>
<p>语法分析仅仅停留在token表达式合法性层面上，它并不知道该表达式是否有意义，比如哪些命令是要后台运行，哪些命令的I/O被重定向到管道线上，通配符该如何扩展等等，这时候要靠语义分析了。这里的“语义”体现在对特殊字符的动态处理以及语法树节点的字段设置，根据<strong>上下文(context)</strong>而定。比如对于元字符&#8217;&gt;&#8217;，我们要判断输出重定向到哪个文件，是截断还是追加。对于通配符&#8217;?&#8217;、&#8217;*&#8217;和'[&#8230;]&#8217;，我们要决定对哪些字符进行扩展，这些在/etc/glob中专门处理。对于语法树节点，除了自身固有属性之外，还需要继承上层节点的属性，以及下推属性到下层子树节点，下面列了一张表格说明。</p>
<table border="1" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td valign="top" width="67">
<p align="center">DTYP</p>
</td>
<td valign="top" width="217">
<p align="center">DLEF/DRIG</p>
</td>
<td valign="top" width="227">
<p align="center">DFLG</p>
</td>
<td valign="top" width="57">
<p align="center">DSPR</p>
</td>
</tr>
<tr>
<td valign="top" width="67">
<p align="center">TLST</p>
</td>
<td valign="top" width="217">可以为空，也可以是其它节点，类型可以是TLST/TFIL/TCOM</td>
<td valign="top" width="227">自身属性为0；如果带&#8217;&amp;&#8217;，则下推属性FINT|FAND|FPRS到左右子树（忽略信号、后台异步，打印pid）</td>
<td valign="top" width="57">空</td>
</tr>
<tr>
<td valign="top" width="67">
<p align="center">TFIL</p>
</td>
<td valign="top" width="217">必须同时存在、，类型只能是TCOM或TPAR</td>
<td valign="top" width="227">自身属性继承自上层TLST；下推FPIN到左子树节点；下推FPOU到右子树节点。</td>
<td valign="top" width="57">空</td>
</tr>
<tr>
<td valign="top" width="67">
<p align="center">TPAR</p>
</td>
<td valign="top" width="217">空</td>
<td rowspan="2" valign="top" width="227">继承上层的TLST和TFIL；如果是追加模式重定向输出，加上FCAT；如果是复合命令中最后一个子命令，加上FPAR， 将不会fork子进程。</td>
<td valign="top" width="57">子命令</td>
</tr>
<tr>
<td valign="top" width="67">
<p align="center">TCOM</p>
</td>
<td valign="top" width="217">左子树节点为输入重定向文件，右子树为节点输出重定向文件。</td>
<td valign="top" width="57">空</td>
</tr>
</tbody>
</table>
<h4>执行命令(Executor)</h4>
<p>当前面一系列步骤之后，如果错误计数为0，则解释器从语法树的根节点开始，<strong>深度优先遍历</strong>所有节点，并根据前面语法和语义分析得到的类型和属性，一一执行所包含的命令，以生成最后的系统调用。</p>
<p>对于<strong>命令序列(TLST)节点</strong>，从左至右顺序执行子树节点命令。</p>
<p>对于<strong>过滤器(TFIL)节点</strong>，创建管道文件句柄，作为左右子树的重定向文件。</p>
<p>对于<strong>简单命令(TCOM)和复合命令(TPAR)节点</strong>，首先筛选出系统内置命令(built-in)，对于剩下的外部命令则fork一个子进程执行它。如果是复合命令中最后一个子命令，那么仍在原来的进程上执行而不必创建新进程。可执行文件路径按先后顺序搜索：①本地路径；②/bin；③/usr/bin。</p>
<p><strong>多进程环境下，特别要注意文件句柄管理</strong>。命令间共享标准输入输出设备之外，还会重定向到管道线，而父进程在fork之后子进程会获取一份文件句柄拷贝，所以<span style="color: #ff0000;"><strong>父进程必须在fork之后立即关闭闲置的管道线句柄（如果有的话）以免造成资源泄漏，子进程也将在重定向之后关闭管道线句柄。</strong></span></p>
<p>对于<strong>后台命令</strong>需要打印pid，但不需要响应中断信号，父进程也不必等待子进程终止。其余进程命令执行中可捕获中断信号，并转入相应的处理函数。</p>
<p>解释器用内置的errno全局变量保存进程终止状态，并生成<strong>终止报告(termination report)</strong>，系统调用wait()用于返回终止进程的pid并输出报告消息索引。</p>
<h4>孰优孰劣</h4>
<p>尽管Thompson Shell是一款优秀的命令解释器，还产生了多项历史创举，但遗憾的是依然得不到命运女神的垂青，这要归咎于其自身的缺陷——<strong>功能单一、命令分散、控制流过于简单，尚无法用来编写脚本(script)</strong>。随着Unix日益壮大，它已经无法应付趋于繁杂的编程项目了。那时还出现了一个叫<a href="http://en.wikipedia.org/wiki/John_Mashey" target="_blank">John Mashey</a>的人写的<a href="http://en.wikipedia.org/wiki/PWB_shell" target="_blank">PWB Shell</a>（又叫做Mashey Shell），基于Thompson Shell做了些改进，扩展了命令集，增加了shell变量，还增加了if-then-else-endif，for，while等控制逻辑。不幸的是它比Thompson Shell更短命，因为1977年它遇上了一个强劲的对手。</p>
<p>没错，那就是Bourne Shell，它的主要优点是真正实现了结构化脚本编程，比之前的shell实现得都要好，更要命的是它与前两个shell都不兼容，于是一场标准化的论战开始了。在<a href="http://en.wikipedia.org/wiki/David_Korn_(computer_scientist)" target="_blank">David G. Korn</a>（<a href="http://en.wikipedia.org/wiki/Korn_shell" target="_blank">ksh</a>作者）写的<a href="http://www.in-ulm.de/~mascheck/bourne/korn.html" target="_blank">&#8220;ksh &#8211; An Extensible High Level Language&#8221;</a>一文中提及，Steve Bourne和John Mashey在三次连续的Unix用户组集会上争论他们各自的理由。在这些集会之间，各自增进他们的shell来拥有对方的功能。还设立了一个委员会来选择标准shell，最终还是选择了Bourne shell作为标准。</p>
<p>于是从Unix V7开始就有了前面所说的&#8221;Bourne Shell Family&#8221;。然而历史上没有完美的技术，随着八、九十年代操作系统迅猛发展，针对Bourne Shell的诟病也越来越多了。在解释器本身实现上，我看到网上一个对其评价是<a href="http://lwn.net/Articles/471015/" target="_blank">&#8220;universally considered to be one of the most horrible C code ever written&#8221;</a>，至于原因去看一下mac.h就知道了，包括基本运算符、关键字在内的大量宏定义使得整个代码看上去简直不是C写的，也许Bourne是想把解释器打造成自己独特的风格吧，也难怪后来的bash以<strong>&#8220;born again&#8221;</strong>命名就是对其祖先的戏谑性调侃。另外<a href="http://www.in-ulm.de/~mascheck/bourne/segv.html" target="_blank">内存管理</a>上的一些毛病带来平台可移植性问题，至于其中的技术细节有点高级，超出本文范畴。</p>
<h4>Thompson Again Shell?</h4>
<p>虽然历史没有给Thompson Shell一个机会，但它并非就此同Unix V6那样一同沦为开源博物馆上的古老“化石”。作为出自顶级黑客之手的作品，作为伴随Unix那样伟大操作系统一同曾经流行计算机的产物，至今仍受国内外程序员的缅怀，或将其改写，或为其作注。比如国外一个站点<a href="http://v6shell.org/" target="_blank">v6shell.org</a>上就实现了一个免费开源的可移植性shell，它兼容并扩充原来的Thompson Shell并且可用来做脚本编程。再比如中国程序员<a href="http://blog.chinaunix.net/uid-20106293-id-142129.html" target="_blank">寒蝉退士</a>在其个人博客上发布了一个注解版，并对原版做了一些改写，主要是将<strong>K&amp;R C</strong>转为<strong>ANSI C</strong>，并且符合<strong>POSIX规范</strong>，使原本晦涩难懂的源码变得清晰易读起来。正是因为接触到他的版本激起了我对老Unix的考古兴趣，才有了这篇“考古笔记”。我在想不知今后会不会像bash那样，出一个tash来呢？</p>
<h4>一些感想</h4>
<p>本来全文应该就此结束了，但此时此刻不禁想多说几句。这篇笔记当初并非有意而为之，在hacking源码的过程中感想积累多了也就逐渐成章了。看代码、作注解、查资料、写此文，前后历经四个多礼拜，是在繁杂的工作中“挤乳沟”挤出来的零散时间片拼凑起来的，虽然文字不长但也算耗费了一番心血，酸甜苦辣心中自明，体会到踏上社会之后潜下心做研究之艰难。如今面对这样一份不到900行写成的，没有一行多余的代码，<strong>简洁(clarity)、干净(clean)、快速(fast)，</strong>这就是Pure C的魅力，我深为这种厚重的编程功力所折服，正所谓<strong>“大道至简”</strong>吧。虽然要完全弄懂它需要很多时间，但我相信这种代价却是值得的。</p>
<p>最后再八卦一下，2011年Dennis Ritchie去世了，有人生前问过他“学C需要多久才能成为熟练开发者并写出重要产品代码？”，Ritchie回答“我不知道，我从没去学过C。”<a href="http://www.cs.columbia.edu/~aho/Talks/12-09-07_DMR.pdf" target="_blank">(I don’t know. I never had to learn C.)</a>其实这里已经给出了答案——<strong>那就是没有比去阅读Unix源代码更好的选择了，某种意义上C语言就是为Unix而生的。</strong></p>
<p><img decoding="async" loading="lazy" class="aligncenter" alt="Dennis Mac Ritchie" src="http://th05.deviantart.net/fs71/PRE/f/2011/296/7/2/dennis_ritchie_by_juanosborne-d4dooi9.jpg" width="611" height="314" /></p>
<h4>参考资料</h4>
<p><a href="http://www.tuhs.org/" target="_blank">The Unix Heritage Society</a>：Unix社区遗产，上面有v6和v7以及其它一些衍生版本的操作系统源代码。</p>
<p><a href="http://www.in-ulm.de/~mascheck/bourne/" target="_blank">The Traditional Bourne Shell Family</a>：Bourne Shell家族简史。</p>
<p><a href="http://v6shell.org/" target="_blank">v6shell</a>：osh，一个基于Thompson Shell的开源可移植性old shell。</p>
<p><a href="http://blog.chinaunix.net/uid-20106293-id-142129.html" target="_blank">寒蝉退士的博客</a>：Thompson Shell的一个注解版。</p>
<p><a href="https://www.ibm.com/developerworks/linux/library/l-linux-shells/index.html?ca=drs-" target="_blank">Evolution of shells in Linux</a>：简述Linux Shell演变史。</p>
<p>附录一个中文注释的 <a href="https://coolshell.cn/wp-content/uploads/2013/04/shell源码.zip">shell源码</a></p>
<p>（全文完）<!--



<p align="center"><a href= target=_blank><img decoding="async" src=""></a></p>





<p align="center"><img decoding="async" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg"> <img decoding="async" loading="lazy" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg" width="300" height="300"> <br />关注CoolShell微信公众账号和微信小程序</p>

 

--></p>
<div style="margin-top: 15px; font-size: 16px;color: #cc0000;">
<p align="center"><strong>（转载本站文章请注明作者和出处 <a href="https://coolshell.cn/">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>
</div>

<div class="wp_rp_wrap  wp_rp_vertical_m" ><div class="wp_rp_content"><h3 class="related_post_title">相关文章</h3><ul class="related_post wp_rp"><li ><a href="https://coolshell.cn/articles/19996.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2019/11/ken.dennis-300x186-1-150x150.jpeg" alt="Unix 50 年：Ken Thompson 的密码" width="150" height="150" /></a><a href="https://coolshell.cn/articles/19996.html" class="wp_rp_title">Unix 50 年：Ken Thompson 的密码</a></li><li ><a href="https://coolshell.cn/articles/9070.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2013/02/awk-150x150.jpg" alt="AWK 简明教程" width="150" height="150" /></a><a href="https://coolshell.cn/articles/9070.html" class="wp_rp_title">AWK 简明教程</a></li><li ><a href="https://coolshell.cn/articles/8883.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2013/01/linux-bash-300x225-150x150.jpg" alt="应该知道的Linux技巧" width="150" height="150" /></a><a href="https://coolshell.cn/articles/8883.html" class="wp_rp_title">应该知道的Linux技巧</a></li><li ><a href="https://coolshell.cn/articles/8619.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2012/11/shell.01-150x150.png" alt="你可能不知道的Shell" width="150" height="150" /></a><a href="https://coolshell.cn/articles/8619.html" class="wp_rp_title">你可能不知道的Shell</a></li><li ><a href="https://coolshell.cn/articles/2322.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2010/04/o_unixrichiethompson-150x150.jpg" alt="Unix传奇(上篇)" width="150" height="150" /></a><a href="https://coolshell.cn/articles/2322.html" class="wp_rp_title">Unix传奇(上篇)</a></li><li ><a href="https://coolshell.cn/articles/1761.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2009/11/spell_it_with_e-150x150.jpg" alt="Go语言源码的一个改动" width="150" height="150" /></a><a href="https://coolshell.cn/articles/1761.html" class="wp_rp_title">Go语言源码的一个改动</a></li></ul></div></div>The post <a href="https://coolshell.cn/articles/9410.html">Unix考古记：一个“遗失”的shell</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></content:encoded>
					
					<wfw:commentRss>https://coolshell.cn/articles/9410.html/feed</wfw:commentRss>
			<slash:comments>26</slash:comments>
		
		
			</item>
		<item>
		<title>sed 简明教程</title>
		<link>https://coolshell.cn/articles/9104.html</link>
					<comments>https://coolshell.cn/articles/9104.html#comments</comments>
		
		<dc:creator><![CDATA[陈皓]]></dc:creator>
		<pubDate>Wed, 20 Feb 2013 00:36:48 +0000</pubDate>
				<category><![CDATA[Unix/Linux]]></category>
		<category><![CDATA[编程工具]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[sed]]></category>
		<category><![CDATA[Unix]]></category>
		<guid isPermaLink="false">http://coolshell.cn/?p=9104</guid>

					<description><![CDATA[<p>awk于1977年出生，今年36岁本命年，sed比awk大2-3岁，awk就像林妹妹，sed就是宝玉哥哥了。所以 林妹妹跳了个Topless，他的哥哥sed坐不...</p>
<p class="read-more"><a class="btn btn-default" href="https://coolshell.cn/articles/9104.html"> Read More<span class="screen-reader-text">  Read More</span></a></p>
The post <a href="https://coolshell.cn/articles/9104.html">sed 简明教程</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></description>
										<content:encoded><![CDATA[<p><script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158"
     crossorigin="anonymous"></script><img decoding="async" loading="lazy" class="alignright wp-image-9126" src="https://coolshell.cn/wp-content/uploads/2013/02/sed-superman.png" alt="" width="216" height="216" srcset="https://coolshell.cn/wp-content/uploads/2013/02/sed-superman.png 270w, https://coolshell.cn/wp-content/uploads/2013/02/sed-superman-150x150.png 150w, https://coolshell.cn/wp-content/uploads/2013/02/sed-superman-200x200.png 200w" sizes="(max-width: 216px) 100vw, 216px" />awk于1977年出生，今年36岁本命年，sed比awk大2-3岁，awk就像林妹妹，sed就是宝玉哥哥了。所以 <a title="AWK 简明教程" href="https://coolshell.cn/articles/9070.html" target="_blank" rel="noopener noreferrer">林妹妹跳了个Topless</a>，他的哥哥sed坐不住了，也一定要出来抖一抖。</p>
<p>sed全名叫stream editor，流编辑器，用程序的方式来编辑文本，相当的hacker啊。sed基本上就是玩正则模式匹配，所以，玩sed的人，正则表达式一般都比较强。</p>
<p>同样，本篇文章不会说sed的全部东西，你可以参看<a href="http://www.gnu.org/software/sed/manual/sed.html" target="_blank" rel="noopener noreferrer">sed的手册</a>，我这里主要还是想和大家竞争一下那些从手机指缝间或马桶里流走的时间，用这些时间来学习一些东西。当然，接下来的还是要靠大家自己双手。</p>
<h4>用s命令替换</h4>
<p>我使用下面的这段文本做演示：</p>
<pre data-enlighter-language="shell" class="EnlighterJSRAW">$ cat pets.txt
This is my cat
  my cat&#039;s name is betty
This is my dog
  my dog&#039;s name is frank
This is my fish
  my fish&#039;s name is george
This is my goat
  my goat&#039;s name is adam</pre>
<p>把其中的my字符串替换成Hao Chen&#8217;s，下面的语句应该很好理解（s表示替换命令，/my/表示匹配my，/Hao Chen&#8217;s/表示把匹配替换成Hao Chen&#8217;s，/g 表示一行上的替换所有的匹配）：</p>
<pre data-enlighter-language="shell" class="EnlighterJSRAW">$ sed &quot;s/my/Hao Chen&#039;s/g&quot; pets.txt
This is Hao Chen&#039;s cat
  Hao Chen&#039;s cat&#039;s name is betty
This is Hao Chen&#039;s dog
  Hao Chen&#039;s dog&#039;s name is frank
This is Hao Chen&#039;s fish
  Hao Chen&#039;s fish&#039;s name is george
This is Hao Chen&#039;s goat
  Hao Chen&#039;s goat&#039;s name is adam</pre>
<p>注意：如果你要使用单引号，那么你没办法通过\&#8217;这样来转义，就有双引号就可以了，在双引号内可以用\&#8221;来转义。</p>
<p><span id="more-9104"></span></p>
<p>再注意：上面的sed并没有对文件的内容改变，只是把处理过后的内容输出，如果你要写回文件，你可以使用重定向，如：</p>
<p><code data-enlighter-language="shell" class="EnlighterJSRAW">$ sed &quot;s/my/Hao Chen&#039;s/g&quot; pets.txt &gt; hao_pets.txt</code></p>
<p>或使用 -i 参数直接修改文件内容：</p>
<p><code data-enlighter-language="shell" class="EnlighterJSRAW">$ sed -i &quot;s/my/Hao Chen&#039;s/g&quot; pets.txt</code></p>
<p>在每一行最前面加点东西：</p>
<pre data-enlighter-language="shell" class="EnlighterJSRAW">$ sed &#039;s/^/#/g&#039; pets.txt
#This is my cat
#  my cat&#039;s name is betty
#This is my dog
#  my dog&#039;s name is frank
#This is my fish
#  my fish&#039;s name is george
#This is my goat
#  my goat&#039;s name is adam</pre>
<p>在每一行最后面加点东西：</p>
<pre data-enlighter-language="shell" class="EnlighterJSRAW">$ sed &#039;s/$/ --- /g&#039; pets.txt
This is my cat ---
  my cat&#039;s name is betty ---
This is my dog ---
  my dog&#039;s name is frank ---
This is my fish ---
  my fish&#039;s name is george ---
This is my goat ---
  my goat&#039;s name is adam ---</pre>
<p>顺手介绍一下正则表达式的一些最基本的东西：</p>
<ul>
<li> <code>^</code> 表示一行的开头。如：<code>/^#/</code> 以#开头的匹配。</li>
<li> <code>$</code> 表示一行的结尾。如：<code>/}$/</code> 以}结尾的匹配。</li>
<li> <code>&#92;&lt;</code> 表示词首。 如：<code>&#92;&lt;abc</code> 表示以 abc 为首的詞。</li>
<li> <code>&#92;&gt;</code> 表示词尾。 如：<code>abc&#92;&gt;</code> 表示以 abc 結尾的詞。</li>
<li> <code>.</code> 表示任何单个字符。</li>
<li> <code>*</code> 表示某个字符出现了0次或多次。</li>
<li> <code>&#91; &#93;</code> 字符集合。 如：<code>&#91;abc&#93;</code> 表示匹配a或b或c，还有 <code>&#91;a-zA-Z&#93;</code> 表示匹配所有的26个字符。如果其中有^表示反，如 <code>&#91;^a&#93;</code> 表示非a的字符</li>
</ul>
<p>正规则表达式是一些很牛的事，比如我们要去掉某html中的tags：</p>
<pre data-enlighter-language="html" class="EnlighterJSRAW">

&lt;b&gt;This&lt;/b&gt; is what &lt;span style=&quot;text-decoration: underline;&quot;&gt;I&lt;/span&gt; meant. Understand?

</pre>
<p>看看我们的sed命令</p>
<pre data-enlighter-language="shell" class="EnlighterJSRAW">
# 如果你这样搞的话，就会有问题
$ sed &#039;s/&lt;.*&gt;//g&#039; html.txt
 Understand?

# 要解决上面的那个问题，就得像下面这样。
# 其中的&#039;[^&gt;]&#039; 指定了除了&gt;的字符重复0次或多次。
$ sed &#039;s/&lt;[^&gt;]*&gt;//g&#039; html.txt
This is what I meant. Understand?</pre>
<p>我们再来看看指定需要替换的内容：</p>
<pre data-enlighter-language="shell" class="EnlighterJSRAW" data-enlighter-highlight="4">$ sed &quot;3s/my/your/g&quot; pets.txt
This is my cat
  my cat&#039;s name is betty
This is your dog
  my dog&#039;s name is frank
This is my fish
  my fish&#039;s name is george
This is my goat
  my goat&#039;s name is adam</pre>
<p>下面的命令只替换第3到第6行的文本。</p>
<pre data-enlighter-language="shell" class="EnlighterJSRAW" data-enlighter-highlight="4,5,6,7">$ sed &quot;3,6s/my/your/g&quot; pets.txt
This is my cat
  my cat&#039;s name is betty
This is your dog
  your dog&#039;s name is frank
This is your fish
  your fish&#039;s name is george
This is my goat
  my goat&#039;s name is adam</pre>
<p>&nbsp;</p>
<pre data-enlighter-language="shell" class="EnlighterJSRAW">$ cat my.txt
This is my cat, my cat&#039;s name is betty
This is my dog, my dog&#039;s name is frank
This is my fish, my fish&#039;s name is george
This is my goat, my goat&#039;s name is adam</pre>
<p>只替换每一行的第一个s：</p>
<pre data-enlighter-language="shell" class="EnlighterJSRAW">$ sed &#039;s/s/S/1&#039; my.txt
ThiS is my cat, my cat&#039;s name is betty
ThiS is my dog, my dog&#039;s name is frank
ThiS is my fish, my fish&#039;s name is george
ThiS is my goat, my goat&#039;s name is adam</pre>
<p>只替换每一行的第二个s：</p>
<pre data-enlighter-language="shell" class="EnlighterJSRAW">$ sed &#039;s/s/S/2&#039; my.txt
This iS my cat, my cat&#039;s name is betty
This iS my dog, my dog&#039;s name is frank
This iS my fish, my fish&#039;s name is george
This iS my goat, my goat&#039;s name is adam</pre>
<p>只替换第一行的第3个以后的s：</p>
<pre data-enlighter-language="shell" class="EnlighterJSRAW">$ sed &#039;s/s/S/3g&#039; my.txt
This is my cat, my cat&#039;S name iS betty
This is my dog, my dog&#039;S name iS frank
This is my fiSh, my fiSh&#039;S name iS george
This is my goat, my goat&#039;S name iS adam</pre>
<h4>多个匹配</h4>
<p>如果我们需要一次替换多个模式，可参看下面的示例：（第一个模式把第一行到第三行的my替换成your，第二个则把第3行以后的This替换成了That）</p>
<pre data-enlighter-language="shell" class="EnlighterJSRAW">$ sed &#039;1,3s/my/your/g; 3,$s/This/That/g&#039; my.txt
This is your cat, your cat&#039;s name is betty
This is your dog, your dog&#039;s name is frank
That is your fish, your fish&#039;s name is george
That is my goat, my goat&#039;s name is adam</pre>
<p>上面的命令等价于：（注：下面使用的是sed的-e命令行参数）</p>
<p><code data-enlighter-language="shell" class="EnlighterJSRAW">sed -e &#039;1,3s/my/your/g&#039; -e &#039;3,$s/This/That/g&#039; my.txt</code></p>
<p>我们可以使用&amp;来当做被匹配的变量，然后可以在基本左右加点东西。如下所示：</p>
<pre data-enlighter-language="shell" class="EnlighterJSRAW">$ sed &#039;s/my/[&amp;]/g&#039; my.txt
This is [my] cat, [my] cat&#039;s name is betty
This is [my] dog, [my] dog&#039;s name is frank
This is [my] fish, [my] fish&#039;s name is george
This is [my] goat, [my] goat&#039;s name is adam</pre>
<h4>圆括号匹配</h4>
<p>使用圆括号匹配的示例：（圆括号括起来的正则表达式所匹配的字符串会可以当成变量来使用，sed中使用的是\1,\2&#8230;）</p>
<pre data-enlighter-language="shell" class="EnlighterJSRAW">$ sed &#039;s/This is my \([^,&amp;]*\),.*is \(.*\)/\1:\2/g&#039; my.txt
cat:betty
dog:frank
fish:george
goat:adam</pre>
<p>上面这个例子中的正则表达式有点复杂，解开如下（去掉转义字符）：</p>
<p>正则为：This is my (&#91;^,&#93;*),.*is (.*)<br />
匹配为：This is my (cat),&#8230;&#8230;&#8230;.is (betty)</p>
<p>然后：\1就是cat，\2就是betty</p>
<h4>sed的命令</h4>
<p>让我们回到最一开始的例子pets.txt，让我们来看几个命令：</p>
<h5>N命令</h5>
<p>先来看N命令 —— 把下一行的内容纳入当成缓冲区做匹配。</p>
<p>下面的的示例会把原文本中的偶数行纳入奇数行匹配，而s只匹配并替换一次，所以，就成了下面的结果：</p>
<pre data-enlighter-language="shell" class="EnlighterJSRAW">$ sed &#039;N;s/my/your/&#039; pets.txt
This is your cat
  my cat&#039;s name is betty
This is your dog
  my dog&#039;s name is frank
This is your fish
  my fish&#039;s name is george
This is your goat
  my goat&#039;s name is adam</pre>
<p>也就是说，原来的文件成了：</p>
<pre data-enlighter-language="shell" class="EnlighterJSRAW">This is my cat\n  my cat&#039;s name is betty
This is my dog\n  my dog&#039;s name is frank
This is my fish\n  my fish&#039;s name is george
This is my goat\n  my goat&#039;s name is adam</pre>
<p>这样一来，下面的例子你就明白了，</p>
<pre data-enlighter-language="shell" class="EnlighterJSRAW">$ sed &#039;N;s/\n/,/&#039; pets.txt
This is my cat,  my cat&#039;s name is betty
This is my dog,  my dog&#039;s name is frank
This is my fish,  my fish&#039;s name is george
This is my goat,  my goat&#039;s name is adam</pre>
<h5>a命令和i命令</h5>
<p>a命令就是append， i命令就是insert，它们是用来添加行的。如：</p>
<pre data-enlighter-language="shell" class="EnlighterJSRAW" data-enlighter-highlight="2,12"># 其中的1i表明，其要在第1行前插入一行（insert）
$ sed &quot;1 i This is my monkey, my monkey&#039;s name is wukong&quot; my.txt
This is my monkey, my monkey&#039;s name is wukong
This is my cat, my cat&#039;s name is betty
This is my dog, my dog&#039;s name is frank
This is my fish, my fish&#039;s name is george
This is my goat, my goat&#039;s name is adam

# 其中的1a表明，其要在最后一行后追加一行（append）
$ sed &quot;$ a This is my monkey, my monkey&#039;s name is wukong&quot; my.txt
This is my cat, my cat&#039;s name is betty
This is my monkey, my monkey&#039;s name is wukong
This is my dog, my dog&#039;s name is frank
This is my fish, my fish&#039;s name is george
This is my goat, my goat&#039;s name is adam</pre>
<p>我们可以运用匹配来添加文本：</p>
<pre data-enlighter-language="shell" class="EnlighterJSRAW" data-enlighter-highlight="6"># 注意其中的/fish/a，这意思是匹配到/fish/后就追加一行
$ sed &quot;/fish/a This is my monkey, my monkey&#039;s name is wukong&quot; my.txt
This is my cat, my cat&#039;s name is betty
This is my dog, my dog&#039;s name is frank
This is my fish, my fish&#039;s name is george
This is my monkey, my monkey&#039;s name is wukong
This is my goat, my goat&#039;s name is adam</pre>
<p>下面这个例子是对每一行都挺插入：</p>
<pre data-enlighter-language="shell" class="EnlighterJSRAW">$ sed &quot;/my/a ----&quot; my.txt
This is my cat, my cat&#039;s name is betty
----
This is my dog, my dog&#039;s name is frank
----
This is my fish, my fish&#039;s name is george
----
This is my goat, my goat&#039;s name is adam
----</pre>
<h5>c命令</h5>
<p>c 命令是替换匹配行</p>
<pre data-enlighter-language="shell" class="EnlighterJSRAW">$ sed &quot;2 c This is my monkey, my monkey&#039;s name is wukong&quot; my.txt
This is my cat, my cat&#039;s name is betty
This is my monkey, my monkey&#039;s name is wukong
This is my fish, my fish&#039;s name is george
This is my goat, my goat&#039;s name is adam

$ sed &quot;/fish/c This is my monkey, my monkey&#039;s name is wukong&quot; my.txt
This is my cat, my cat&#039;s name is betty
This is my dog, my dog&#039;s name is frank
This is my monkey, my monkey&#039;s name is wukong
This is my goat, my goat&#039;s name is adam</pre>
<h5>d命令</h5>
<p>删除匹配行</p>
<pre data-enlighter-language="shell" class="EnlighterJSRAW">$ sed &#039;/fish/d&#039; my.txt
This is my cat, my cat&#039;s name is betty
This is my dog, my dog&#039;s name is frank
This is my goat, my goat&#039;s name is adam

$ sed &#039;2d&#039; my.txt
This is my cat, my cat&#039;s name is betty
This is my fish, my fish&#039;s name is george
This is my goat, my goat&#039;s name is adam

$ sed &#039;2,$d&#039; my.txt
This is my cat, my cat&#039;s name is betty</pre>
<h5>p命令</h5>
<p>打印命令</p>
<p>你可以把这个命令当成grep式的命令</p>
<pre data-enlighter-language="shell" class="EnlighterJSRAW"># 匹配fish并输出，可以看到fish的那一行被打了两遍，
# 这是因为sed处理时会把处理的信息输出
$ sed &#039;/fish/p&#039; my.txt
This is my cat, my cat&#039;s name is betty
This is my dog, my dog&#039;s name is frank
This is my fish, my fish&#039;s name is george
This is my fish, my fish&#039;s name is george
This is my goat, my goat&#039;s name is adam

# 使用n参数就好了
$ sed -n &#039;/fish/p&#039; my.txt
This is my fish, my fish&#039;s name is george

# 从一个模式到另一个模式
$ sed -n &#039;/dog/,/fish/p&#039; my.txt
This is my dog, my dog&#039;s name is frank
This is my fish, my fish&#039;s name is george

#从第一行打印到匹配fish成功的那一行
$ sed -n &#039;1,/fish/p&#039; my.txt
This is my cat, my cat&#039;s name is betty
This is my dog, my dog&#039;s name is frank
This is my fish, my fish&#039;s name is george</pre>
<h4>几个知识点</h4>
<p>好了，下面我们要介绍四个sed的基本知识点：</p>
<h5>Pattern Space</h5>
<p>第零个是关于-n参数的，大家也许没看懂，没关系，我们来看一下sed处理文本的伪代码，并了解一下Pattern Space的概念：</p>
<pre data-enlighter-language="c" class="EnlighterJSRAW">foreach line in file {
    //放入把行Pattern_Space
    Pattern_Space &lt;= line;

    // 对每个pattern space执行sed命令
    Pattern_Space &lt;= EXEC(sed_cmd, Pattern_Space);

    // 如果没有指定 -n 则输出处理后的Pattern_Space
    if (sed option hasn&#039;t &quot;-n&quot;)  {
       print Pattern_Space
    }
}</pre>
<h5>Address</h5>
<p>第一个是关于address，几乎上述所有的命令都是这样的（注：其中的!表示匹配成功后是否执行命令）</p>
<p><strong>&#91;address&#91;,address&#93;&#93;&#91;!&#93;{cmd}</strong></p>
<p>address可以是一个数字，也可以是一个模式，你可以通过逗号要分隔两个address 表示两个address的区间，参执行命令cmd，伪代码如下：</p>
<pre data-enlighter-language="c" class="EnlighterJSRAW">
bool bexec = false
foreach line in file {
    if ( match(address1) ){
        bexec = true;
    }

    if ( bexec == true) {
        EXEC(sed_cmd);
    }

    if ( match (address2) ) {
        bexec = false;
    }
}</pre>
<p>关于address可以使用相对位置，如：</p>
<pre data-enlighter-language="shell" class="EnlighterJSRAW"># 其中的+3表示后面连续3行
$ sed &#039;/dog/,+3s/^/# /g&#039; pets.txt
This is my cat
  my cat&#039;s name is betty
# This is my dog
#   my dog&#039;s name is frank
# This is my fish
#   my fish&#039;s name is george
This is my goat
  my goat&#039;s name is adam</pre>
<h5>命令打包</h5>
<p>第二个是cmd可以是多个，它们可以用分号分开，可以用大括号括起来作为嵌套命令。下面是几个例子：</p>
<pre data-enlighter-language="shell" class="EnlighterJSRAW" data-enlighter-highlight="12,21,31">$ cat pets.txt
This is my cat
  my cat&#039;s name is betty
This is my dog
  my dog&#039;s name is frank
This is my fish
  my fish&#039;s name is george
This is my goat
  my goat&#039;s name is adam

# 对3行到第6行，执行命令/This/d
$ sed &#039;3,6 {/This/d}&#039; pets.txt
This is my cat
  my cat&#039;s name is betty
  my dog&#039;s name is frank
  my fish&#039;s name is george
This is my goat
  my goat&#039;s name is adam

# 对3行到第6行，匹配/This/成功后，再匹配/fish/，成功后执行d命令
$ sed &#039;3,6 {/This/{/fish/d}}&#039; pets.txt
This is my cat
  my cat&#039;s name is betty
This is my dog
  my dog&#039;s name is frank
  my fish&#039;s name is george
This is my goat
  my goat&#039;s name is adam

# 从第一行到最后一行，如果匹配到This，则删除之；如果前面有空格，则去除空格
$ sed &#039;1,${/This/d;s/^ *//g}&#039; pets.txt
my cat&#039;s name is betty
my dog&#039;s name is frank
my fish&#039;s name is george
my goat&#039;s name is adam </pre>
<h5>Hold Space</h5>
<p>第三个我们再来看一下 Hold Space</p>
<p>接下来，我们需要了解一下Hold Space的概念，我们先来看四个命令：</p>
<p>g： 将hold space中的内容拷贝到pattern space中，原来pattern space里的内容清除<br />
G： 将hold space中的内容append到pattern space\n后<br />
h： 将pattern space中的内容拷贝到hold space中，原来的hold space里的内容被清除<br />
H： 将pattern space中的内容append到hold space\n后<br />
x： 交换pattern space和hold space的内容</p>
<p>这些命令有什么用？我们来看两个示例吧，用到的示例文件是：</p>
<pre data-enlighter-language="shell" class="EnlighterJSRAW">$ cat t.txt
one
two
three</pre>
<p>第一个示例：</p>
<pre data-enlighter-language="shell" class="EnlighterJSRAW" data-enlighter-highlight="1">$ sed &#039;H;g&#039; t.txt
one

one
two

one
two
three</pre>
<p>是不是有点没看懂，我作个图你就看懂了。</p>
<p><img decoding="async" loading="lazy" class="aligncenter size-full wp-image-9118" src="https://coolshell.cn/wp-content/uploads/2013/02/sed_demo_00.jpg" alt="" width="592" height="404" srcset="https://coolshell.cn/wp-content/uploads/2013/02/sed_demo_00.jpg 592w, https://coolshell.cn/wp-content/uploads/2013/02/sed_demo_00-300x204.jpg 300w" sizes="(max-width: 592px) 100vw, 592px" /></p>
<p>第二个示例，反序了一个文件的行：</p>
<pre data-enlighter-language="shell" class="EnlighterJSRAW" data-enlighter-highlight="1">$ sed &#039;1!G;h;$!d&#039; t.txt
three
two
one</pre>
<p>其中的 &#8216;1!G;h;$!d&#8217; 可拆解为三个命令</p>
<ul>
<li>1!G —— 只有第一行不执行G命令，将hold space中的内容append回到pattern space</li>
<li>h —— 第一行都执行h命令，将pattern space中的内容拷贝到hold space中</li>
<li>$!d —— 除了最后一行不执行d命令，其它行都执行d命令，删除当前行</li>
</ul>
<p>这个执行序列很难理解，做个图如下大家就明白了：</p>
<p><img decoding="async" loading="lazy" class="aligncenter size-full wp-image-9110" src="https://coolshell.cn/wp-content/uploads/2013/02/sed_demo.jpg" alt="" width="623" height="316" srcset="https://coolshell.cn/wp-content/uploads/2013/02/sed_demo.jpg 623w, https://coolshell.cn/wp-content/uploads/2013/02/sed_demo-300x152.jpg 300w" sizes="(max-width: 623px) 100vw, 623px" /></p>
<p>就先说这么多吧，希望对大家有用。</p>
<p>（全文完）<!--



<p align="center"><a href= target=_blank><img decoding="async" src=""></a></p>





<p align="center"><img decoding="async" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg"> <img decoding="async" loading="lazy" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg" width="300" height="300"> <br />关注CoolShell微信公众账号和微信小程序</p>

 

--></p>
<div style="margin-top: 15px; font-size: 16px;color: #cc0000;">
<p align="center"><strong>（转载本站文章请注明作者和出处 <a href="https://coolshell.cn/">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>
</div>

<div class="wp_rp_wrap  wp_rp_vertical_m" ><div class="wp_rp_content"><h3 class="related_post_title">相关文章</h3><ul class="related_post wp_rp"><li ><a href="https://coolshell.cn/articles/18360.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg" alt="程序员练级攻略（2018)  与我的专栏" width="150" height="150" /></a><a href="https://coolshell.cn/articles/18360.html" class="wp_rp_title">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href="https://coolshell.cn/articles/17998.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2017/07/systemd-1-150x150.jpeg" alt="Linux PID 1 和 Systemd" width="150" height="150" /></a><a href="https://coolshell.cn/articles/17998.html" class="wp_rp_title">Linux PID 1 和 Systemd</a></li><li ><a href="https://coolshell.cn/articles/12103.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2014/11/tux-fork-150x150.gif" alt="vfork 挂掉的一个问题" width="150" height="150" /></a><a href="https://coolshell.cn/articles/12103.html" class="wp_rp_title">vfork 挂掉的一个问题</a></li><li ><a href="https://coolshell.cn/articles/11847.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2014/08/puzzle-150x150.png" alt="谜题的答案和活动的心得体会" width="150" height="150" /></a><a href="https://coolshell.cn/articles/11847.html" class="wp_rp_title">谜题的答案和活动的心得体会</a></li><li ><a href="https://coolshell.cn/articles/9070.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2013/02/awk-150x150.jpg" alt="AWK 简明教程" width="150" height="150" /></a><a href="https://coolshell.cn/articles/9070.html" class="wp_rp_title">AWK 简明教程</a></li><li ><a href="https://coolshell.cn/articles/8883.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2013/01/linux-bash-300x225-150x150.jpg" alt="应该知道的Linux技巧" width="150" height="150" /></a><a href="https://coolshell.cn/articles/8883.html" class="wp_rp_title">应该知道的Linux技巧</a></li></ul></div></div>The post <a href="https://coolshell.cn/articles/9104.html">sed 简明教程</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></content:encoded>
					
					<wfw:commentRss>https://coolshell.cn/articles/9104.html/feed</wfw:commentRss>
			<slash:comments>209</slash:comments>
		
		
			</item>
		<item>
		<title>AWK 简明教程</title>
		<link>https://coolshell.cn/articles/9070.html</link>
					<comments>https://coolshell.cn/articles/9070.html#comments</comments>
		
		<dc:creator><![CDATA[陈皓]]></dc:creator>
		<pubDate>Sun, 17 Feb 2013 00:38:29 +0000</pubDate>
				<category><![CDATA[Unix/Linux]]></category>
		<category><![CDATA[杂项资源]]></category>
		<category><![CDATA[编程工具]]></category>
		<category><![CDATA[awk]]></category>
		<category><![CDATA[gawk]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[Shell]]></category>
		<category><![CDATA[Unix]]></category>
		<guid isPermaLink="false">http://coolshell.cn/?p=9070</guid>

					<description><![CDATA[<p>有一些网友看了前两天的《Linux下应该知道的技巧》希望我能教教他们用awk和sed，所以，出现了这篇文章。我估计这些80后的年轻朋友可能对awk/sed这类上...</p>
<p class="read-more"><a class="btn btn-default" href="https://coolshell.cn/articles/9070.html"> Read More<span class="screen-reader-text">  Read More</span></a></p>
The post <a href="https://coolshell.cn/articles/9070.html">AWK 简明教程</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></description>
										<content:encoded><![CDATA[<p><script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158"
     crossorigin="anonymous"></script><img decoding="async" loading="lazy" class="alignright size-full wp-image-9093" alt="" src="https://coolshell.cn/wp-content/uploads/2013/02/awk.jpg" width="350" height="279" srcset="https://coolshell.cn/wp-content/uploads/2013/02/awk.jpg 350w, https://coolshell.cn/wp-content/uploads/2013/02/awk-300x239.jpg 300w, https://coolshell.cn/wp-content/uploads/2013/02/awk-339x270.jpg 339w" sizes="(max-width: 350px) 100vw, 350px" />有一些网友看了前两天的《<a title="应该知道的Linux技巧" href="https://coolshell.cn/articles/8883.html" target="_blank">Linux下应该知道的技巧</a>》希望我能教教他们用awk和sed，所以，出现了这篇文章。我估计这些80后的年轻朋友可能对awk/sed这类上古神器有点陌生了，所以需要我这个老家伙来炒炒冷饭。<strong>况且，AWK是贝尔实验室1977年搞出来的文本出现神器，今年是蛇年，是AWK的本命年，而且年纪和我相仿，所以非常有必要为他写篇文章</strong>。</p>
<p>之所以叫AWK是因为其取了三位创始人 <a title="Alfred Aho" href="http://en.wikipedia.org/wiki/Alfred_Aho">Alfred Aho</a>，<a title="Peter J. Weinberger" href="http://en.wikipedia.org/wiki/Peter_J._Weinberger">Peter Weinberger</a>, 和 <a title="Brian Kernighan" href="http://en.wikipedia.org/wiki/Brian_Kernighan">Brian Kernighan</a> 的Family Name的首字符。要学AWK，就得提一提AWK的一本相当经典的书《<a href="http://plan9.bell-labs.com/cm/cs/awkbook/" rel="nofollow">The AWK Programming Language</a>》，它在<a href="http://book.douban.com/subject/1876898/" target="_blank">豆瓣上的评分</a>是9.4分！在<a href="http://www.amazon.cn/mn/detailApp/?asin=020107981X" target="_blank">亚马逊上居然卖1022.30元</a>。</p>
<p>我在这里的教程并不想面面俱到，本文和我之前的<a title="Go 语言简介（上）— 语法" href="https://coolshell.cn/articles/8460.html" target="_blank">Go语言简介</a>一样，全是示例，基本无废话。</p>
<p><strong>我只想达到两个目的：</strong></p>
<p style="text-align: left; padding-left: 30px;"><strong>1）你可以在乘坐公交地铁上下班，或是在坐马桶拉大便时读完（保证是一泡大便的工夫）。</strong></p>
<p style="text-align: left; padding-left: 30px;"><strong>2）我只想让这篇博文像一个火辣的脱衣舞女挑起你的兴趣，然后还要你自己去下工夫去撸。</strong></p>
<p>废话少说，我们开始脱吧（注：这里只是topless）。</p>
<h4>起步上台</h4>
<p>我从netstat命令中提取了如下信息作为用例：</p>
<p><span id="more-9070"></span></p>
<pre data-enlighter-language="shell" class="EnlighterJSRAW">$ cat netstat.txt
Proto Recv-Q Send-Q Local-Address          Foreign-Address             State
tcp        0      0 0.0.0.0:3306           0.0.0.0:*                   LISTEN
tcp        0      0 0.0.0.0:80             0.0.0.0:*                   LISTEN
tcp        0      0 127.0.0.1:9000         0.0.0.0:*                   LISTEN
tcp        0      0 coolshell.cn:80        124.205.5.146:18245         TIME_WAIT
tcp        0      0 coolshell.cn:80        61.140.101.185:37538        FIN_WAIT2
tcp        0      0 coolshell.cn:80        110.194.134.189:1032        ESTABLISHED
tcp        0      0 coolshell.cn:80        123.169.124.111:49809       ESTABLISHED
tcp        0      0 coolshell.cn:80        116.234.127.77:11502        FIN_WAIT2
tcp        0      0 coolshell.cn:80        123.169.124.111:49829       ESTABLISHED
tcp        0      0 coolshell.cn:80        183.60.215.36:36970         TIME_WAIT
tcp        0   4166 coolshell.cn:80        61.148.242.38:30901         ESTABLISHED
tcp        0      1 coolshell.cn:80        124.152.181.209:26825       FIN_WAIT1
tcp        0      0 coolshell.cn:80        110.194.134.189:4796        ESTABLISHED
tcp        0      0 coolshell.cn:80        183.60.212.163:51082        TIME_WAIT
tcp        0      1 coolshell.cn:80        208.115.113.92:50601        LAST_ACK
tcp        0      0 coolshell.cn:80        123.169.124.111:49840       ESTABLISHED
tcp        0      0 coolshell.cn:80        117.136.20.85:50025         FIN_WAIT2
tcp        0      0 :::22                  :::*                        LISTEN
</pre>
<p>下面是最简单最常用的awk示例，其输出第1列和第4例，</p>
<ul>
<li>其中单引号中的被大括号括着的就是awk的语句，注意，其只能被单引号包含。</li>
<li>其中的$1..$n表示第几例。注：$0表示整个行。</li>
</ul>
<pre data-enlighter-language="shell" class="EnlighterJSRAW" data-enlighter-highlight="1">$ awk &#039;{print $1, $4}&#039; netstat.txt
Proto Local-Address
tcp 0.0.0.0:3306
tcp 0.0.0.0:80
tcp 127.0.0.1:9000
tcp coolshell.cn:80
tcp coolshell.cn:80
tcp coolshell.cn:80
tcp coolshell.cn:80
tcp coolshell.cn:80
tcp coolshell.cn:80
tcp coolshell.cn:80
tcp coolshell.cn:80
tcp coolshell.cn:80
tcp coolshell.cn:80
tcp coolshell.cn:80
tcp coolshell.cn:80
tcp coolshell.cn:80
tcp coolshell.cn:80
tcp :::22</pre>
<p>我们再来看看awk的格式化输出，和C语言的printf没什么两样：</p>
<pre data-enlighter-language="shell" class="EnlighterJSRAW" data-enlighter-highlight="1">$ awk &#039;{printf &quot;%-8s %-8s %-8s %-18s %-22s %-15s\n&quot;,$1,$2,$3,$4,$5,$6}&#039; netstat.txt
Proto    Recv-Q   Send-Q   Local-Address      Foreign-Address        State
tcp      0        0        0.0.0.0:3306       0.0.0.0:*              LISTEN
tcp      0        0        0.0.0.0:80         0.0.0.0:*              LISTEN
tcp      0        0        127.0.0.1:9000     0.0.0.0:*              LISTEN
tcp      0        0        coolshell.cn:80    124.205.5.146:18245    TIME_WAIT
tcp      0        0        coolshell.cn:80    61.140.101.185:37538   FIN_WAIT2
tcp      0        0        coolshell.cn:80    110.194.134.189:1032   ESTABLISHED
tcp      0        0        coolshell.cn:80    123.169.124.111:49809  ESTABLISHED
tcp      0        0        coolshell.cn:80    116.234.127.77:11502   FIN_WAIT2
tcp      0        0        coolshell.cn:80    123.169.124.111:49829  ESTABLISHED
tcp      0        0        coolshell.cn:80    183.60.215.36:36970    TIME_WAIT
tcp      0        4166     coolshell.cn:80    61.148.242.38:30901    ESTABLISHED
tcp      0        1        coolshell.cn:80    124.152.181.209:26825  FIN_WAIT1
tcp      0        0        coolshell.cn:80    110.194.134.189:4796   ESTABLISHED
tcp      0        0        coolshell.cn:80    183.60.212.163:51082   TIME_WAIT
tcp      0        1        coolshell.cn:80    208.115.113.92:50601   LAST_ACK
tcp      0        0        coolshell.cn:80    123.169.124.111:49840  ESTABLISHED
tcp      0        0        coolshell.cn:80    117.136.20.85:50025    FIN_WAIT2
tcp      0        0        :::22              :::*                   LISTEN</pre>
<h4>脱掉外套</h4>
<h5>过滤记录</h5>
<p>我们再来看看如何过滤记录（下面过滤条件为：第三列的值为0 &amp;&amp; 第6列的值为LISTEN）</p>
<pre data-enlighter-language="shell" class="EnlighterJSRAW" data-enlighter-highlight="1">$ awk &#039;$3==0 &amp;&amp; $6==&quot;LISTEN&quot; &#039; netstat.txt
tcp        0      0 0.0.0.0:3306               0.0.0.0:*              LISTEN
tcp        0      0 0.0.0.0:80                 0.0.0.0:*              LISTEN
tcp        0      0 127.0.0.1:9000             0.0.0.0:*              LISTEN
tcp        0      0 :::22                      :::*                   LISTEN</pre>
<p>其中的“==”为比较运算符。其他比较运算符：!=, &gt;, &lt;, &gt;=, &lt;=</p>
<p>我们来看看各种过滤记录的方式：</p>
<pre data-enlighter-language="shell" class="EnlighterJSRAW" data-enlighter-highlight="1">$ awk &#039; $3&gt;0 {print $0}&#039; netstat.txt
Proto Recv-Q Send-Q Local-Address          Foreign-Address             State
tcp        0   4166 coolshell.cn:80        61.148.242.38:30901         ESTABLISHED
tcp        0      1 coolshell.cn:80        124.152.181.209:26825       FIN_WAIT1
tcp        0      1 coolshell.cn:80        208.115.113.92:50601        LAST_ACK</pre>
<p>如果我们需要表头的话，我们可以引入内建变量NR：</p>
<pre data-enlighter-language="shell" class="EnlighterJSRAW" data-enlighter-highlight="1">$ awk &#039;$3==0 &amp;&amp; $6==&quot;LISTEN&quot; || NR==1 &#039; netstat.txt
Proto Recv-Q Send-Q Local-Address          Foreign-Address             State
tcp        0      0 0.0.0.0:3306           0.0.0.0:*                   LISTEN
tcp        0      0 0.0.0.0:80             0.0.0.0:*                   LISTEN
tcp        0      0 127.0.0.1:9000         0.0.0.0:*                   LISTEN
tcp        0      0 :::22                  :::*                        LISTEN</pre>
<p>再加上格式化输出：</p>
<pre data-enlighter-language="shell" class="EnlighterJSRAW" data-enlighter-highlight="1">$ awk &#039;$3==0 &amp;&amp; $6==&quot;LISTEN&quot; || NR==1 {printf &quot;%-20s %-20s %s\n&quot;,$4,$5,$6}&#039; netstat.txt
Local-Address        Foreign-Address      State
0.0.0.0:3306         0.0.0.0:*            LISTEN
0.0.0.0:80           0.0.0.0:*            LISTEN
127.0.0.1:9000       0.0.0.0:*            LISTEN
:::22                :::*                 LISTEN</pre>
<h5><strong>内建变量</strong></h5>
<p>说到了内建变量，我们可以来看看awk的一些内建变量：</p>
<table border="0" cellspacing="1" cellpadding="4">
<tbody>
<tr>
<td bgcolor="#ffffff">$0</td>
<td bgcolor="#ffffff">当前记录（这个变量中存放着整个行的内容）</td>
</tr>
<tr>
<td bgcolor="#ffffff">$1~$n</td>
<td bgcolor="#ffffff">当前记录的第n个字段，字段间由FS分隔</td>
</tr>
<tr>
<td bgcolor="#ffffff">FS</td>
<td bgcolor="#ffffff">输入字段分隔符 默认是空格或Tab</td>
</tr>
<tr>
<td bgcolor="#ffffff">NF</td>
<td bgcolor="#ffffff">当前记录中的字段个数，就是有多少列</td>
</tr>
<tr>
<td bgcolor="#ffffff">NR</td>
<td bgcolor="#ffffff">已经读出的记录数，就是行号，从1开始，如果有多个文件话，这个值也是不断累加中。</td>
</tr>
<tr>
<td bgcolor="#ffffff">FNR</td>
<td bgcolor="#ffffff">当前记录数，与NR不同的是，这个值会是各个文件自己的行号</td>
</tr>
<tr>
<td bgcolor="#ffffff">RS</td>
<td bgcolor="#ffffff">输入的记录分隔符， 默认为换行符</td>
</tr>
<tr>
<td bgcolor="#ffffff">OFS</td>
<td bgcolor="#ffffff">输出字段分隔符， 默认也是空格</td>
</tr>
<tr>
<td bgcolor="#ffffff">ORS</td>
<td bgcolor="#ffffff">输出的记录分隔符，默认为换行符</td>
</tr>
<tr>
<td bgcolor="#ffffff">FILENAME</td>
<td bgcolor="#ffffff">当前输入文件的名字</td>
</tr>
</tbody>
</table>
<p>怎么使用呢，比如：我们如果要输出行号：</p>
<pre data-enlighter-language="shell" class="EnlighterJSRAW" data-enlighter-highlight="1">$ awk &#039;$3==0 &amp;&amp; $6==&quot;ESTABLISHED&quot; || NR==1 {printf &quot;%02s %s %-20s %-20s %s\n&quot;,NR, FNR, $4,$5,$6}&#039; netstat.txt
01 1 Local-Address        Foreign-Address      State
07 7 coolshell.cn:80      110.194.134.189:1032 ESTABLISHED
08 8 coolshell.cn:80      123.169.124.111:49809 ESTABLISHED
10 10 coolshell.cn:80      123.169.124.111:49829 ESTABLISHED
14 14 coolshell.cn:80      110.194.134.189:4796 ESTABLISHED
17 17 coolshell.cn:80      123.169.124.111:49840 ESTABLISHED</pre>
<h5><strong>指定分隔符</strong></h5>
<pre data-enlighter-language="shell" class="EnlighterJSRAW" data-enlighter-highlight="1">$  awk  &#039;BEGIN{FS=&quot;:&quot;} {print $1,$3,$6}&#039; /etc/passwd
root 0 /root
bin 1 /bin
daemon 2 /sbin
adm 3 /var/adm
lp 4 /var/spool/lpd
sync 5 /sbin
shutdown 6 /sbin
halt 7 /sbin</pre>
<p>上面的命令也等价于：（-F的意思就是指定分隔符）</p>
<p><code data-enlighter-language="shell" class="EnlighterJSRAW">$ awk  -F: &#039;{print $1,$3,$6}&#039; /etc/passwd</code></p>
<p>注：如果你要指定多个分隔符，你可以这样来：</p>
<p><code data-enlighter-language="shell" class="EnlighterJSRAW">awk -F &#039;[;:]&#039;</code></p>
<p>再来看一个以\t作为分隔符输出的例子（下面使用了/etc/passwd文件，这个文件是以:分隔的）：</p>
<pre data-enlighter-language="shell" class="EnlighterJSRAW" data-enlighter-highlight="1">$ awk  -F: &#039;{print $1,$3,$6}&#039; OFS=&quot;\t&quot; /etc/passwd
root    0       /root
bin     1       /bin
daemon  2       /sbin
adm     3       /var/adm
lp      4       /var/spool/lpd
sync    5       /sbin</pre>
<h4>脱掉衬衫</h4>
<h5>字符串匹配</h5>
<p>我们再来看几个字符串匹配的示例：</p>
<pre data-enlighter-language="shell" class="EnlighterJSRAW" data-enlighter-highlight="1,8">$ awk &#039;$6 ~ /FIN/ || NR==1 {print NR,$4,$5,$6}&#039; OFS=&quot;\t&quot; netstat.txt
1       Local-Address   Foreign-Address State
6       coolshell.cn:80 61.140.101.185:37538    FIN_WAIT2
9       coolshell.cn:80 116.234.127.77:11502    FIN_WAIT2
13      coolshell.cn:80 124.152.181.209:26825   FIN_WAIT1
18      coolshell.cn:80 117.136.20.85:50025     FIN_WAIT2

$ $ awk &#039;$6 ~ /WAIT/ || NR==1 {print NR,$4,$5,$6}&#039; OFS=&quot;\t&quot; netstat.txt
1       Local-Address   Foreign-Address State
5       coolshell.cn:80 124.205.5.146:18245     TIME_WAIT
6       coolshell.cn:80 61.140.101.185:37538    FIN_WAIT2
9       coolshell.cn:80 116.234.127.77:11502    FIN_WAIT2
11      coolshell.cn:80 183.60.215.36:36970     TIME_WAIT
13      coolshell.cn:80 124.152.181.209:26825   FIN_WAIT1
15      coolshell.cn:80 183.60.212.163:51082    TIME_WAIT
18      coolshell.cn:80 117.136.20.85:50025     FIN_WAIT2</pre>
<p>上面的第一个示例匹配FIN状态， 第二个示例匹配WAIT字样的状态。其实 ~ 表示模式开始。/ /中是模式。这就是一个正则表达式的匹配。</p>
<p>其实awk可以像grep一样的去匹配第一行，就像这样：</p>
<pre data-enlighter-language="shell" class="EnlighterJSRAW" data-enlighter-highlight="1">$ awk &#039;/LISTEN/&#039; netstat.txt
tcp        0      0 0.0.0.0:3306            0.0.0.0:*               LISTEN
tcp        0      0 0.0.0.0:80              0.0.0.0:*               LISTEN
tcp        0      0 127.0.0.1:9000          0.0.0.0:*               LISTEN
tcp        0      0 :::22                   :::*                    LISTEN</pre>
<p>我们可以使用 “/FIN|TIME/” 来匹配 FIN 或者 TIME :</p>
<pre data-enlighter-language="shell" class="EnlighterJSRAW" data-enlighter-highlight="1">$ awk &#039;$6 ~ /FIN|TIME/ || NR==1 {print NR,$4,$5,$6}&#039; OFS=&quot;\t&quot; netstat.txt
1       Local-Address   Foreign-Address State
5       coolshell.cn:80 124.205.5.146:18245     TIME_WAIT
6       coolshell.cn:80 61.140.101.185:37538    FIN_WAIT2
9       coolshell.cn:80 116.234.127.77:11502    FIN_WAIT2
11      coolshell.cn:80 183.60.215.36:36970     TIME_WAIT
13      coolshell.cn:80 124.152.181.209:26825   FIN_WAIT1
15      coolshell.cn:80 183.60.212.163:51082    TIME_WAIT
18      coolshell.cn:80 117.136.20.85:50025     FIN_WAIT2</pre>
<p>再来看看模式取反的例子：</p>
<pre data-enlighter-language="shell" class="EnlighterJSRAW" data-enlighter-highlight="1">$ awk &#039;$6 !~ /WAIT/ || NR==1 {print NR,$4,$5,$6}&#039; OFS=&quot;\t&quot; netstat.txt
1       Local-Address   Foreign-Address State
2       0.0.0.0:3306    0.0.0.0:*       LISTEN
3       0.0.0.0:80      0.0.0.0:*       LISTEN
4       127.0.0.1:9000  0.0.0.0:*       LISTEN
7       coolshell.cn:80 110.194.134.189:1032    ESTABLISHED
8       coolshell.cn:80 123.169.124.111:49809   ESTABLISHED
10      coolshell.cn:80 123.169.124.111:49829   ESTABLISHED
12      coolshell.cn:80 61.148.242.38:30901     ESTABLISHED
14      coolshell.cn:80 110.194.134.189:4796    ESTABLISHED
16      coolshell.cn:80 208.115.113.92:50601    LAST_ACK
17      coolshell.cn:80 123.169.124.111:49840   ESTABLISHED
19      :::22   :::*    LISTEN</pre>
<p>或是：</p>
<p><code data-enlighter-language="shell" class="EnlighterJSRAW">awk &#039;!/WAIT/&#039; netstat.txt</code></p>
<p><strong>折分文件</strong></p>
<p>awk拆分文件很简单，使用重定向就好了。下面这个例子，是按第6例分隔文件，相当的简单（其中的NR!=1表示不处理表头）。</p>
<pre data-enlighter-language="shell" class="EnlighterJSRAW" data-enlighter-highlight="1">$ awk &#039;NR!=1{print &gt; $6}&#039; netstat.txt

$ ls
ESTABLISHED  FIN_WAIT1  FIN_WAIT2  LAST_ACK  LISTEN  netstat.txt  TIME_WAIT

$ cat ESTABLISHED
tcp        0      0 coolshell.cn:80        110.194.134.189:1032        ESTABLISHED
tcp        0      0 coolshell.cn:80        123.169.124.111:49809       ESTABLISHED
tcp        0      0 coolshell.cn:80        123.169.124.111:49829       ESTABLISHED
tcp        0   4166 coolshell.cn:80        61.148.242.38:30901         ESTABLISHED
tcp        0      0 coolshell.cn:80        110.194.134.189:4796        ESTABLISHED
tcp        0      0 coolshell.cn:80        123.169.124.111:49840       ESTABLISHED

$ cat FIN_WAIT1
tcp        0      1 coolshell.cn:80        124.152.181.209:26825       FIN_WAIT1

$ cat FIN_WAIT2
tcp        0      0 coolshell.cn:80        61.140.101.185:37538        FIN_WAIT2
tcp        0      0 coolshell.cn:80        116.234.127.77:11502        FIN_WAIT2
tcp        0      0 coolshell.cn:80        117.136.20.85:50025         FIN_WAIT2

$ cat LAST_ACK
tcp        0      1 coolshell.cn:80        208.115.113.92:50601        LAST_ACK

$ cat LISTEN
tcp        0      0 0.0.0.0:3306           0.0.0.0:*                   LISTEN
tcp        0      0 0.0.0.0:80             0.0.0.0:*                   LISTEN
tcp        0      0 127.0.0.1:9000         0.0.0.0:*                   LISTEN
tcp        0      0 :::22                  :::*                        LISTEN

$ cat TIME_WAIT
tcp        0      0 coolshell.cn:80        124.205.5.146:18245         TIME_WAIT
tcp        0      0 coolshell.cn:80        183.60.215.36:36970         TIME_WAIT
tcp        0      0 coolshell.cn:80        183.60.212.163:51082        TIME_WAIT</pre>
<p>你也可以把指定的列输出到文件：</p>
<p><code data-enlighter-language="shell" class="EnlighterJSRAW">awk &#039;NR!=1{print $4,$5 &gt; $6}&#039; netstat.txt</code></p>
<p>再复杂一点：（注意其中的if-else-if语句，可见awk其实是个脚本解释器）</p>
<pre data-enlighter-language="shell" class="EnlighterJSRAW" data-enlighter-highlight="1,2,3">$ awk &#039;NR!=1{if($6 ~ /TIME|ESTABLISHED/) print &gt; &quot;1.txt&quot;;
else if($6 ~ /LISTEN/) print &gt; &quot;2.txt&quot;;
else print &gt; &quot;3.txt&quot; }&#039; netstat.txt

$ ls ?.txt
1.txt  2.txt  3.txt

$ cat 1.txt
tcp        0      0 coolshell.cn:80        124.205.5.146:18245         TIME_WAIT
tcp        0      0 coolshell.cn:80        110.194.134.189:1032        ESTABLISHED
tcp        0      0 coolshell.cn:80        123.169.124.111:49809       ESTABLISHED
tcp        0      0 coolshell.cn:80        123.169.124.111:49829       ESTABLISHED
tcp        0      0 coolshell.cn:80        183.60.215.36:36970         TIME_WAIT
tcp        0   4166 coolshell.cn:80        61.148.242.38:30901         ESTABLISHED
tcp        0      0 coolshell.cn:80        110.194.134.189:4796        ESTABLISHED
tcp        0      0 coolshell.cn:80        183.60.212.163:51082        TIME_WAIT
tcp        0      0 coolshell.cn:80        123.169.124.111:49840       ESTABLISHED

$ cat 2.txt
tcp        0      0 0.0.0.0:3306           0.0.0.0:*                   LISTEN
tcp        0      0 0.0.0.0:80             0.0.0.0:*                   LISTEN
tcp        0      0 127.0.0.1:9000         0.0.0.0:*                   LISTEN
tcp        0      0 :::22                  :::*                        LISTEN

$ cat 3.txt
tcp        0      0 coolshell.cn:80        61.140.101.185:37538        FIN_WAIT2
tcp        0      0 coolshell.cn:80        116.234.127.77:11502        FIN_WAIT2
tcp        0      1 coolshell.cn:80        124.152.181.209:26825       FIN_WAIT1
tcp        0      1 coolshell.cn:80        208.115.113.92:50601        LAST_ACK
tcp        0      0 coolshell.cn:80        117.136.20.85:50025         FIN_WAIT2</pre>
<h5>统计</h5>
<p>下面的命令计算所有的C文件，CPP文件和H文件的文件大小总和。</p>
<pre data-enlighter-language="shell" class="EnlighterJSRAW">$ ls -l  *.cpp *.c *.h | awk &#039;{sum+=$5} END {print sum}&#039;
2511401</pre>
<p>我们再来看一个统计各个connection状态的用法：（我们可以看到一些编程的影子了，大家都是程序员我就不解释了。注意其中的数组的用法）</p>
<pre data-enlighter-language="shell" class="EnlighterJSRAW" data-enlighter-highlight="1">$ awk &#039;NR!=1{a[$6]++;} END {for (i in a) print i &quot;, &quot; a[i];}&#039; netstat.txt
TIME_WAIT, 3
FIN_WAIT1, 1
ESTABLISHED, 6
FIN_WAIT2, 3
LAST_ACK, 1
LISTEN, 4</pre>
<p>再来看看统计每个用户的进程的占了多少内存（注：sum的RSS那一列）</p>
<pre data-enlighter-language="shell" class="EnlighterJSRAW" data-enlighter-highlight="1">$ ps aux | awk &#039;NR!=1{a[$1]+=$6;} END { for(i in a) print i &quot;, &quot; a[i]&quot;KB&quot;;}&#039;
dbus, 540KB
mysql, 99928KB
www, 3264924KB
root, 63644KB
hchen, 6020KB</pre>
<h4>脱掉内衣</h4>
<h5>awk脚本</h5>
<p>在上面我们可以看到一个END关键字。END的意思是“处理完所有的行的标识”，即然说到了END就有必要介绍一下BEGIN，这两个关键字意味着执行前和执行后的意思，语法如下：</p>
<ul>
<li>BEGIN{ 这里面放的是执行前的语句 }</li>
<li>END {这里面放的是处理完所有的行后要执行的语句 }</li>
<li>{这里面放的是处理每一行时要执行的语句}</li>
</ul>
<p>为了说清楚这个事，我们来看看下面的示例：</p>
<p>假设有这么一个文件（学生成绩表）：</p>
<pre data-enlighter-language="shell" class="EnlighterJSRAW">$ cat score.txt
Marry   2143 78 84 77
Jack    2321 66 78 45
Tom     2122 48 77 71
Mike    2537 87 97 95
Bob     2415 40 57 62</pre>
<p>我们的awk脚本如下（我没有写有命令行上是因为命令行上不易读，另外也在介绍另一种用法）：</p>
<pre data-enlighter-language="shell" class="EnlighterJSRAW">$ cat cal.awk
#!/bin/awk -f
#运行前
BEGIN {
    math = 0
    english = 0
    computer = 0

    printf &quot;NAME    NO.   MATH  ENGLISH  COMPUTER   TOTAL\n&quot;
    printf &quot;---------------------------------------------\n&quot;
}
#运行中
{
    math+=$3
    english+=$4
    computer+=$5
    printf &quot;%-6s %-6s %4d %8d %8d %8d\n&quot;, $1, $2, $3,$4,$5, $3+$4+$5
}
#运行后
END {
    printf &quot;---------------------------------------------\n&quot;
    printf &quot;  TOTAL:%10d %8d %8d \n&quot;, math, english, computer
    printf &quot;AVERAGE:%10.2f %8.2f %8.2f\n&quot;, math/NR, english/NR, computer/NR
}</pre>
<p>我们来看一下执行结果：（也可以这样运行 ./cal.awk score.txt）</p>
<pre data-enlighter-language="shell" class="EnlighterJSRAW">$ awk -f cal.awk score.txt
NAME    NO.   MATH  ENGLISH  COMPUTER   TOTAL
---------------------------------------------
Marry  2143     78       84       77      239
Jack   2321     66       78       45      189
Tom    2122     48       77       71      196
Mike   2537     87       97       95      279
Bob    2415     40       57       62      159
---------------------------------------------
  TOTAL:       319      393      350
AVERAGE:     63.80    78.60    70.00</pre>
<h5>环境变量</h5>
<p>即然说到了脚本，我们来看看怎么和环境变量交互：（使用-v参数和ENVIRON，使用ENVIRON的环境变量需要export）</p>
<pre data-enlighter-language="shell" class="EnlighterJSRAW" data-enlighter-highlight="9">$ x=5

$ y=10
$ export y

$ echo $x $y
5 10

$ awk -v val=$x &#039;{print $1, $2, $3, $4+val, $5+ENVIRON[&quot;y&quot;]}&#039; OFS=&quot;\t&quot; score.txt
Marry   2143    78      89      87
Jack    2321    66      83      55
Tom     2122    48      82      81
Mike    2537    87      102     105
Bob     2415    40      62      72
</pre>
<h4>几个花活</h4>
<p>最后，我们再来看几个小例子：</p>
<pre data-enlighter-language="shell" class="EnlighterJSRAW">#从file文件中找出长度大于80的行
awk &#039;length&gt;80&#039; file

#按连接数查看客户端IP
netstat -ntu | awk &#039;{print $5}&#039; | cut -d: -f1 | sort | uniq -c | sort -nr

#打印99乘法表
seq 9 | sed &#039;H;g&#039; | awk -v RS=&#039;&#039; &#039;{for(i=1;i&lt;=NF;i++)printf(&quot;%dx%d=%d%s&quot;, i, NR, i*NR, i==NR?&quot;\n&quot;:&quot;\t&quot;)}&#039; </pre>
<h4>自己撸吧</h4>
<p>关于其中的一些知识点可以参看<a href="http://www.gnu.org/software/gawk/manual/gawk.html" target="_blank">gawk的手册</a>：</p>
<ul>
<li>内建变量，参看：<a href="http://www.gnu.org/software/gawk/manual/gawk.html#Built_002din-Variables" target="_blank">http://www.gnu.org/software/gawk/manual/gawk.html#Built_002din-Variables</a></li>
<li>流控方面，参看：<a href="http://www.gnu.org/software/gawk/manual/gawk.html#Statements" target="_blank">http://www.gnu.org/software/gawk/manual/gawk.html#Statements</a></li>
<li>内建函数，参看：<a href="http://www.gnu.org/software/gawk/manual/gawk.html#Built_002din" target="_blank">http://www.gnu.org/software/gawk/manual/gawk.html#Built_002din</a></li>
<li>正则表达式，参看：<a href="http://www.gnu.org/software/gawk/manual/gawk.html#Regexp" target="_blank">http://www.gnu.org/software/gawk/manual/gawk.html#Regexp</a></li>
</ul>
<p>（全文完）<!--



<p align="center"><a href= target=_blank><img decoding="async" src=""></a></p>





<p align="center"><img decoding="async" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg"> <img decoding="async" loading="lazy" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg" width="300" height="300"> <br />关注CoolShell微信公众账号和微信小程序</p>

 

--></p>
<div style="margin-top: 15px; font-size: 16px;color: #cc0000;">
<p align="center"><strong>（转载本站文章请注明作者和出处 <a href="https://coolshell.cn/">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>
</div>

<div class="wp_rp_wrap  wp_rp_vertical_m" ><div class="wp_rp_content"><h3 class="related_post_title">相关文章</h3><ul class="related_post wp_rp"><li ><a href="https://coolshell.cn/articles/8883.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2013/01/linux-bash-300x225-150x150.jpg" alt="应该知道的Linux技巧" width="150" height="150" /></a><a href="https://coolshell.cn/articles/8883.html" class="wp_rp_title">应该知道的Linux技巧</a></li><li ><a href="https://coolshell.cn/articles/8619.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2012/11/shell.01-150x150.png" alt="你可能不知道的Shell" width="150" height="150" /></a><a href="https://coolshell.cn/articles/8619.html" class="wp_rp_title">你可能不知道的Shell</a></li><li ><a href="https://coolshell.cn/articles/19219.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2019/03/linux.ninja_-150x150.png" alt="打造高效的工作环境 &#8211; Shell 篇" width="150" height="150" /></a><a href="https://coolshell.cn/articles/19219.html" class="wp_rp_title">打造高效的工作环境 &#8211; Shell 篇</a></li><li ><a href="https://coolshell.cn/articles/18360.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg" alt="程序员练级攻略（2018)  与我的专栏" width="150" height="150" /></a><a href="https://coolshell.cn/articles/18360.html" class="wp_rp_title">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href="https://coolshell.cn/articles/17998.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2017/07/systemd-1-150x150.jpeg" alt="Linux PID 1 和 Systemd" width="150" height="150" /></a><a href="https://coolshell.cn/articles/17998.html" class="wp_rp_title">Linux PID 1 和 Systemd</a></li><li ><a href="https://coolshell.cn/articles/12103.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2014/11/tux-fork-150x150.gif" alt="vfork 挂掉的一个问题" width="150" height="150" /></a><a href="https://coolshell.cn/articles/12103.html" class="wp_rp_title">vfork 挂掉的一个问题</a></li></ul></div></div>The post <a href="https://coolshell.cn/articles/9070.html">AWK 简明教程</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></content:encoded>
					
					<wfw:commentRss>https://coolshell.cn/articles/9070.html/feed</wfw:commentRss>
			<slash:comments>260</slash:comments>
		
		
			</item>
		<item>
		<title>从面向对象的设计模式看软件设计</title>
		<link>https://coolshell.cn/articles/8961.html</link>
					<comments>https://coolshell.cn/articles/8961.html#comments</comments>
		
		<dc:creator><![CDATA[陈皓]]></dc:creator>
		<pubDate>Fri, 01 Feb 2013 00:15:59 +0000</pubDate>
				<category><![CDATA[Unix/Linux]]></category>
		<category><![CDATA[杂项资源]]></category>
		<category><![CDATA[程序设计]]></category>
		<category><![CDATA[Design]]></category>
		<category><![CDATA[design pattern]]></category>
		<category><![CDATA[Object-Oriented]]></category>
		<category><![CDATA[Unix]]></category>
		<guid isPermaLink="false">http://coolshell.cn/?p=8961</guid>

					<description><![CDATA[<p>前些天发了一篇《如此理解面向对象编程》的文章，然后引起了大家的热议。然后我在微博上说了一句——“那23个经典的设计模式和OO半毛钱关系没有，只不过人家用OO来实...</p>
<p class="read-more"><a class="btn btn-default" href="https://coolshell.cn/articles/8961.html"> Read More<span class="screen-reader-text">  Read More</span></a></p>
The post <a href="https://coolshell.cn/articles/8961.html">从面向对象的设计模式看软件设计</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></description>
										<content:encoded><![CDATA[<p><script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158"
     crossorigin="anonymous"></script>前些天发了一篇《<a title="如此理解面向对象编程" href="https://coolshell.cn/articles/8745.html" target="_blank" rel="bookmark">如此理解面向对象编程</a>》的文章，然后引起了大家的热议。然后我在<a href="http://weibo.com/1401880315/z9wWHrrVR" target="_blank">微博上说</a>了一句——“<strong>那23个经典的设计模式和OO半毛钱关系没有，只不过人家用OO来实现罢了……OO的设计模式思想和Unix的设计思想基本没什么差别</strong>”，结果引来了一点点争议。所以，我写下这篇文章把我的观点说明一下。我希望这样可以让大家更容易地理解什么是设计模式。<strong>我顺便帮OO和 Unix/Linux搞搞基</strong>。</p>
<h4>什么是模式</h4>
<p>在正式说明GoF的那23个经典的设计模式其实和OO关系不大并和Unix的设计思想很相似的这个观点之前，让我先来说说什么是模式？设计模式的英文是Design Pattern，模式是Pattern的汉译。所谓Pattern就是一种规则，或是一种模型，或是一种习惯。Pattern这个东西到处都是，并不只有技术圏子里才有。比如：</p>
<ul>
<li>文章有文章的Pattern。如新闻有新闻的Pattern（第一段话简述了整个新闻），诗歌总是抒情的，论文总是死板的，讲稿总是高谈的，漫画总是幽默的，……</li>
<li><span style="line-height: 13px;">小说有小说的Pattern。比如，</span>
<ul>
<li><span style="line-height: 13px;">武侠小说必然要整个武林大会，整几个NB的武功和大师，分个正派和反派，还有一个或数个惊天阴谋，坏人总是要在一开始占尽优势，好人总是要力挽狂澜……</span></li>
<li><span style="line-height: 13px;">言情小说总是要有第三者，总是要有负心人，里面的女子总是要哭得死去活来，但又痴心不改，……</span></li>
</ul>
</li>
<li> 新闻联播的模式是：头10分钟领导很忙，中间10分钟人民很幸福，后10分钟国外很乱。中国政府官方宣传稿也模式也很明显，各种赞美，口号，胜利，总是要坚持个什么，团结个什么，迈向个什么，某某精神，某某思想，群众情绪稳定，不明真相，等等……</li>
<li>春节的模式是，回家，吃饺子，放个鞭炮，给压岁钱，同学聚会…… 同学聚会的模式基本上都是在饭桌上回忆一下校园时光，比较一下各自的当前处境，调戏一下女同学……</li>
<li>…… ……</li>
</ul>
<p>这就是Pattern，只要你细心观察，你会发现这世间有很多很多的Pattern。</p>
<p><span id="more-8961"></span></p>
<h4>GoF的23个设计模式</h4>
<p>《<a href="http://product.china-pub.com/25961" target="_blank">设计模式</a>》这本书中，GoF这四个人总结了23个经典的面向对象的设计模式，某中有5个创建模式，7个结构模式，11个行为模式。<strong>很多人都会觉得这是面向对象的设计模式，很多人也觉得非面向对象不能用这些模式。我觉得这是一种教条主义。</strong>就像《<a title="各种流行的编程风格" href="https://coolshell.cn/articles/2058.html" target="_blank">那些流行的编程方法</a>》中的“设计模式驱动型编程”一样，就像《<a title="如此理解面向对象编程" href="https://coolshell.cn/articles/8745.html" target="_blank">如此理解面向对象</a>》一样的那么的滑稽。</p>
<p>好了，回到我的论点——“<strong>GoF的这23个设计模式和OO关系不大，并且和Unix的设计思想基本一致，只不过GoF用OO实现了它们</strong>”，就像我上面说过的那些生活中的Pattern一样，只要你仔细思考，你会发现这23个设计模式在我们的生活和社会中也能有他们的身影。而且也一样可以用OO的方式实现之。</p>
<p>让我们来看看这23个经典的设计模式中的几个常用的模式：</p>
<p><strong>Factory 模式</strong>，这个模式可能是是个人都知道的模式。这个模式在现实社会中就像各种工厂一样，工厂跨界的不多，基本上都是在生产同一类的产品，有的生产汽车，有的生产电视，有的生产衣服，有的生产卫生纸……基本上来说，一个生产线上只有做同一类的东西。这和Factory模式很相似。编程中，像内存池，线程池，连接池等池化技术都是这个模式，当然，Factory给你的一个对象，而不单单只是资源，factory创建出来的对象都有同样的接口可以被多态调用。<strong>这其实和Unix把所有的硬件都factory成文件一样，并提供了read/write等文件操作来让你操作任意设备的I/O</strong>。</p>
<p><strong>Abstract Factor</strong>y：抽象工厂这个模式是创建一组有同一主题的不同的类。这个模式在现实社会当中也有很多例子，比如：</p>
<ul>
<li>移动公司的合约机计划，88套餐（通话100分钟，短信100条，彩信，20条，上网200M），128套餐（通话200分钟，短信150条，彩信50条，上网500M）……</li>
</ul>
<ul>
<li>家里的装修，总是要有厨卫，有门，有灯，有沙发，有茶几，有床，有衣柜，有电视，有冰箱，有洗衣机……，这些都是必需的，只是每个家庭里的具体装修不一样。</li>
</ul>
<ul>
<li>Diablo游戏中的Normal，Hard，Nightmare，Hell模式，这些模式的怪和场景和故事情况都差不多，就是每个场景的怪物和装备的属性不一样。或是WarCraft中的地图就是一个Abstract Factory模式(注：Warcraft的地图什么都能干)。这和学校中的小学，初中，高中，大学差不多，都是一样的学习环境，一样的教学方式，一样的教室，都要期中考和期末考，都有班长和科代表，就是学的东西的难度不一样，但基本上都是语文，英语，数，理，化，还有永远都有的政治课。学校就是一个抽象工厂。</li>
</ul>
<p>这就是抽象工厂的业务模型（或是：Business Pattern），你觉得是不是不一定非要用OO来实现这样的模式？（我们思考一下，我们会不会被先入为主了，觉得不会OO都不知道怎么实现了），不用OO，用相同格式但内容不同的配置文件是不是也能实现？在Unix下<strong>，抽象工厂这个模式在Unix下就像是/etc/rcX.d下的那些东西，1代表命令行单用户，2，代表命令行多用户，3代表命令行多用户完整模式启动，5代表图形界面启动，0代表关机，6代表重启，你要切换的话，init &lt;X&gt;就行了</strong>。</p>
<p><strong>Prototype模式</strong>，原型模式，复制一个类的实现。这个模式在现实中的例子也有很多：传真，复印，都是这个模式。<strong>Unix进程和Github项目的Fork就是一种。进程fork明显不是OO的模型</strong>（参看：<a title="一个fork的面试题" href="https://coolshell.cn/articles/7965.html" target="_blank">关于Fork的一道面试题</a>）。用非OO的方法同样可以实现这个模式。</p>
<p><strong>Singleton模式</strong>，单例模式。生活中，公司只有一个CEO，法律限制你只能有一个老婆，你只能有一个身份证号，一个TCP端口只能被一个进程使用，等等。软件开发方面，并不一定只有OO才能做到，你可以用一个全局变量，一个中心服务器，甚至可以使用行政手段来约束开发中不会出现多个实例。<strong>Unix下实现单例进程的一个最常用的实践是在进程启动的时候用“(S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)”模式打开一个“锁文件”</strong>。</p>
<p><strong>Adapter模式</strong>，适配器模式。可以兼容欧洲美国中国的插头或插座，万能读卡器，可以播放各种格式多媒体文件的插放器，可以解析FTP/HTTP/HTTPS/等网络协议的浏览器，可以兼容各大银行的银联接口、支付宝、Paypal、VISA等银行接口，可以适配各种后端的解释器的Nginx或Apache，等等。用非OO的编程方式就是重新包装成一个标准接口。<strong>这个模式很像Unix下的/dev下的那些文件，操作系统把系统设备适配成文件，于是你就可以使用read/write来进行读写了</strong>。</p>
<p><strong>Bridge模式</strong>，桥接模式。这个模式用的更多，比如一个灯具可以接各种灯泡或灯管，一个电钻可以换上不同的钻头来适应不同的材料，一辆汽车可以随时更换不同的轮胎来适应不同的路面，你的桌面可以随时更换一个图片来适应你的心情，你的单反相机可以更换不同的镜头来拍不同的照片…… 桥接模式说白了就是组件化，模块化，可以自由拼装。在OO中，其主要是通过让业务类组合一个标准接口来完成，这在非OO的程序设计中用得实在是太多了，主要是通过回调函数或是标准接口来实现。这个也是Unix设计哲学中的主要思想。<strong>在Unix中，文件的权限使用的就是Bridge模式，标准接口是用户，用户组和其它，rwx三个模式，然后用 chmod/chown改一改，这文件就有不同的属主和属性了</strong>。</p>
<p><strong>Decorator模式</strong>，装饰模式。这个模式在生活中太多了，你给你的手机或电脑贴个什么，挂个什么，吃东西的时候加点什么佐料，多点肉还是多个蛋，一个Unix/Linux命令的各种参数是对这个命令的修饰，等等。<strong>我觉得这个模式在Unix中最经常的体现就是通过管道把命令连接起来来完成一个功能</strong>，比如：ps -elf  是列进程的，用管道 grep hchen就可以达到过滤的目的，grep的逻辑没有侵入ps中，grep 修饰了 ps，但是其组合起来完成了一个特定的功能。可见，这和OO没有什么关系。</p>
<p><strong>Facade模式，</strong>这个模式我们每个人从会编程的时候就在无意识地用这个模式了。这个模式就是把一大堆类拼装起来，并统一往外提供提口。在现实生活中这样的例子太多了，比如：旅行社把机票，酒店，景点，导游，司机，进店打了一个包叫旅行；IBM把主机，存储，OS，J2EE，DB，网络，流程打了个包叫企业级解决方案。Unix中最典型的一个例子就是用Shell脚本组合各种命令来创造一个新的功能，这是的Shell中的各种命令通过标准I/O这个接口进行组合交互。</p>
<p><strong>Proxy模式</strong>，代理模式。我们租个房，买个机票，打个官司，都少不了代理，人大代表代理了老百姓去行使政治权力。我们去饭馆里吃饭也是一种代理模式，因为我们只管吃就好了，洗菜做饭洗碗的工作都被Proxy帮你干了，于是你就省事多了。操作系统就是硬件的代理，CDN就是网站的代理，……使用代理你可以让事情变理更简单，也可以在代理层加入一些权限检查，这样可以让业务模块更关注业务，而把一些非业务的事情剥离出来交给代理以完成解耦。可见这个模式和OO没啥关系。<strong>Unix下这个模式最佳体现就是Shell，它代理了系统调用并提供UI</strong>。还有很多命令会帮你把/proc目录下的那些文件内容整理和显示出来。</p>
<p><strong>Chain of Responsibility模式</strong>，劫匪来抢银行，保安搞不定，就交给110，110搞不定就交给武警。有什么事件发生时的响应的Escalation Path，办公中的逐级审批。这个模式用一个函数指针数组或是栈结构就可以实现了。这个思想很像编程中的异常处理机制，一层一层地往上传递异常直到异常被捕捉。<strong>在Unix下，一个最简单的例子就是用 &amp;&amp; 或 || 来把命令拼起来，如：cmd1 &amp;&amp; cmd2  或 cmd3 || cmd4 ， 如果cmd1失败了，cmd2就不会执行，如果cmd3失败了，cmd4才会执行。</strong>如： cd lib &amp;&amp; rm -rf .o 或 ping -c1 coolshell.cn &amp;&amp; ssh haoel@coolshell.cn</p>
<p><strong>Command模式</strong>，这恐怕是软件里最多的模式了，比如：编译器里的Undo/Redo，宏录制。还有数据库的事务处理，线程池，设置向导，包括程序并行执行的指令集等等。这个模式主要是把一个对象的行为封装成一个一个的有相同接口的command，然后交给一个统一的命令执行器执行或管理这些命令。<strong>这个模式和我们的Unix/Linux机器启动时在/etc/init.d下的那些S和K开头的脚本很像，把各种daemon的启动和退出行为封装成一个脚本其支持reload/start/stop/status这样的命令，然后把他们按一定的规范做符号链接到/etc/init.d目录下，这样操作系统就会接管这些daemon的启动和退出</strong>。</p>
<p><strong>Observer模式</strong>，观察者模式，这个模式也叫pub-sub模式，很像我们用手机订阅手机报，微博的follow的信息流也是这样的一个模式。MVC中的C会sub V中的事件，用非OO的方式其实也是一个回调函数的事。在很多异步系统中，你需要知道最终的调用有没有成功，比如说调用支付宝的支付接口，你需要向支付宝注册一个回调的接口，以便支付宝回调你。<strong>Linux下的一些系统调用如epoll/aio/inotify/signal都是这种思路</strong>。</p>
<p><strong>Strategy 模式</strong>，策略模式，这个模式和Bridge模式很像，只不过Bridge是结构模式，其主要是用于对象的构造；而Strategy是行为模式，主要是用于对象的行为。策略模式很像浏览器里的各种插件，只要你装了某个插件，你就有某个功能。你可以安装多个插件来让你的浏览器有更多的功能（书本上的这个模式是你只能选用一个算法，当然，我们不用那么教条）。<strong>就像《<a title="你可能不知道的Shell" href="https://coolshell.cn/articles/8619.html" target="_blank">你可能不知道的Shell</a>》中的那个设置设置$EDITOR变量后可以按ctrl+x e启动编译器，或是用set -o vi或set -o emacs 来让自己的shell像vi或 emacs 一样，或是像find -exec或xargs一样的拼装命令</strong>。</p>
<p><strong>Bridge 和 Strategy是OO设计模式里的“Favor Composition Over Inheritance” 的典范，其实现了接口与实现分离的</strong>。Unix中的Shell就是一种，你可随意地更换不同的Shell。还有Emacs中的LISP驱动C，C实现了引擎，交给LISP实现逻辑。把程序分为前端和后端，通过socket专用应用协议进行通讯，前端实现策略，后端实现机制。再看看makefile把编译器和源代码的解耦，命令行输出这个接口可以把一个复杂的功能解耦并抽像成各种各样小而美的小功能命令，等等这样的例子，你会发现，还有大量的编程框架都会多少采用这样的思想，可以让你的软件像更换汽车零件一样方便。我在用<a title="用Unix的设计思想来应对多变的需求" href="https://coolshell.cn/articles/7236.html" target="_blank">Unix的设计思想来应对变更的需求</a>中说过灯具厂，灯泡厂，和开关厂的例子。</p>
<h4>后记</h4>
<p>因为写作仓促，上面的那些东西，可能会你让你觉得有些牵强，那么抱歉了，你可以帮我看看在生活中和 Unix里有没有更帅的例子。</p>
<p>不过，我们会发现上面OO搞出来的那么多模式在Unix下看来好像没有那么复杂，而且Unix下看起来并没有那么多模式，而且Unix中的设计模式无非就是这么几个关键词：<strong>单一，简洁，模块，拼装</strong>。我们再来看看OO设计的两大准则：<strong>1）钟情于组合而不是继承，2）依赖于接口而不是实现</strong>。还有S.O.L.I.D原则也一样（如果你仔细观察，你会发现SOLID原则在Unix下也是完美地体现）。你看，Unix和OO设计模式是不是完美的统一吗？</p>
<p>我有种强烈的感觉——<strong>Unix对这些所谓的OO的设计模式实现得更好</strong>。因为Unix就一条设计模式！再次推荐《<em><a href="http://book.douban.com/subject/5387401/" target="_blank">The Art of Unix Programming</a></em>》</p>
<p><img decoding="async" loading="lazy" class="size-full wp-image-8967 aligncenter" alt="Unix Kiss" src="https://coolshell.cn/wp-content/uploads/2013/01/kiss.png" width="468" height="219" srcset="https://coolshell.cn/wp-content/uploads/2013/01/kiss.png 468w, https://coolshell.cn/wp-content/uploads/2013/01/kiss-300x140.png 300w" sizes="(max-width: 468px) 100vw, 468px" /></p>
<h4>餐后甜点</h4>
<p>我上面提到了《<em><a href="http://book.douban.com/subject/5387401/" target="_blank">The Art of Unix Programming</a></em>》，所以我有必要再谈谈这本书中我中毒最深的一章《模块性：保持清晰和简洁》中所谈到的胶合层。</p>
<p>胶合层这一节中说了，我们开发软件一般要么Top-Down，要么Bottom-Up，这两种方法都有好有不好。顶层一般是应用逻辑层，底层一般是原语层（我理解为技术沉淀层，或是技术基础层）。自顶向下的开发，你可能会因为开发到底层后发现底层可沉淀的东西越来越不爽（因为被可能被很多业务逻辑所侵入），如果自底向上的开发，你可能越到上层你越发现很多你下面干的基础上工作有很多用不上（比如干多了）。所以，最好的方式是同时进行，一会顶层，一会底层，来来回回的开发——说白了就是在开发中不断的重构，边开发边理解边沉淀。</p>
<p>无论怎么样，你会发现需要一层胶合层来胶合业务逻辑层和底层原语层（软件开发中的业务层和技术层的胶合），Unix的设计哲学认为，这层胶合层应该尽量地薄，胶合层越多，我们就只能在其中苦苦挣扎。</p>
<p>其实，<strong>胶合层原则就是分离原则上更为上层地体现，策略（业务逻辑）和机制（基础技术或原语）的清楚的分离。你可以看到，OO和Unix都是在做这样的分离。但是需要注意到的时，OO用抽象接口来做这个分离——很多OO的模式中，抽象层太多了，导致胶合层太过于复杂了，也就是说，OO鼓励了——“厚重地胶合和复杂层次”，反而增加了程序的复杂度（这种情况在恶化中）。而Unix采用的是薄的胶合层，薄地相当的优雅</strong>。（通过这段话的描述，我相信你会明白了《<a title="如此理解面向对象编程" href="https://coolshell.cn/articles/8745.html" target="_blank" rel="bookmark">如此理解面向对象编程</a>》中的个例子——为什么用OO来实现会比用非OO来实现更为地恶心——那就是因为OO胶合层太复杂了）</p>
<p><strong>OO的最大的问题就——接口复杂度太高，胶合层太多！</strong>（注：Unix编程艺术这本书里说了软件有三个复杂度：代码量、接口、实现，这三个东西构成了我们的软件复杂度）</p>
<h4>再送一个果盘</h4>
<p>大家一定记得《<a title="SteveY对Amazon和Google平台的长篇大论 - 60,581 人阅读" href="https://coolshell.cn/articles/5701.html">SteveY对Amazon和Google平台的长篇大论</a>》中Amazon中那个令人非常向往的SOA式的架构。因为以前在Amazon，有些话不好说。现在可以说了，我在Amazon里，我个人对这个服务化的架构相当的不待见，太复杂，复杂以乱七八糟，方向是好的，想法也是好的，但是这东西和OO一样，造成大量的接口复杂度，今天的Amazon，完全没人知道各个服务是怎么个调用的，一团乱麻（其内部并不像你看到的AWS那么的美妙。注：AWS是非常不错的，是相当好的设计）。</p>
<p><strong>那么我们怎么来解决SOA的接口复杂度问题？其实，Unix早就给出了答案——数据驱动编程</strong>（详见：《Unix编程艺术》的第9.1章），在我离开Amazon的时候，美国总部的Principle SDE们在吐槽今天Amazon的SOA架构，更好的架构应该是数据驱动式的。（今天还在Amazon的同学可以上内网boardcast上看看相关的Principle Talk视频）</p>
<p>（瞎扯一句：这本来是我想在2012年杭州QCon上的分享的一个主题，无奈当时被大会组织者给拒了，所以只好讲了一个《建一支小团队》，今天有多人还是不能明白甚至反感我的那个《小团队》的演讲，但是我相信那是必然的趋势，就像十年前大家在说“程序员只能干到30岁”时，当时的我我却毫不犹豫地相信十年后，30岁以上的有经验的老程序员一定会成为各个公司角逐和竟争的红人）<!--



<p align="center"><a href= target=_blank><img decoding="async" src=""></a></p>





<p align="center"><img decoding="async" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg"> <img decoding="async" loading="lazy" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg" width="300" height="300"> <br />关注CoolShell微信公众账号和微信小程序</p>

 

--></p>
<div style="margin-top: 15px; font-size: 16px;color: #cc0000;">
<p align="center"><strong>（转载本站文章请注明作者和出处 <a href="https://coolshell.cn/">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>
</div>

<div class="wp_rp_wrap  wp_rp_vertical_m" ><div class="wp_rp_content"><h3 class="related_post_title">相关文章</h3><ul class="related_post wp_rp"><li ><a href="https://coolshell.cn/articles/9949.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2013/07/inverted-bookshelf_thumb-150x150.jpg" alt="IoC/DIP其实是一种管理思想" width="150" height="150" /></a><a href="https://coolshell.cn/articles/9949.html" class="wp_rp_title">IoC/DIP其实是一种管理思想</a></li><li ><a href="https://coolshell.cn/articles/7236.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2012/05/Bannière-Unix-linux-150x150.jpg" alt="用Unix的设计思想来应对多变的需求" width="150" height="150" /></a><a href="https://coolshell.cn/articles/7236.html" class="wp_rp_title">用Unix的设计思想来应对多变的需求</a></li><li ><a href="https://coolshell.cn/articles/4535.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/1.jpg" alt="一些软件设计的原则" width="150" height="150" /></a><a href="https://coolshell.cn/articles/4535.html" class="wp_rp_title">一些软件设计的原则</a></li><li ><a href="https://coolshell.cn/articles/17416.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2016/07/cache-150x150.png" alt="缓存更新的套路" width="150" height="150" /></a><a href="https://coolshell.cn/articles/17416.html" class="wp_rp_title">缓存更新的套路</a></li><li ><a href="https://coolshell.cn/articles/6950.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/21.jpg" alt="需求变化与IoC" width="150" height="150" /></a><a href="https://coolshell.cn/articles/6950.html" class="wp_rp_title">需求变化与IoC</a></li><li ><a href="https://coolshell.cn/articles/21672.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2021/12/bachelor-mechanical-eng-icon@72x-150x150.png" alt="我做系统架构的一些原则" width="150" height="150" /></a><a href="https://coolshell.cn/articles/21672.html" class="wp_rp_title">我做系统架构的一些原则</a></li></ul></div></div>The post <a href="https://coolshell.cn/articles/8961.html">从面向对象的设计模式看软件设计</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></content:encoded>
					
					<wfw:commentRss>https://coolshell.cn/articles/8961.html/feed</wfw:commentRss>
			<slash:comments>92</slash:comments>
		
		
			</item>
		<item>
		<title>应该知道的Linux技巧</title>
		<link>https://coolshell.cn/articles/8883.html</link>
					<comments>https://coolshell.cn/articles/8883.html#comments</comments>
		
		<dc:creator><![CDATA[陈皓]]></dc:creator>
		<pubDate>Wed, 09 Jan 2013 00:24:29 +0000</pubDate>
				<category><![CDATA[Unix/Linux]]></category>
		<category><![CDATA[操作系统]]></category>
		<category><![CDATA[杂项资源]]></category>
		<category><![CDATA[Bash]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[Performance]]></category>
		<category><![CDATA[Shell]]></category>
		<category><![CDATA[Unix]]></category>
		<category><![CDATA[vim]]></category>
		<guid isPermaLink="false">http://coolshell.cn/?p=8883</guid>

					<description><![CDATA[<p>这篇文章来源于Quroa的一个问答《What are some time-saving tips that every Linux user should kn...</p>
<p class="read-more"><a class="btn btn-default" href="https://coolshell.cn/articles/8883.html"> Read More<span class="screen-reader-text">  Read More</span></a></p>
The post <a href="https://coolshell.cn/articles/8883.html">应该知道的Linux技巧</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></description>
										<content:encoded><![CDATA[<p><script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158"
     crossorigin="anonymous"></script><img decoding="async" loading="lazy" class="alignright size-full wp-image-8899" alt="" src="https://coolshell.cn/wp-content/uploads/2013/01/linux-bash-300x225.jpg" width="300" height="225" />这篇文章来源于Quroa的一个问答《<a href="http://www.quora.com/Linux/What-are-some-time-saving-tips-that-every-Linux-user-should-know#" target="_blank">What are some time-saving tips that every Linux user should know?</a>》—— Linux用户有哪些应该知道的提高效率的技巧。我觉得挺好的，总结得比较好，把其转过来，并加了一些自己的理解。 首先，我想告诉大家，<strong>在Unix/Linux下，最有效率技巧的不是操作图形界面，而是命令行操作，因为命令行意味着自动化</strong>。如果你看过《<a title="你可能不知道的Shell" href="https://coolshell.cn/articles/8619.html" target="_blank">你可能不知道的Shell</a>》以及《<a title="28个Unix/Linux的命令行神器" href="https://coolshell.cn/articles/7829.html" target="_blank">28个Unix/Linux的命令行神器</a>》你就会知道Linux有多强大，这个强大完全来自于命令行，于是，就算你不知道怎么去<a title="做个环保主义的程序员" href="https://coolshell.cn/articles/7186.html" target="_blank">做一个环保主义的程序员</a>，至少他们可以让你少熬点夜，从而有利于你的身体健康和性生活。下面是一个有点长的列表，正如作者所说，你并不需要知道所有的这些东西，但是如果你还在很沉重地在使用Linux的话，这些东西都值得你看一看。 （注：如果你想知道下面涉及到的命令的更多的用法，你一定要man一点。对于一些命令，你可以需要先yum或apt-get来安装一下，如果有什么问题，别忘了Google。如果你要Baidu的话，我仅代表这个地球上所有的生物包括微生物甚至细菌病毒和小强BS你到宇宙毁灭）</p>
<h4>基础</h4>
<ul>
<li><strong>学习 <a href="http://www.quora.com/Bash-shell" target="_blank">Bash</a> </strong>。你可以man bash来看看bash的东西，并不复杂也并不长。你用别的shell也行，但是bash是很强大的并且也是系统默认的。（学习zsh或tsch只会让你在很多情况下受到限制）</li>
</ul>
<ul>
<li><strong>学习 vim</strong> 。在Linux下，基本没有什么可与之竞争的编<del>译</del>辑器（就算你是一个Emacs或Eclipse的重度用户）。你可以看看《<a title="简明 Vim 练级攻略" href="https://coolshell.cn/articles/5426.html" target="_blank">简明vim攻略</a>》和 《<a title="游戏：VIM大冒险" href="https://coolshell.cn/articles/7166.html" target="_blank">Vim的冒险游戏</a>》以及《<a title="给程序员的VIM速查卡" href="https://coolshell.cn/articles/5479.html" target="_blank">给程序员的Vim速查卡</a>》还有《<a title="将vim变得简单:如何在vim中得到你最喜爱的IDE特性" href="https://coolshell.cn/articles/894.html" target="_blank">把Vim变成一个编程的IDE</a>》等等。</li>
</ul>
<ul>
<li><strong>了解 ssh</strong>。明白不需要口令的用户认证（通过ssh-agent, ssh-add），学会用ssh翻墙，用scp而不是ftp传文件，等等。你知道吗？scp 远端的时候，你可以按tab键来查看远端的目录和文件（当然，需要无口令的用户认证），这都是bash的功劳。</li>
</ul>
<p><span id="more-8883"></span></p>
<ul>
<li><strong>熟悉bash的作业管理</strong>，如： &amp;, Ctrl-Z, Ctrl-C, jobs, fg, bg, kill, 等等。当然，你也要知道Ctrl+\（SIGQUIT）和Ctrl+C （SIGINT）的区别。</li>
</ul>
<ul>
<li><strong>简单的文件管理</strong> ： ls 和 ls -l (你最好知道 &#8220;ls -l&#8221; 的每一列的意思), less, head, tail 和 tail -f, ln 和 ln -s (你知道明白hard link和soft link的不同和优缺点), chown, chmod, du (如果你想看看磁盘的大小 du -sk *), df, mount。当然，原作者忘了find命令。</li>
</ul>
<ul>
<li><strong>基础的网络管理</strong>： ip 或 ifconfig, dig。当然，原作者还忘了如netstat, ping, traceroute, 等</li>
</ul>
<ul>
<li><strong>理解正则表达式</strong>，还有grep/egrep的各种选项。比如： -o, -A, 和 -B 这些选项是很值得了解的。</li>
</ul>
<ul>
<li><strong>学习使用 apt-get 和 yum 来查找和安装软件</strong>（前者的经典分发包是Ubuntu，后者的经典分发包是Redhat），我还建议你试着从源码编译安装软件。</li>
</ul>
<p><b>日常</b></p>
<ul>
<li>在 bash 里，使用 Ctrl-R 而不是上下光标键来查找历史命令。</li>
</ul>
<ul>
<li>在 bash里，使用 Ctrl-W 来删除最后一个单词，使用 Ctrl-U 来删除一行。请man bash后查找Readline Key Bindings一节来看看bash的默认热键，比如：Alt-. 把上一次命令的最后一个参数打出来，而Alt-* 则列出你可以输入的命令。</li>
</ul>
<ul>
<li>回到上一次的工作目录： cd &#8211;  （回到home是 cd ~）</li>
</ul>
<ul>
<li>使用 xargs。这是一个很强大的命令。你可以使用-L来限定有多少个命令，也可以用-P来指定并行的进程数。如果你不知道你的命令会变成什么样，你可以使用xargs echo来看看会是什么样。当然， -I{} 也很好用。示例：</li>
</ul>
<blockquote>
<pre data-enlighter-language="shell" class="EnlighterJSRAW">find . -name \*.py | xargs grep some_function

cat hosts | xargs -I{} ssh root@{} hostname</pre>
</blockquote>
<ul>
<li>pstree -p 可以帮你显示进程树。（读过我的那篇《<a title="一个fork的面试题" href="https://coolshell.cn/articles/7965.html" target="_blank">一个fork的面试题</a>》的人应该都不陌生）</li>
</ul>
<ul>
<li>使用 pgrep 和 pkill 来找到或是kill 某个名字的进程。 (-f 选项很有用).</li>
</ul>
<ul>
<li>了解可以发给进程的信号。例如：要挂起一个进程，使用 kill -STOP [pid]. 使用 man 7 signal 来查看各种信号，使用kill -l 来查看数字和信号的对应表</li>
</ul>
<ul>
<li>使用 nohup 或  disown 如果你要让某个进程运行在后台。</li>
</ul>
<ul>
<li>使用netstat -lntp来看看有侦听在网络某端口的进程。当然，也可以使用 lsof。</li>
</ul>
<ul>
<li>在bash的脚本中，你可以使用 set -x 来debug输出。使用 set -e 来当有错误发生的时候abort执行。考虑使用 set -o pipefail 来限制错误。还可以使用trap来截获信号（如截获ctrl+c）。</li>
</ul>
<ul>
<li>在bash 脚本中，subshells (写在圆括号里的) 是一个很方便的方式来组合一些命令。一个常用的例子是临时地到另一个目录中，例如：</li>
</ul>
<blockquote>
<pre data-enlighter-language="shell" class="EnlighterJSRAW"># do something in current dir
(cd /some/other/dir; other-command)
# continue in original dir</pre>
</blockquote>
<ul>
<li>在 bash 中，注意那里有很多的变量展开。如：检查一个变量是否存在: ${name:?error message}。如果一个bash的脚本需要一个参数，也许就是这样一个表达式 input_file=${1:?usage: $0 input_file}。一个计算表达式： i=$(( (i + 1) % 5 ))。一个序列： {1..10}。 截断一个字符串： ${var%suffix} 和 ${var#prefix}。 示例： if var=foo.pdf, then echo ${var%.pdf}.txt prints &#8220;foo.txt&#8221;.</li>
</ul>
<ul>
<li>通过 &lt;(some command) 可以把某命令当成一个文件。示例：比较一个本地文件和远程文件 /etc/hosts： diff /etc/hosts &lt;(ssh somehost cat /etc/hosts)</li>
</ul>
<ul>
<li>了解什么叫 &#8220;<a href="http://zh.wikipedia.org/wiki/Here%E6%96%87%E6%A1%A3" target="_blank">here documents</a>&#8221; ，就是诸如 cat &lt;&lt;EOF 这样的东西。</li>
</ul>
<ul>
<li>在 bash中，使用重定向到标准输出和标准错误。如： some-command &gt;logfile 2&gt;&amp;1。另外，要确认某命令没有把某个打开了的文件句柄重定向给标准输入，最佳实践是加上 &#8220;&lt;/dev/null&#8221;，把/dev/null重定向到标准输入。</li>
</ul>
<ul>
<li>使用 man ascii 来查看 ASCII 表。</li>
</ul>
<ul>
<li>在远端的 ssh 会话里，使用 screen 或 dtach 来保存你的会话。（参看《<a title="28个Unix/Linux的命令行神器" href="https://coolshell.cn/articles/7829.html" target="_blank">28个Unix/Linux的命令行神器</a>》）</li>
</ul>
<ul>
<li>要来debug Web，试试curl 和 curl -I 或是 wget 。我觉得debug Web的利器是firebug，curl和wget是用来抓网页的，呵呵。</li>
</ul>
<ul>
<li>把 HTML 转成文本： lynx -dump -stdin</li>
</ul>
<ul>
<li>如果你要处理XML，使用 xmlstarlet</li>
</ul>
<ul>
<li>对于 Amazon S3， s3cmd 是一个很方便的命令（还有点不成熟）</li>
</ul>
<ul>
<li>在 ssh中，知道怎么来使用ssh隧道。通过 -L or -D (还有-R) ，翻墙神器。</li>
</ul>
<ul>
<li>你还可以对你的ssh 做点优化。比如，.ssh/config 包含着一些配置：避免链接被丢弃，链接新的host时不需要确认，转发认证，以前使用压缩（如果你要使用scp传文件）：</li>
</ul>
<blockquote>
<pre data-enlighter-language="shell" class="EnlighterJSRAW">TCPKeepAlive=yes
ServerAliveInterval=15
ServerAliveCountMax=6
StrictHostKeyChecking=no
Compression=yes
ForwardAgent=yes</pre>
</blockquote>
<ul>
<li>如果你有输了个命令行，但是你改变注意了，但你又不想删除它，因为你要在历史命令中找到它，但你也不想执行它。那么，你可以按下 Alt-# ，于是这个命令关就被加了一个#字符，于是就被注释掉了。</li>
</ul>
<p><b>数据处理 </b></p>
<ul>
<li>了解 sort 和 uniq 命令 (包括 uniq 的 -u 和 -d 选项).</li>
</ul>
<ul>
<li>了解用 cut, paste, 和 join 命令来操作文本文件。很多人忘了在cut前使用join。</li>
</ul>
<ul>
<li>如果你知道怎么用sort/uniq来做集合交集、并集、差集能很大地促进你的工作效率。假设有两个文本文件a和b已解被 uniq了，那么，用sort/uniq会是最快的方式，无论这两个文件有多大（sort不会被内存所限，你甚至可以使用-T选项，如果你的/tmp目录很小）</li>
</ul>
<blockquote>
<pre data-enlighter-language="shell" class="EnlighterJSRAW">cat a b | sort | uniq &gt; c   # c is a union b 并集

cat a b | sort | uniq -d &gt; c   # c is a intersect b 交集

cat a b b | sort | uniq -u &gt; c   # c is set difference a - b 差集</pre>
</blockquote>
<ul>
<li>了解和字符集相关的命令行工具，包括排序和性能。很多的Linux安装程序都会设置LANG 或是其它和字符集相关的环境变量。这些东西可能会让一些命令（如：sort）的执行性能慢N多倍（注：就算是你用UTF-8编码文本文件，你也可以很安全地使用ASCII来对其排序）。如果你想Disable那个i18n 并使用传统的基于byte的排序方法，那就设置export LC_ALL=C （实际上，你可以把其放在 .bashrc）。如果这设置这个变量，你的sort命令很有可能会是错的。</li>
</ul>
<ul>
<li>了解 awk 和 sed，并用他们来做一些简单的数据修改操作。例如：求第三列的数字之和： awk &#8216;{ x += $3 } END { print x }&#8217;。这可能会比Python快3倍，并比Python的代码少三倍。</li>
</ul>
<ul>
<li>使用 shuf 来打乱一个文件中的行或是选择文件中一个随机的行。</li>
</ul>
<ul>
<li>了解sort命令的选项。了解key是什么（-t和-k）。具体说来，你可以使用-k1,1来对第一列排序，-k1来对全行排序。</li>
</ul>
<ul>
<li>Stable sort (sort -s) 会很有用。例如：如果你要想对两例排序，先是以第二列，然后再以第一列，那么你可以这样： sort -k1,1 | sort -s -k2,2</li>
</ul>
<ul>
<li>我们知道，在bash命令行下，Tab键是用来做目录文件自动完成的事的。但是如果你想输入一个Tab字符（比如：你想在sort -t选项后输入&lt;tab&gt;字符），你可以先按Ctrl-V，然后再按Tab键，就可以输入&lt;tab&gt;字符了。当然，你也可以使用$&#8217;\t&#8217;。</li>
</ul>
<ul>
<li>如果你想查看二进制文件，你可以使用hd命令（在CentOS下是hexdump命令），如果你想编译二进制文件，你可以使用bvi命令（<a href="http://bvi.sourceforge.net/" target="_blank">http://bvi.sourceforge.net/</a> 墙）</li>
</ul>
<ul>
<li>另外，对于二进制文件，你可以使用strings（配合grep等）来查看二进制中的文本。</li>
</ul>
<ul>
<li>对于文本文件转码，你可以试一下 iconv。或是试试更强的 uconv 命令（这个命令支持更高级的Unicode编码）</li>
</ul>
<ul>
<li>如果你要分隔一个大文件，你可以使用split命令（split by size）和csplit命令（split by a pattern）。</li>
</ul>
<p><b>系统调试</b></p>
<ul>
<li>如果你想知道磁盘、CPU、或网络状态，你可以使用 iostat, netstat, top (或更好的 htop), 还有 dstat 命令。你可以很快地知道你的系统发生了什么事。关于这方面的命令，还有iftop, iotop等（参看《<a title="28个Unix/Linux的命令行神器" href="https://coolshell.cn/articles/7829.html" target="_blank">28个Unix/Linux的命令行神器</a>》）</li>
</ul>
<ul>
<li>要了解内存的状态，你可以使用free和vmstat命令。具体来说，你需要注意 “cached” 的值，这个值是Linux内核占用的内存。还有free的值。</li>
</ul>
<ul>
<li>Java 系统监控有一个小的技巧是，你可以使用kill -3 &lt;pid&gt; 发一个SIGQUIT的信号给JVM，可以把堆栈信息（包括垃圾回收的信息）dump到stderr/logs。</li>
</ul>
<ul>
<li>使用 mtr 会比使用 traceroute 要更容易定位一个网络问题。</li>
</ul>
<ul>
<li>如果你要找到哪个socket或进程在使用网络带宽，你可以使用 iftop 或 nethogs。</li>
</ul>
<ul>
<li>Apache的一个叫 ab 的工具是一个很有用的，用quick-and-dirty的方式来测试网站服务器的性能负载的工作。如果你需要更为复杂的测试，你可以试试 siege。</li>
</ul>
<ul>
<li>如果你要抓网络包的话，试试 wireshark 或 tshark。</li>
</ul>
<ul>
<li>了解 strace 和 ltrace。这两个命令可以让你查看进程的系统调用，这有助于你分析进程的hang在哪了，怎么crash和failed的。你还可以用其来做性能profile，使用 -c 选项，你可以使用-p选项来attach上任意一个进程。</li>
</ul>
<ul>
<li>了解用ldd命令来检查相关的动态链接库。注意：<a title="ldd 的一个安全问题" href="https://coolshell.cn/articles/1626.html" target="_blank">ldd的安全问题</a></li>
</ul>
<ul>
<li>使用gdb来调试一个正在运行的进程或分析core dump文件。参看我写的《<a title="GDB中应该知道的几个调试方法" href="https://coolshell.cn/articles/3643.html" target="_blank">GDB中应该知道的几个调试方法</a>》</li>
</ul>
<ul>
<li>学会到 /proc 目录中查看信息。这是一个Linux内核运行时记录的整个操作系统的运行统计和信息，比如： /proc/cpuinfo, /proc/xxx/cwd, /proc/xxx/exe, /proc/xxx/fd/, /proc/xxx/smaps.</li>
</ul>
<ul>
<li>如果你调试某个东西为什么出错时，sar命令会有用。它可以让你看看 CPU, 内存, 网络, 等的统计信息。</li>
</ul>
<ul>
<li>使用 dmesg 来查看一些硬件或驱动程序的信息或问题。</li>
</ul>
<p>作者最后加了一个免责声明：Disclaimer: Just because you <i>can</i> do something in bash, doesn&#8217;t necessarily mean you should. ;) （全文完）<!--



<p align="center"><a href= target=_blank><img decoding="async" src=""></a></p>





<p align="center"><img decoding="async" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg"> <img decoding="async" loading="lazy" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg" width="300" height="300"> <br />关注CoolShell微信公众账号和微信小程序</p>

 

--></p>
<div style="margin-top: 15px; font-size: 16px;color: #cc0000;">
<p align="center"><strong>（转载本站文章请注明作者和出处 <a href="https://coolshell.cn/">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>
</div>

<div class="wp_rp_wrap  wp_rp_vertical_m" ><div class="wp_rp_content"><h3 class="related_post_title">相关文章</h3><ul class="related_post wp_rp"><li ><a href="https://coolshell.cn/articles/8619.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2012/11/shell.01-150x150.png" alt="你可能不知道的Shell" width="150" height="150" /></a><a href="https://coolshell.cn/articles/8619.html" class="wp_rp_title">你可能不知道的Shell</a></li><li ><a href="https://coolshell.cn/articles/7829.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2012/07/dstat_screenshot-150x150.png" alt="28个Unix/Linux的命令行神器" width="150" height="150" /></a><a href="https://coolshell.cn/articles/7829.html" class="wp_rp_title">28个Unix/Linux的命令行神器</a></li><li ><a href="https://coolshell.cn/articles/9070.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2013/02/awk-150x150.jpg" alt="AWK 简明教程" width="150" height="150" /></a><a href="https://coolshell.cn/articles/9070.html" class="wp_rp_title">AWK 简明教程</a></li><li ><a href="https://coolshell.cn/articles/19219.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2019/03/linux.ninja_-150x150.png" alt="打造高效的工作环境 &#8211; Shell 篇" width="150" height="150" /></a><a href="https://coolshell.cn/articles/19219.html" class="wp_rp_title">打造高效的工作环境 &#8211; Shell 篇</a></li><li ><a href="https://coolshell.cn/articles/18360.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg" alt="程序员练级攻略（2018)  与我的专栏" width="150" height="150" /></a><a href="https://coolshell.cn/articles/18360.html" class="wp_rp_title">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href="https://coolshell.cn/articles/17998.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2017/07/systemd-1-150x150.jpeg" alt="Linux PID 1 和 Systemd" width="150" height="150" /></a><a href="https://coolshell.cn/articles/17998.html" class="wp_rp_title">Linux PID 1 和 Systemd</a></li></ul></div></div>The post <a href="https://coolshell.cn/articles/8883.html">应该知道的Linux技巧</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></content:encoded>
					
					<wfw:commentRss>https://coolshell.cn/articles/8883.html/feed</wfw:commentRss>
			<slash:comments>192</slash:comments>
		
		
			</item>
		<item>
		<title>你可能不知道的Shell</title>
		<link>https://coolshell.cn/articles/8619.html</link>
					<comments>https://coolshell.cn/articles/8619.html#comments</comments>
		
		<dc:creator><![CDATA[404null]]></dc:creator>
		<pubDate>Fri, 23 Nov 2012 00:19:24 +0000</pubDate>
				<category><![CDATA[Unix/Linux]]></category>
		<category><![CDATA[杂项资源]]></category>
		<category><![CDATA[编程工具]]></category>
		<category><![CDATA[Bash]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[Shell]]></category>
		<category><![CDATA[Unix]]></category>
		<guid isPermaLink="false">http://coolshell.cn/?p=8619</guid>

					<description><![CDATA[<p>Shell也叫做命令行界面，它是*nix操作系统下用户和计算机的交互界面。Shell这个词是指操作系统中提供访问内核服务的程序。 这篇文章向大家介绍Shell一...</p>
<p class="read-more"><a class="btn btn-default" href="https://coolshell.cn/articles/8619.html"> Read More<span class="screen-reader-text">  Read More</span></a></p>
The post <a href="https://coolshell.cn/articles/8619.html">你可能不知道的Shell</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></description>
										<content:encoded><![CDATA[<p><script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158"
     crossorigin="anonymous"></script>Shell也叫做命令行界面，它是*nix操作系统下用户和计算机的交互界面。Shell这个词是指操作系统中提供访问内核服务的程序。</p>
<p>这篇文章向大家介绍Shell一些非广为人知、但却实用有趣的知识，权当品尝shell主食后的甜点吧。</p>
<h4>科普</h4>
<p>先科普几个你可能不知道的事实：</p>
<ul>
<li>Shell几乎是和Unix操作系统一起诞生，第一个Unix Shell是肯·汤普逊（Ken Thompson）以Multics上的Shell为模范在1971年改写而成，并命名Thompson sh。即便是后来流行的bash（shell的一种变体），它的年龄实际上比当前流行的所有的Linux kernel都大，可谓在Linux系统上是先有Shell再有Kernel。</li>
</ul>
<ul>
<li>当前绝大部分*nix和MacOS操作系统里的默认的Shell都是bash，bash由Brian Fox在1987年创造，全称Bourne Again shell ( bash)。</li>
</ul>
<ul>
<li>你或许听说除了bash之外，还有Bourne shell ( sh)，Korn shell ( ksh)，C shell （包括 csh and tcsh），但是你知道这个星球上一共存在着大约50多种不同的shell么？想了解他们，请参考 <a href="http://www.freebsd.org/ports/shells.html" target="_blank" rel="noopener">http://www.freebsd.org/ports/shells.html</a>。</li>
</ul>
<ul>
<li>每个月<a href="http://www.tiobe.com/index.php/content/paperinfo/tpci/index.html" target="_blank" rel="noopener">tiobe</a>上都会给一个编程语言的排名，来显示各种语言的流行度。排名指数综合了全球范围内使用该语言的工程师人数、教学的课程数和第三方供应商数。截止至2012年11月份，tiobe公布的编程语言排行榜里，bash的指数是0.56%排名22位。如果算上它旗下的awk 0.21%和tcl 0.146%，大概就能排到14名。注意这里还不包括bash的同源的兄弟姐妹csh、ksh等，算上它们，shell家族有望接近前十。值得一提的是一直以来shell的排名就很稳定，不像某些“暴发户”语言，比如objective-c，这些语言的流行完全是因为当前Apple系的崛起，但这种热潮极有可能来得快去得更快。</li>
</ul>
<p><span id="more-8619"></span></p>
<p><img decoding="async" loading="lazy" class="aligncenter size-full wp-image-18796" src="https://coolshell.cn/wp-content/uploads/2012/11/shell.01.png" alt="" width="616" height="614" srcset="https://coolshell.cn/wp-content/uploads/2012/11/shell.01.png 616w, https://coolshell.cn/wp-content/uploads/2012/11/shell.01-150x150.png 150w, https://coolshell.cn/wp-content/uploads/2012/11/shell.01-300x300.png 300w, https://coolshell.cn/wp-content/uploads/2012/11/shell.01-200x200.png 200w, https://coolshell.cn/wp-content/uploads/2012/11/shell.01-271x270.png 271w" sizes="(max-width: 616px) 100vw, 616px" /></p>
<p><img decoding="async" loading="lazy" class="aligncenter size-full wp-image-18797" src="https://coolshell.cn/wp-content/uploads/2012/11/shell.02.png" alt="" width="290" height="531" /></p>
<p>&nbsp;</p>
<p>全球最大的源代码仓库Github里，shell相关的项目数占到了8%，跻身前5和Java相当，可见在实战工程里，shell可谓宝刀不老。图片来源，<a href="https://github.com/languages">参见这里</a></p>
<p><img decoding="async" loading="lazy" class="aligncenter size-full wp-image-18798" src="https://coolshell.cn/wp-content/uploads/2012/11/shell.03.png" alt="" width="700" height="284" /></p>
<h4>一些强大的命令</h4>
<p>再分享一些可能你不知道的shell用法和脚本，简单&amp;强大！</p>
<p><em>在阅读以下部分前，强烈建议读者打开一个shell实验，这些都不是shell教科书里的大路货哦：）</em></p>
<ul>
<li><strong><code>!$</code></strong><br />
<code>!$</code>是一个特殊的环境变量，它代表了上一个命令的最后一个字符串。如：你可能会这样：</p>
<pre data-enlighter-language="shell" class="EnlighterJSRAW">$mkdir mydir
$mv mydir yourdir
$cd yourdir</pre>
<p>可以改成：</p>
<pre data-enlighter-language="shell" class="EnlighterJSRAW">$mkdir mydir
$mv !$ yourdir
$cd !$</pre>
</li>
</ul>
<ul>
<li><strong><code>sudo !!</code></strong><br />
以root的身份执行上一条命令 。<br />
场景举例：比如Ubuntu里用<code>apt-get</code>安装软件包的时候是需要root身份的，我们经常会忘记在<code>apt-get</code>前加<code>sudo</code>。每次不得不加上<code>sudo</code>再重新键入这行命令，这时可以很方便的用<code>sudo !!</code>完事。<br />
（陈皓注：在shell下，有时候你会输入很长的命令，你可以使用!xxx来重复最近的一次命令，比如，你以前输入过，vi /where/the/file/is, 下次你可以使用 !vi 重得上次最近一次的vi命令。）</li>
</ul>
<ul>
<li><strong><code>cd –</code></strong><br />
回到上一次的目录 。<br />
场景举例：当前目录为<code>/home/a</code>，用<code>cd ../b</code>切换到<code>/home/b</code>。这时可以通过反复执行<code>cd –</code>命令在<code>/home/a</code>和<code>/home/b</code>之间来回方便的切换。<br />
（陈皓注：cd ~ 是回到自己的Home目录，cd ~user，是进入某个用户的Home目录）</li>
</ul>
<ul>
<li><strong><code>'ALT+.' or '&lt;ESC&gt; .'</code></strong><br />
热建alt+. 或 esc+. 可以把上次命令行的参数给重复出来。</li>
</ul>
<ul>
<li><strong><code>^old^new</code></strong><br />
替换前一条命令里的部分字符串。<br />
场景：<code>echo "wanderful"</code>，其实是想输出<code>echo "wonderful"</code>。只需要<code>^a^o</code>就行了，对很长的命令的错误拼写有很大的帮助。（陈皓注：也可以使用 <strong>!!:gs/old/new</strong>）</li>
</ul>
<ul>
<li><strong><code>du -s * | sort -n | tail</code></strong><br />
列出当前目录里最大的10个文件。</li>
</ul>
<ul>
<li><strong><code>:w !sudo tee %</code></strong><br />
在vi中保存一个只有root可以写的文件</li>
</ul>
<ul>
<li><strong><code>date -d@1234567890</code></strong><br />
时间截转时间</li>
</ul>
<ul>
<li>
<div title="Click to select this command">
<div><strong><code>&gt; file.txt</code></strong><br />
创建一个空文件，比touch短。</div>
</div>
</li>
</ul>
<ul>
<li><strong><code>mtr coolshell.cn</code></strong><br />
mtr命令比traceroute要好。</li>
</ul>
<ul>
<li>在命令行前加空格，该命令不会进入history里。</li>
</ul>
<ul>
<li><strong><code>echo "ls -l" | at midnight</code></strong><br />
在某个时间运行某个命令。</li>
</ul>
<ul>
<li><strong><code>curl -u user:pass -d status="Tweeting from the shell" http://twitter.com/statuses/update.xml</code></strong><br />
命令行的方式更新twitter。</li>
</ul>
<ul>
<li><strong><code>curl -u username --silent "https://mail.google.com/mail/feed/atom" | perl -ne 'print "\t" if /&lt;name&gt;/; print "$2\n" if /&lt;(title|name)&gt;(.*)&lt;\/\1&gt;/;'</code></strong><br />
检查你的gmail未读邮件</li>
</ul>
<ul>
<li><strong><code>ps aux | sort -nk +4 | tail</code></strong><br />
列出头十个最耗内存的进程</li>
</ul>
<ul>
<li><strong><code>man ascii</code></strong><br />
显示ascii码表。<br />
场景：忘记ascii码表的时候还需要google么?尤其在天朝网络如此“顺畅”的情况下，就更麻烦在GWF多应用一次规则了，直接用本地的<code>man ascii</code>吧。</li>
</ul>
<ul>
<li><strong><code>ctrl-x e</code></strong><br />
快速启动你的默认编辑器（由变量$EDITOR设置）。</li>
</ul>
<ul>
<li><strong><code>netstat –tlnp</code></strong><br />
列出本机进程监听的端口号。（陈皓注：netstat -anop 可以显示侦听在这个端口号的进程）</li>
</ul>
<ul>
<li><strong><code>tail -f /path/to/file.log | sed '/^Finished: SUCCESS$/ q'</code></strong><br />
当file.log里出现Finished: SUCCESS时候就退出tail，这个命令用于实时监控并过滤log是否出现了某条记录。</li>
</ul>
<ul>
<li><strong><code>ssh user@server bash &lt; /path/to/local/script.sh</code></strong><br />
在远程机器上运行一段脚本。这条命令最大的好处就是不用把脚本拷到远程机器上。</li>
</ul>
<ul>
<li><strong><code>ssh user@host cat /path/to/remotefile | diff /path/to/localfile -</code></strong><br />
比较一个远程文件和一个本地文件</li>
</ul>
<ul>
<li>
<div title="Click to select this command">
<div><strong><code>net rpc shutdown -I ipAddressOfWindowsPC -U username%password</code></strong><br />
远程关闭一台Windows的机器</div>
</div>
</li>
</ul>
<ul>
<li><strong><code>screen -d -m -S some_name ping my_router</code></strong><br />
后台运行一段不终止的程序，并可以随时查看它的状态。<code>-d -m</code>参数启动“分离”模式，<code>-S</code>指定了一个session的标识。可以通过<code>-R</code>命令来重新“挂载”一个标识的session。更多细节请参考screen用法 <code>man screen</code>。</li>
</ul>
<ul>
<li><strong><code>wget --random-wait -r -p -e robots=off -U mozilla http://www.example.com</code></strong><br />
下载整个www.example.com网站。（注：别太过分，大部分网站都有防爬功能了：））</li>
</ul>
<ul>
<li><strong><code>curl ifconfig.me</code></strong><br />
当你的机器在内网的时候，可以通过这个命令查看外网的IP。</li>
</ul>
<ul>
<li><strong><code>convert input.png -gravity NorthWest -background transparent -extent 720x200  output.png</code></strong><br />
改一下图片的大小尺寸</li>
</ul>
<ul>
<li><strong><code>lsof –i</code></strong><br />
实时查看本机网络服务的活动状态。</li>
</ul>
<ul>
<li><strong><code>vim scp://username@host//path/to/somefile</code></strong><br />
vim一个远程文件</li>
</ul>
<ul>
<li><strong><code>python -m SimpleHTTPServer</code></strong><br />
一句话实现一个HTTP服务，把当前目录设为HTTP服务目录，可以通过<code>http://localhost:8000</code>访问 这也许是这个星球上最简单的HTTP服务器的实现了。</li>
</ul>
<ul>
<li><strong><code>history | awk '{CMD[$2]++;count++;} END { for (a in CMD )print CMD[a] " " CMD[a]/count*100 "% " a }' | grep -v "./" | column -c3 -s " " -t | sort -nr | nl | head -n10</code></strong><br />
(陈皓注：有点复杂了，history|awk &#8216;{print $2}&#8217;|awk &#8216;BEGIN {FS=&#8221;|&#8221;} {print $1}&#8217;|sort|uniq -c|sort -rn|head -10)<br />
这行脚本能输出你最常用的十条命令，由此甚至可以洞察你是一个什么类型的程序员。</li>
</ul>
<ul>
<li>
<div title="Click to select this command">
<div><strong><code>tr -c "[:digit:]" " " &lt; /dev/urandom | dd cbs=$COLUMNS conv=unblock | GREP_COLOR="1;32" grep --color "[^ ]"</code></strong><br />
想看看Marix的屏幕效果吗？（不是很像，但也很Cool!）</div>
</div>
</li>
</ul>
<p>看不懂行代码？没关系，系统的学习一下*nix shell脚本吧，力荐<a href="http://www.ituring.com.cn/book/980">《Linux命令行与Shell脚本编程大全》</a>。</p>
<h4>参考文献：</h4>
<ul>
<li><a href="http://en.wikipedia.org/wiki/Unix_shell#Shell_categories">Unix Shell Wiki</a></li>
<li><a href="https://github.com">Github language ranking</a></li>
<li><a href="http://www.softpanorama.org/People/Shell_giants/introduction.shtml">An introduction of Unix Shell history</a></li>
<li><a href="http://www.tiobe.com/index.php/content/paperinfo/tpci/index.html" target="_blank" rel="noopener">Tiobe Software</a></li>
<li><a href="http://www.commandlinefu.com/" target="_blank" rel="noopener">http://www.commandlinefu.com/</a></li>
</ul>
<p><!--



<p align="center"><a href= target=_blank><img decoding="async" src=""></a></p>





<p align="center"><img decoding="async" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg"> <img decoding="async" loading="lazy" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg" width="300" height="300"> <br />关注CoolShell微信公众账号和微信小程序</p>

 

--></p>
<div style="margin-top: 15px; font-size: 16px;color: #cc0000;">
<p align="center"><strong>（转载本站文章请注明作者和出处 <a href="https://coolshell.cn/">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>
</div>

<div class="wp_rp_wrap  wp_rp_vertical_m" ><div class="wp_rp_content"><h3 class="related_post_title">相关文章</h3><ul class="related_post wp_rp"><li ><a href="https://coolshell.cn/articles/8883.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2013/01/linux-bash-300x225-150x150.jpg" alt="应该知道的Linux技巧" width="150" height="150" /></a><a href="https://coolshell.cn/articles/8883.html" class="wp_rp_title">应该知道的Linux技巧</a></li><li ><a href="https://coolshell.cn/articles/9070.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2013/02/awk-150x150.jpg" alt="AWK 简明教程" width="150" height="150" /></a><a href="https://coolshell.cn/articles/9070.html" class="wp_rp_title">AWK 简明教程</a></li><li ><a href="https://coolshell.cn/articles/19219.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2019/03/linux.ninja_-150x150.png" alt="打造高效的工作环境 &#8211; Shell 篇" width="150" height="150" /></a><a href="https://coolshell.cn/articles/19219.html" class="wp_rp_title">打造高效的工作环境 &#8211; Shell 篇</a></li><li ><a href="https://coolshell.cn/articles/18360.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg" alt="程序员练级攻略（2018)  与我的专栏" width="150" height="150" /></a><a href="https://coolshell.cn/articles/18360.html" class="wp_rp_title">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href="https://coolshell.cn/articles/17998.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2017/07/systemd-1-150x150.jpeg" alt="Linux PID 1 和 Systemd" width="150" height="150" /></a><a href="https://coolshell.cn/articles/17998.html" class="wp_rp_title">Linux PID 1 和 Systemd</a></li><li ><a href="https://coolshell.cn/articles/12103.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2014/11/tux-fork-150x150.gif" alt="vfork 挂掉的一个问题" width="150" height="150" /></a><a href="https://coolshell.cn/articles/12103.html" class="wp_rp_title">vfork 挂掉的一个问题</a></li></ul></div></div>The post <a href="https://coolshell.cn/articles/8619.html">你可能不知道的Shell</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></content:encoded>
					
					<wfw:commentRss>https://coolshell.cn/articles/8619.html/feed</wfw:commentRss>
			<slash:comments>149</slash:comments>
		
		
			</item>
		<item>
		<title>对技术的态度</title>
		<link>https://coolshell.cn/articles/8088.html</link>
					<comments>https://coolshell.cn/articles/8088.html#comments</comments>
		
		<dc:creator><![CDATA[陈皓]]></dc:creator>
		<pubDate>Thu, 16 Aug 2012 15:50:25 +0000</pubDate>
				<category><![CDATA[杂项资源]]></category>
		<category><![CDATA[编程语言]]></category>
		<category><![CDATA[C++]]></category>
		<category><![CDATA[Java]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[Programmer]]></category>
		<category><![CDATA[Unix]]></category>
		<category><![CDATA[程序员]]></category>
		<guid isPermaLink="false">http://coolshell.cn/?p=8088</guid>

					<description><![CDATA[<p>最近人品爆发，图灵社区，InfoQ，51CTO相继对我做了采访，前两天我把InfoQ对我的采访张贴了出来，今天，图灵社区和51CTO对我的采访发布了（图灵的访谈...</p>
<p class="read-more"><a class="btn btn-default" href="https://coolshell.cn/articles/8088.html"> Read More<span class="screen-reader-text">  Read More</span></a></p>
The post <a href="https://coolshell.cn/articles/8088.html">对技术的态度</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></description>
										<content:encoded><![CDATA[<p><script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158"
     crossorigin="anonymous"></script>最近人品爆发，图灵社区，InfoQ，51CTO相继对我做了采访，前两天我把<a title="InfoQ的ArchSummit大会对我的采访" href="https://coolshell.cn/articles/8031.html" target="_blank">InfoQ对我的采访张贴了出来</a>，今天，图灵社区和51CTO对我的采访发布了（<a title="图灵访谈之三十二：我的精神家园——陈皓（@左耳朵耗子）专访" href="http://www.ituring.com.cn/article/9174" target="_blank">图灵的访谈</a> ，<a title="专访陈皓：有关带队、沟通、成长与变化" href="http://developer.51cto.com/art/201208/353256.htm" target="_blank">51CTO的访谈</a>），我是一个有技术焦虑症的人，我的经历比较特殊，对大家来说可能也没有什么意思，这两个采都有一些重叠的部分，不过有些观点我想再加强一些，并放在这里和大家一起分享一下。</p>
<h4>对于日新月异的新技术，你是什么态度？</h4>
<p>遇到新技术我会去了解，但不会把很大的精力放在这些技术（如：NoSQL，Node.js，等）。这些技术尚不成熟，只需要跟得住就可以了。技术十年以上可能是一个门槛。有人说技术更新换代很快，我一点儿都不觉得是这样想。虽然有不成熟的技术不断地涌出，但是成熟的技术，比如Unix，40多年，C，40多年，C++，30多年，TCP/IP，20多年，Java也有将近20年了……，所以，如果你着眼成熟的技术，其实并不多。</p>
<p>我的观点是——<strong>要了解技术就一定需要了解整个计算机的技术历史发展和进化路线。</strong>（这个观点，我在《<a title="程序员技术练级攻略" href="https://coolshell.cn/articles/4990.html" target="_blank">程序员练级攻略</a>》和《<a title="C++的坑真的多吗？" href="https://coolshell.cn/articles/7992.html" target="_blank">C++的坑多吗？</a>》中提到过多次了。）因为，<strong>你要朝着球运动的轨迹去，而不是朝着球的位置去，要知道球的运动轨迹，你就需要知道它历史上是怎么跑的</strong>。</p>
<p>如果要捋一个技术的脉络，70年代Unix的出现，是软件发展方面的一个里程碑，那个时期的C语言，也是语言方面的里程碑。（当时）所有的项目都在Unix/C上，全世界人都在用这两样东西写软件。Linux跟随的是Unix, Windows下的开发也是 C/C++。这时候出现的C++很自然就被大家接受了，企业级的系统很自然就会迁移到这上面，C++虽然接过了C的接力棒，但是它的问题是它没有一个企业方面的架构，而且太随意了，否则也不会有今天的Java。C++和C非常接近，它只不过是C的一个扩展，长年没有一个企业架构的框架。而Java在被发明后，被IBM把企业架构这部分的需求接了过来，J2EE的出现让C/C++捉襟见肘了，在语言进化上，还有Python/Ruby，后面还有了.NET，但可惜的是这只局限在Windows平台上。这些就是企业级软件方面语言层面就是C -&gt; C++ -&gt; Java这条主干，操作系统是Unix -&gt; Linux/Windows这条主干，软件开发中需要了解的网络知识就是Ethernet -&gt; IP -&gt; TCP/UDP 这条主干。另外一条脉络就是互联网方面的（HTML/CSS/JS/LAMP…）。我是一个有技术忧虑症的人，这几条软件开发的主线一定不能放弃。</p>
<p>另外，从架构上来说，我们可以看到，</p>
<p><span id="more-8088"></span></p>
<ul>
<li>从单机的年代，到C/S架构（界面，业务逻辑，数据SQL都在Client上，只有数据库服库在S上）</li>
<li>再到B/S结构（用浏览器来充当Client，但是传统的ASP/PHP/JSP/Perl/CGI这样的编程也都把界面，业务逻辑，和SQL都放在一起），但是B/S已经把这些东西放到了Web Server上，</li>
<li>再到后来的中间件，把业务逻辑再抽出一层，放到一个叫App Server上，经典的三层结构。</li>
<li>然后再到分布式结构，业务层分布式，数据层分布式。</li>
<li>再到今天的云架构——全部移到服务器。</li>
</ul>
<div>我们可以看到技术的变迁都一直再把东西往后端移，前端只剩一个浏览器或是一个手机。通过这个你可以看到整个技术发展的趋势。所以，如果你了解了这些变迁，了解了这些变迁过程“不断填坑”的过程，你将会对技术有很强的把握。</div>
<p>另外，我听到有很多人说，一些技术不适用，一些技术太学院派，但对我来说，无论是应用还是学术，我都会看，知识不愁多。何必搞应用的和搞学术的分开阵营，都是知识，学就好了。</p>
<p>技术的发展要根植于历史，而不是未来。不要和我描述这个技术的未来会多么美好（InfoQ 的 ArchSummit大会上有一个微软来的人把Node.js说得跟仙女一样，然后给了一个Hello World），我承认你用一些新的技术可以实现很多花哨的东西。但是，我认为技术都是承前的，只有承前的才会常青。所以说“某某（技术）要火”这样的话是没有意义的，等它火了、应用多了，规模大了，再说。有些人说：“不学C/C++也是没有问题的”，我对此的回应是：<strong>如果连技术主干都可以不学的话，还有什么其他的好学呢？这些是计算机发展的根、脉络、祖师爷，这样的东西怎么可以不学呢？</strong></p>
<p><strong></strong>另外，我们要去了解整个计算机文化，我觉得计算机文化源起于Unix/C这条线上（注意，我说的是文化不是技术）。我也写过很多与Unix文化相关的文章，大家可以看看我写的“<a title="Unix传奇(上篇)" href="https://coolshell.cn/articles/2322.html" target="_blank">Unix传奇</a>（<a title="Unix传奇(下篇)" href="https://coolshell.cn/articles/2324.html" target="_blank">尤其是下篇</a>）”。</p>
<h4>可是在应用环境中，对新技术的需求是很高的，你觉得在教育领域计算机科学的侧重应该是什么样的？</h4>
<p>学校教的大部分都是知识密集型的技术，但是社会上的企业大部分都是劳动密集型的。什么是劳动密集型的企业呢？麦当劳炸薯条就是劳动密集型的工作，用不到学校教授的那些知识。如果有一天你不炸薯条了，而要去做更大更专业的东西，学校里的知识就会派上用场。有人说一个语言、一个技术，能解决问题能用就行了，我不这样认为。<strong>我觉得你应该至少要知道这些演变和进化的过程。而如果你要解决一些业务和技术难题，就需要抓住某种技术很深入地学习，当成艺术一样来学习。</strong></p>
<p>我在“<a title="软件开发的“三重门”" href="https://coolshell.cn/articles/6526.html" target="_blank">软件开发‘三重门’</a>”里说过，第一重门是业务功能，在这重门里，的确是会编程就可以了；第二重门是业务性能，在这一重门里，技术的基础就很管用了，比如：操作系统的文件管理，进程调度，内存管理，网络的七层模型，TCP/<del>UCP</del>UDP的协议，语言用法、编译和类库的实现，数据结构，算法等等就非常关键了；第三重门是业务智能，在这一重门里，你会发现很多东西都很学院派了，比如，搜索算法，推荐算法，预测，统计，机器学习，图像识别，分布式架构和算法，等等，你需要读很多计算机学院派的论文。</p>
<p>总之，这主要看你职业生涯的背景了，如果你整天被当作劳动力来使用，你用到的技术就比较浅，比较实用，但是如果你做一些知识密集型的工作，你就需要用心来搞搞研究，就会发现你需要理论上的知识。比如说，我之前做过的跨国库存调配，需要知道最短路径的算法，而我现在在亚马逊做的库存预测系统，数据挖掘的那些东西都需要很强的数学建模、算法、数据挖掘的功底。</p>
<p>我觉得真正的高手都来自知识密集型的学院派。他们更强的是，可以把那些理论的基础知识应用到现在的业务上来。但很可惜，<strong>我们国内今天的教育并没有很好地把那些学院派的理论知识和现实的业务问题很好地接合起来。</strong>比如说一些哈希表或二叉树的数据结构，如果我们的学校在讲述这些知识的时候能够接合实际的业务问题，效果会非常不错，如：设计一个IP地址和地理位置的查询系统，设计一个分布式的NoSQL的数据库，或是设计一个地理位置的检索应用等等。在学习操作系统的时候，如果老师可以带学生做一个手机或嵌入式操作系统，或是研究一下Unix System V或是Linux的源码的话，会更有意思。在学习网络知识的时候，能带学生重点学一下以太网和TCP/IP的特性，并调优，如果能做一个网络上的pub/sub的消息系统或是做一个像Nginx一样的web server，那会更好。如果在学图形学的过程中能带领学生实践一个作图工具或是一个游戏引擎，那会更有意思。</p>
<p>总之，我们的教育和现实脱节太严重了，教的东西无论是在技术还是在实践上都严重落后和脱节，没有通过实际的业务或技术问题来教学生那些理论知识，这是一个失败。</p>
<h4><strong>那么，现在做一个软件开发者是否更加困难了？</strong></h4>
<p>我觉得倒不是。做一个软件开发者更简单了。因为现在互联网很发达，你可以找到很多共享的知识——相对于我那个时候。第一，知识你容易查到，然后社区很多，文章、分享的人也越来越多。我们那个时候没有的。上网一查，什么都没有。都得去自己琢磨，自己去调查。所以我觉得相比我们那个时候更容易了。第二，工具变多了。现在的工具比那个时候好用多了。我们那个时候就是一天到晚在vi里面，连个自动提示都没有，连个版本库管理都没有。不光工具变多，框架也多了，各种各样的编程框架。我们那时候都是生写。写JavaScript，生写，连个jQuery都没有。没有这些辅助性的、让你提高生产力的东西。J2EE那时候也没有。而且整个（开发环境）都很不成熟。一个服务器的最高配置就1GB的情况下，一个WebSphere起来就占了900多MB——这还能跑什么应用？所以只能去用最基础的系统。所以我觉得现在，无论是环境，还是开发的过程，都更规范了。以前我做开发的时候就是，什么都不懂就上了，瞎搞，没有什么开发规范，没有人理你，反正你搞得好就搞好，搞不好就搞不好了，全靠自己，包括做测试维护等等。我觉得现在的软件开发就很好，你一上去，就有好的工具，有好的知识库，有好的社区，有好的开发框架，还有好的流程，方法，甚至还有人帮你做测试，还有人告诉你应该怎么做。幸福得很。现在好多人还说这个不好那个不好，开发难什么的。其实容易多了。</p>
<p>但是，有个东西我觉得是现在的软件开发者比我们那时候变得更难的。就是，你享福了以后，人就变懒，变娇气了。对很多东西的抱怨就开始多了。我们那个时候哪有什么好抱怨的？没啥好抱怨的，有活就干，有东西学就赶快学。现在呢，学个什么东西还挑挑拣拣的，抱怨这个语言太扯，那个IDE不好，这个框架太差，版本管理工具太扯，等等。<strong>这就好像以前我没东西吃，只有个糠吃，要是有面包有馒头，我就觉得非常非常好了。现在是，好吃的东西多了我们还学会挑食了，这也不好用，那也不好用</strong>。</p>
<p>根本就不是技术变难了，环境变差了，是程序员变娇气了。所以软件开发变难，归根结底还是程序员们自己变娇气了。</p>
<h4>你如何在进度压力下，享受技术带来的快乐？</h4>
<p>中国人中庸的思想，入世和出世，每天的工作就是入世。举个例子，我十年前在上海的时候，给交通银行做项目的时候，每周休息一天，早九点到晚十点，每天工作12个小时，这样的工作持续了一整年，没有节假日，项目上的技术也没什么意思。当时我晚上十点回到住处，还想学一些C++/Java和Unix/Windows的技术，于是就看书到晚上11:30，每天如此，一年下来学到很多东西，时间没有荒废，心里就很开心。<strong>我觉得当时是快乐的，因为有成长的感觉是快乐的。</strong></p>
<p>现在的我，工作、写博客、养孩子，事情其实更多。我早上7:30起床，会浏览一下国外的新闻，hacker news, tech church, reddit, highavailability之类的站点，9点上班。晚上6、7点钟下班，开始带孩子。十点钟孩子睡了觉，我会开始重新细读一下这一天都发生了些什么事情。这个时间也有可能会用来看书。学习的过程（我）是不喜欢被打断的，所以从十点到十二点，家人都睡了，这正是我连续学习的好时间。可能从晚上11:30开始，我会做点笔记或者写博客。我现在对酷壳文章的质量要求比较高一些，所以大概积累一个星期的时间才可以生成一篇文章。每天我大概都在一两点钟才会睡觉。没办法，我有技术焦虑症。但是觉得这样的生活很充实，也很踏实。</p>
<p>另外，任何一门技术玩深了，都是很有意思的。有些人形成了一个价值取向，“我只做什么，绝不做什么”。前段时间有一个刚来亚马逊的工程师，他原来做的是数据挖掘推荐系统，原来的公司重组要让他做前端，他不肯就离职了，他说他不想做前端。我觉得，前端后端都是编程，Javascript是编程，C++也是编程。<strong>编程不在于你用什么语言去coding，而是你组织程序、设计软件的能力，只要你上升到脑力劳动上来，用什么都一样，技术无贵贱。</strong>你可以不喜欢那个技术，但是还是要了解了解，也没有必要完全不用，完全抛弃。Javascript啊——只要能被Javascript实现的，未来总有一天会被Javascript所取代。</p>
<p>回到问题，怎么才能享受到快乐呢？</p>
<ul>
<li>第一，入世和出世要分开，不要让世俗的东西打扰到你的内心世界，你的情绪不应该为别人所控，也不应该被世俗所污染，活得真实，活得真实你才会快乐。</li>
</ul>
<ul>
<li>第二，就是要有热情，有了热情，你的心情就会很好，加班都可以是快乐的，想一想我们整个通宵用来打游戏的时光，虽然很累，但是你也很开心，这都是因为有了热情的缘故。</li>
</ul>
<p>总之一句话——<strong>如果你没有兴趣，什么都是借口，如果你有兴趣了，什么都是好玩的</strong>。</p>
<h4></h4>
<p>（全文完）<!--



<p align="center"><a href= target=_blank><img decoding="async" src=""></a></p>





<p align="center"><img decoding="async" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg"> <img decoding="async" loading="lazy" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg" width="300" height="300"> <br />关注CoolShell微信公众账号和微信小程序</p>

 

--></p>
<div style="margin-top: 15px; font-size: 16px;color: #cc0000;">
<p align="center"><strong>（转载本站文章请注明作者和出处 <a href="https://coolshell.cn/">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>
</div>

<div class="wp_rp_wrap  wp_rp_vertical_m" ><div class="wp_rp_content"><h3 class="related_post_title">相关文章</h3><ul class="related_post wp_rp"><li ><a href="https://coolshell.cn/articles/18360.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg" alt="程序员练级攻略（2018)  与我的专栏" width="150" height="150" /></a><a href="https://coolshell.cn/articles/18360.html" class="wp_rp_title">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href="https://coolshell.cn/articles/4990.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2011/07/programmer-150x150.png" alt="程序员技术练级攻略" width="150" height="150" /></a><a href="https://coolshell.cn/articles/4990.html" class="wp_rp_title">程序员技术练级攻略</a></li><li ><a href="https://coolshell.cn/articles/4102.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/25.jpg" alt="如何学好C语言" width="150" height="150" /></a><a href="https://coolshell.cn/articles/4102.html" class="wp_rp_title">如何学好C语言</a></li><li ><a href="https://coolshell.cn/articles/4758.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg" alt="如何写出无法维护的代码" width="150" height="150" /></a><a href="https://coolshell.cn/articles/4758.html" class="wp_rp_title">如何写出无法维护的代码</a></li><li ><a href="https://coolshell.cn/articles/1992.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2009/12/language-fanboys-150x150.jpg" alt="程序员眼中的编程语言" width="150" height="150" /></a><a href="https://coolshell.cn/articles/1992.html" class="wp_rp_title">程序员眼中的编程语言</a></li><li ><a href="https://coolshell.cn/articles/19219.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2019/03/linux.ninja_-150x150.png" alt="打造高效的工作环境 &#8211; Shell 篇" width="150" height="150" /></a><a href="https://coolshell.cn/articles/19219.html" class="wp_rp_title">打造高效的工作环境 &#8211; Shell 篇</a></li></ul></div></div>The post <a href="https://coolshell.cn/articles/8088.html">对技术的态度</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></content:encoded>
					
					<wfw:commentRss>https://coolshell.cn/articles/8088.html/feed</wfw:commentRss>
			<slash:comments>132</slash:comments>
		
		
			</item>
		<item>
		<title>一个fork的面试题</title>
		<link>https://coolshell.cn/articles/7965.html</link>
					<comments>https://coolshell.cn/articles/7965.html#comments</comments>
		
		<dc:creator><![CDATA[陈皓]]></dc:creator>
		<pubDate>Wed, 01 Aug 2012 00:20:46 +0000</pubDate>
				<category><![CDATA[C/C++语言]]></category>
		<category><![CDATA[Unix/Linux]]></category>
		<category><![CDATA[操作系统]]></category>
		<category><![CDATA[编程语言]]></category>
		<category><![CDATA[趣味问题]]></category>
		<category><![CDATA[C++]]></category>
		<category><![CDATA[fork]]></category>
		<category><![CDATA[Puzzle]]></category>
		<category><![CDATA[Unix]]></category>
		<category><![CDATA[面试]]></category>
		<guid isPermaLink="false">http://coolshell.cn/?p=7965</guid>

					<description><![CDATA[<p>前两天有人问了个关于Unix的fork()系统调用的面试题，这个题正好是我大约十年前找工作时某公司问我的一个题，我觉得比较有趣，写篇文章与大家分享一下。这个题是...</p>
<p class="read-more"><a class="btn btn-default" href="https://coolshell.cn/articles/7965.html"> Read More<span class="screen-reader-text">  Read More</span></a></p>
The post <a href="https://coolshell.cn/articles/7965.html">一个fork的面试题</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></description>
										<content:encoded><![CDATA[<p><script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158"
     crossorigin="anonymous"></script>前两天有人问了个关于Unix的fork()系统调用的面试题，这个题正好是我大约十年前找工作时某公司问我的一个题，我觉得比较有趣，写篇文章与大家分享一下。这个题是这样的：</p>
<p><strong>题目：请问下面的程序一共输出多少个“-”？</strong></p>
<pre data-enlighter-language="c" class="EnlighterJSRAW">
#include &lt;stdio.h&gt;
#include &lt;sys/types.h&gt;
#include &lt;unistd.h&gt;

int main(void)
{
   int i;
   for(i=0; i&lt;2; i++){
      fork();
      printf(&quot;-&quot;);
   }

   wait(NULL);
   wait(NULL);

   return 0;
}
</pre>
<p>如果你对fork()的机制比较熟悉的话，这个题并不难，输出应该是6个“-”，但是，实际上这个程序会很tricky地输出8个“-”。</p>
<p>要讲清这个题，我们首先需要知道fork()系统调用的特性，</p>
<p><span id="more-7965"></span></p>
<ul>
<li>fork()系统调用是Unix下以自身进程创建子进程的系统调用，一次调用，两次返回，如果返回是0，则是子进程，如果返回值&gt;0，则是父进程（返回值是子进程的pid），这是众为周知的。</li>
</ul>
<ul>
<li>还有一个很重要的东西是，在fork()的调用处，整个父进程空间会原模原样地复制到子进程中，包括指令，变量值，程序调用栈，环境变量，缓冲区，等等。</li>
</ul>
<p>所以，上面的那个程序为什么会输入8个“-”，这是因为printf(&#8220;-&#8220;);语句有buffer，所以，对于上述程序，printf(&#8220;-&#8220;);把“-”放到了缓存中，并没有真正的输出（参看《<a title="C语言的谜题" href="https://coolshell.cn/articles/945.html" target="_blank">C语言的迷题</a>》中的第一题），<strong>在fork的时候，缓存被复制到了子进程空间</strong>，所以，就多了两个，就成了8个，而不是6个。</p>
<p>另外，多说一下，我们知道，Unix下的设备有“<a href="http://en.wikipedia.org/wiki/Device_file#Block_devices" target="_blank">块设备</a>”和“<a href="http://en.wikipedia.org/wiki/Device_file#Character_devices" target="_blank">字符设备</a>”的概念，所谓块设备，就是以一块一块的数据存取的设备，字符设备是一次存取一个字符的设备。磁盘、内存都是块设备，字符设备如键盘和串口。<strong>块设备一般都有缓存，而字符设备一般都没有缓存</strong>。</p>
<p>对于上面的问题，我们如果修改一下上面的printf的那条语句为：</p>
<p><code data-enlighter-language="c" class="EnlighterJSRAW">printf(&quot;-\n&quot;);</code></p>
<p>或是</p>
<pre data-enlighter-language="c" class="EnlighterJSRAW"> printf(&quot;-&quot;);
fflush(stdout);</pre>
<p>就没有问题了（就是6个“-”了），因为程序遇到“\n”，或是EOF，或是缓中区满，或是文件描述符关闭，或是主动flush，或是程序退出，就会把数据刷出缓冲区。需要注意的是，标准输出是行缓冲，所以遇到“\n”的时候会刷出缓冲区，但对于磁盘这个块设备来说，“\n”并不会引起缓冲区刷出的动作，那是全缓冲，你可以使用setvbuf来设置缓冲区大小，或是用fflush刷缓存。</p>
<p>我估计有些朋友可能对于fork()还不是很了解，那么我们把上面的程序改成下面这样：</p>
<pre data-enlighter-language="c" class="EnlighterJSRAW">
#include &lt;stdio.h&gt;
#include &lt;sys/types.h&gt;
#include &lt;unistd.h&gt;
int main(void)
{
   int i;
   for(i=0; i&lt;2; i++){
      fork();
      //注意：下面的printf有“\n”
      printf(&quot;ppid=%d, pid=%d, i=%d \n&quot;, getppid(), getpid(), i);
   }
   sleep(10); //让进程停留十秒，这样我们可以用pstree查看一下进程树
   return 0;
}
</pre>
<p>于是，上面这段程序会输出下面的结果，（注：编译出的可执行的程序名为fork）</p>
<pre data-enlighter-language="shell" class="EnlighterJSRAW">ppid=8858, pid=8518, i=0
ppid=8858, pid=8518, i=1
ppid=8518, pid=8519, i=0
ppid=8518, pid=8519, i=1
ppid=8518, pid=8520, i=1
ppid=8519, pid=8521, i=1

$ pstree -p | grep fork
|-bash(8858)-+-fork(8518)-+-fork(8519)---fork(8521)
|            |            `-fork(8520)</pre>
<p>面对这样的图你可能还是看不懂，没事，我好事做到底，画个图给你看看：</p>
<p><img decoding="async" loading="lazy" class="aligncenter size-full wp-image-7968" title="fork 程序调用图" src="https://coolshell.cn/wp-content/uploads/2012/07/fork01jpg.jpg" alt="" width="620" height="407" srcset="https://coolshell.cn/wp-content/uploads/2012/07/fork01jpg.jpg 620w, https://coolshell.cn/wp-content/uploads/2012/07/fork01jpg-300x197.jpg 300w, https://coolshell.cn/wp-content/uploads/2012/07/fork01jpg-411x270.jpg 411w" sizes="(max-width: 620px) 100vw, 620px" /></p>
<p>注意：上图中的我用了几个色彩，相同颜色的是同一个进程。于是，我们的pstree的图示就可以成为下面这个样子：（下图中的颜色与上图对应）</p>
<p><img decoding="async" loading="lazy" class="aligncenter size-full wp-image-7969" title="fork进程树" src="https://coolshell.cn/wp-content/uploads/2012/07/fork02.jpg" alt="" width="437" height="97" srcset="https://coolshell.cn/wp-content/uploads/2012/07/fork02.jpg 437w, https://coolshell.cn/wp-content/uploads/2012/07/fork02-300x66.jpg 300w" sizes="(max-width: 437px) 100vw, 437px" /></p>
<p>这样，对于printf(&#8220;-&#8220;);这个语句，我们就可以很清楚的知道，哪个子进程复制了父进程标准输出缓中区里的的内容，而导致了多次输出了。（如下图所示，就是我阴影并双边框了那两个子进程）</p>
<p><img decoding="async" loading="lazy" class="aligncenter size-full wp-image-7970" title="fork程序执行图" src="https://coolshell.cn/wp-content/uploads/2012/07/fork03.jpg" alt="" width="626" height="415" srcset="https://coolshell.cn/wp-content/uploads/2012/07/fork03.jpg 626w, https://coolshell.cn/wp-content/uploads/2012/07/fork03-300x198.jpg 300w" sizes="(max-width: 626px) 100vw, 626px" /></p>
<p>现在你明白了吧。（另，对于图中的我本人拙劣的配色，请见谅!）</p>
<p>（全文完）<!--



<p align="center"><a href= target=_blank><img decoding="async" src=""></a></p>





<p align="center"><img decoding="async" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg"> <img decoding="async" loading="lazy" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg" width="300" height="300"> <br />关注CoolShell微信公众账号和微信小程序</p>

 

--></p>
<div style="margin-top: 15px; font-size: 16px;color: #cc0000;">
<p align="center"><strong>（转载本站文章请注明作者和出处 <a href="https://coolshell.cn/">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>
</div>

<div class="wp_rp_wrap  wp_rp_vertical_m" ><div class="wp_rp_content"><h3 class="related_post_title">相关文章</h3><ul class="related_post wp_rp"><li ><a href="https://coolshell.cn/articles/4162.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg" alt="又一个有趣的面试题" width="150" height="150" /></a><a href="https://coolshell.cn/articles/4162.html" class="wp_rp_title">又一个有趣的面试题</a></li><li ><a href="https://coolshell.cn/articles/3961.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/21.jpg" alt="“火柴棍式”程序员面试题" width="150" height="150" /></a><a href="https://coolshell.cn/articles/3961.html" class="wp_rp_title">“火柴棍式”程序员面试题</a></li><li ><a href="https://coolshell.cn/articles/3738.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/26.jpg" alt="打印质数的各种算法" width="150" height="150" /></a><a href="https://coolshell.cn/articles/3738.html" class="wp_rp_title">打印质数的各种算法</a></li><li ><a href="https://coolshell.cn/articles/3445.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg" alt="输出从1到1000的数" width="150" height="150" /></a><a href="https://coolshell.cn/articles/3445.html" class="wp_rp_title">输出从1到1000的数</a></li><li ><a href="https://coolshell.cn/articles/1532.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg" alt="到处都是Unix的胎记" width="150" height="150" /></a><a href="https://coolshell.cn/articles/1532.html" class="wp_rp_title">到处都是Unix的胎记</a></li><li ><a href="https://coolshell.cn/articles/18360.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg" alt="程序员练级攻略（2018)  与我的专栏" width="150" height="150" /></a><a href="https://coolshell.cn/articles/18360.html" class="wp_rp_title">程序员练级攻略（2018)  与我的专栏</a></li></ul></div></div>The post <a href="https://coolshell.cn/articles/7965.html">一个fork的面试题</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></content:encoded>
					
					<wfw:commentRss>https://coolshell.cn/articles/7965.html/feed</wfw:commentRss>
			<slash:comments>223</slash:comments>
		
		
			</item>
		<item>
		<title>28个Unix/Linux的命令行神器</title>
		<link>https://coolshell.cn/articles/7829.html</link>
					<comments>https://coolshell.cn/articles/7829.html#comments</comments>
		
		<dc:creator><![CDATA[陈皓]]></dc:creator>
		<pubDate>Wed, 11 Jul 2012 00:10:11 +0000</pubDate>
				<category><![CDATA[杂项资源]]></category>
		<category><![CDATA[编程工具]]></category>
		<category><![CDATA[CLI]]></category>
		<category><![CDATA[Game]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[Performance]]></category>
		<category><![CDATA[rsync]]></category>
		<category><![CDATA[Unix]]></category>
		<category><![CDATA[vim]]></category>
		<guid isPermaLink="false">http://coolshell.cn/?p=7829</guid>

					<description><![CDATA[<p>下面是Kristóf Kovács收集的28个Unix/Linux下的28个命令行下的工具（原文链接），有一些是大家熟悉的，有一些是非常有用的，有一些是不为人知...</p>
<p class="read-more"><a class="btn btn-default" href="https://coolshell.cn/articles/7829.html"> Read More<span class="screen-reader-text">  Read More</span></a></p>
The post <a href="https://coolshell.cn/articles/7829.html">28个Unix/Linux的命令行神器</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></description>
										<content:encoded><![CDATA[<p><script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158"
     crossorigin="anonymous"></script>下面是<a href="http://kkovacs.eu/" target="_blank">Kristóf Kovács</a>收集的28个Unix/Linux下的28个命令行下的工具（<a href="http://kkovacs.eu/cool-but-obscure-unix-tools" target="_blank">原文链接</a>），有一些是大家熟悉的，有一些是非常有用的，有一些是不为人知的。这些工具都非常不错，希望每个人都知道。本篇文章还在<a href="http://news.ycombinator.com/item?id=2567186" target="_blank">Hacker News上被讨论</a>，你可以过去看看。我以作者的原文中加入了官网链接和一些说明。</p>
<div class="alpha grid_6">
<h4>dstat &amp; sar</h4>
<p>iostat, vmstat, ifstat 三合一的工具，用来查看系统性能（我在《<a title="性能调优攻略" href="https://coolshell.cn/articles/7490.html" target="_blank">性能调优攻略</a>》中提到过那三个xxstat工具）。</p>
<p>官方网站：<a href="http://dag.wieers.com/rpm/packages/dstat/" target="_blank">http://dag.wieers.com/rpm/packages/dstat/</a></p>
<p>你可以这样使用：</p>
<p><code data-enlighter-language="shell" class="EnlighterJSRAW">alias dstat=&#039;dstat -cdlmnpsy&#039;</code></p>
<div class="align_right">
<p><img decoding="async" loading="lazy" class="aligncenter" title="" src="https://coolshell.cn/wp-content/uploads/2012/07/dstat_screenshot.png" alt="dstat screenshot" width="400" height="326" /></p>
<h4 class="caption_text">slurm</h4>
</div>
<p>查看网络流量的一个工具</p>
<p>官方网站：<em>  <a href="https://computing.llnl.gov/linux/slurm/" target="_blank">Simple Linux Utility for Resource Management</a></em></p>
<p><span id="more-7829"></span></p>
<div class="align_right">
<p><img decoding="async" loading="lazy" class="aligncenter" title="" src="https://coolshell.cn/wp-content/uploads/2012/07/slurm_screenshot.png" alt="slurm screenshot" width="400" height="326" /></p>
<p class="caption_text">
</div>
<h4>vim &amp; emacs</h4>
<p>真正程序员的代码编辑器。</p>
<div class="align_right">
<p><img decoding="async" loading="lazy" class="aligncenter" title="" src="https://coolshell.cn/wp-content/uploads/2012/07/vim_screenshot.png" alt="vim screenshot" width="400" height="326" /></p>
<p class="caption_text">
</div>
<h4>screen, dtach, tmux, byobu</h4>
<p>你是不是经常需要 SSH 或者 telent 远程登录到 Linux 服务器？你是不是经常为一些长时间运行的任务而头疼，比如系统备份、ftp 传输等等。通常情况下我们都是为每一个这样的任务开一个远程终端窗口，因为他们执行的时间太长了。必须等待它执行完毕，在此期间可不能关掉窗口或者断开连接，否则这个任务就会被杀掉，一切半途而废了。</p>
<p><a href="http://www.gnu.org/software/screen/" target="_blank"><strong>Screen</strong></a>是一个可以在多个进程之间多路复用一个物理终端的窗口管理器。Screen中有会话的概念，用户可以在一个screen会话中创建多个screen窗口，在每一个screen窗口中就像操作一个真实的telnet/SSH连接窗口那样。请参看IBM DeveloperWorks的这篇文章《<a href="http://www.ibm.com/developerworks/cn/linux/l-cn-screen/" target="_blank">使用 screen 管理你的远程会话</a>》</p>
<p><img decoding="async" loading="lazy" class="aligncenter" title="" src="https://coolshell.cn/wp-content/uploads/2012/07/gnu_screen_screenshot.png" alt="gnu screen screenshot" width="400" height="326" /></p>
<p><a href="http://dtach.sourceforge.net/" target="_blank"><strong>dtach</strong> </a>是用来模拟screen的detach的功能的小工具，其可以让你随意地attach到各种会话上 。下图为dtach+dvtm的样子。</p>
<p><img decoding="async" loading="lazy" class="aligncenter size-full wp-image-7861" title="dtach+dvtm" src="https://coolshell.cn/wp-content/uploads/2012/07/dtach+dvtm.png" alt="" width="500" height="477" srcset="https://coolshell.cn/wp-content/uploads/2012/07/dtach+dvtm.png 500w, https://coolshell.cn/wp-content/uploads/2012/07/dtach+dvtm-300x286.png 300w" sizes="(max-width: 500px) 100vw, 500px" /></p>
<p><strong><a title="http://tmux.sourceforge.net/" href="http://tmux.sourceforge.net/" rel="nofollow">tmux</a></strong>是一个优秀的终端复用软件，类似<a title="http://www.gnu.org/software/screen/" href="http://www.gnu.org/software/screen/" rel="nofollow">GNU Screen</a>，但来自于OpenBSD，采用BSD授权。使用它最直观的好处就是，通过一个终端登录远程主机并运行tmux后，在其中可以开启多个控制台而无需再“浪费”多余的终端来连接这台远程主机；当然其功能远不止于此。与screen相比的优点：可以横向和纵向分割窗口，且窗格可以自由移动和调整大小。可在多个缓冲区进行复制和粘贴，支持跨窗口搜索；非正常断线后不需重新detach；……  有人说——<strong>与tmux相比，screen简直弱爆了</strong>。</p>
<p><img decoding="async" loading="lazy" class="aligncenter size-full wp-image-7860" title="tmux" src="https://coolshell.cn/wp-content/uploads/2012/07/tmux3.png" alt="" width="650" height="404" srcset="https://coolshell.cn/wp-content/uploads/2012/07/tmux3.png 650w, https://coolshell.cn/wp-content/uploads/2012/07/tmux3-300x186.png 300w" sizes="(max-width: 650px) 100vw, 650px" /></p>
<div class="align_right">
<p><a href="https://launchpad.net/byobu/" target="_blank"><strong>byobu</strong></a>是Ubuntu开发的，在Screen的基础上进行包装，使其更加易用的一个工具。最新的Byobu，已经是基于Tmux作为后端了。可通过“byobu-tmux”这个命令行前端来接受各种与tmux一模一样的参数来控制它。Byobu的细节做的非常好，效果图如下：<img decoding="async" loading="lazy" class="aligncenter size-full wp-image-7864" title="byobu-tmux" src="https://coolshell.cn/wp-content/uploads/2012/07/byobu-tmux.jpg" alt="" width="650" height="406" srcset="https://coolshell.cn/wp-content/uploads/2012/07/byobu-tmux.jpg 650w, https://coolshell.cn/wp-content/uploads/2012/07/byobu-tmux-300x187.jpg 300w" sizes="(max-width: 650px) 100vw, 650px" /></p>
<p class="caption_text">
</div>
<h4>multitail</h4>
<p>MultiTail是个用来实现同时监控多个文档、类似tail命令的功能的软件。他和tail的区别就是他会在控制台中打开多个窗口，这样使同时监控多个日志文档成为可能。他还可以看log文件的统计，合并log文件，过滤log文件，分屏，……。</p>
<p>官网：<a href="http://www.vanheusden.com/multitail/">http://www.vanheusden.com/multitail/</a></p>
<div class="align_right">
<p><img decoding="async" loading="lazy" class="aligncenter" title="" src="https://coolshell.cn/wp-content/uploads/2012/07/multitail_screenshot.png" alt="multitail screenshot" width="400" height="326" /></p>
<p class="caption_text">
</div>
<h4>tpp</h4>
<p>终端下的PPT，要是在某某大会上用这个演示PPT，就太TMD的Geek了。</p>
<p>官网：<a href="http://www.ngolde.de/tpp.html">http://www.ngolde.de/tpp.html</a></p>
<div class="align_right">
<p><img decoding="async" loading="lazy" class="aligncenter" title="" src="https://coolshell.cn/wp-content/uploads/2012/07/tpp_screenshot.png" alt="tpp screenshot" width="400" height="326" /></p>
<p class="caption_text">
</div>
<h4>xargs &amp; parallel</h4>
<p>Executes tasks from input (even multithread).</p>
<p>xargs 是一个比较古老的命令，有简单的并行功能，这个不说了。<span>对于</span><a href="http://www.gnu.org/software/parallel/"><span>GNU parallel</span></a><span> ( </span><a href="http://savannah.gnu.org/projects/parallel"><span>online manpage</span></a><span><span> )来说，它不仅能够处理本机上多执行绪，还能分散至远端电脑协助处理。</span><span>而使用GNU parallel前，要先确定本机有安装GNU parallel / ssh / rsync，远端电脑也要安装ssh。</span></span></p>
<div class="align_right">
<p><img decoding="async" loading="lazy" class="aligncenter" title="" src="https://coolshell.cn/wp-content/uploads/2012/07/xargs_screenshot.png" alt="xargs screenshot" width="400" height="326" /></p>
<p class="caption_text">
</div>
<h4>duplicity &amp; rsyncrypto</h4>
<p><a href="http://duplicity.nongnu.org/" target="_blank">Duplicity</a>是使用rsync算法加密的高效率备份软件，Duplicity支持目录加密生产和格式上传到远程或本地文件服务器。</p>
<p><a href="http://rsyncrypto.lingnu.com/index.php/Home_Page" target="_blank">rsyncrypto</a> 就是 rsync + encryption。对于rsync的算法可参看酷壳的<a title="rsync 的核心算法" href="https://coolshell.cn/articles/7425.html" target="_blank">rsync核心算法</a>。</p>
<p>Encrypting backup tools.</p>
<div class="align_right">
<p><img decoding="async" loading="lazy" class="aligncenter" title="" src="https://coolshell.cn/wp-content/uploads/2012/07/duplicity_screenshot.png" alt="duplicity screenshot" width="400" height="326" /></p>
<p class="caption_text">
</div>
<h4>nethack &amp; slash&#8217;em</h4>
<p><a href="http://www.nethack.org/" target="_blank">NetHack</a>（<a href="http://zh.wikipedia.org/zh/NetHack" target="_blank">Wiki</a>），20年历史的古老电脑游戏。没有声音，没有漂亮的界面，不过这个游戏真的很有意思。网上有个家伙说：<strong>如果你一生只做一件事情，那么玩NetHack</strong>。这句话很惹眼，但也让人觉得这个游戏很复杂不容易上手。其实，这个游戏很虽然很复杂，却容易上手。虽然玩通关很难，但上手很容易。NetHack上有许多复杂的规则，&#8221;the DevTeam thinks of everything&#8221;（开发团队想到了所有的事情)。各种各样的怪物，各种各样的武器&#8230;.，有许多spoilers文件来说明其规则。除了每次开始随机生成的地图，每次玩游戏，你也都会碰到奇怪的事情: 因为喝了一种药水，变成了机器人;因为踢坏了商店的门被要求高价赔偿;你的狗为你偷来了商店的东西&#8230;.. 这有点象人生，你不能完全了解这个世界，但你仍然可以选择自己的面对方式。</p>
<p>网上有许多文章所这是最好的电脑游戏或最好的电脑游戏之一。也许是因为它开放的源代码让人赞赏，古老的历史让人宽容，复杂的规则让人敬畏。虽然它不是当前流行的游戏，但它比任何一个当前流行的游戏都更有可能再经受20年的考验。</p>
<p><a href="http://www.slashem.org" target="_blank">Slash&#8217;EM</a> 也是一个基于NetHack的经典游戏。</p>
<div class="align_right">
<p><img decoding="async" loading="lazy" class="aligncenter" title="" src="https://coolshell.cn/wp-content/uploads/2012/07/nethack_screenshot.png" alt="nethack screenshot" width="400" height="326" /></p>
<p class="caption_text">
</div>
<h4>lftp</h4>
<p>利用<a href="http://lftp.yar.ru/" target="_blank">lftp</a>命令行ftp工具进行网站数据的增量备份，镜像，就像使用rsync一样。</p>
<div class="align_right">
<p><img decoding="async" loading="lazy" class="aligncenter" title="" src="https://coolshell.cn/wp-content/uploads/2012/07/lftp_screenshot.png" alt="lftp screenshot" width="400" height="326" /></p>
<p class="caption_text">
</div>
<h4>ack</h4>
<p><a href="http://betterthangrep.com/"><span>ack</span></a><span>是一个perl脚本，是grep的一个可选替换品。其可以对匹配字符有高亮显示。是为程序员专门设计的，默认递归搜索，省提供多种文件类型供选。</span></p>
<div class="align_right">
<p><img decoding="async" loading="lazy" class="aligncenter" title="" src="https://coolshell.cn/wp-content/uploads/2012/07/ack_screenshot.png" alt="ack screenshot" width="400" height="326" /></p>
<p class="caption_text">
</div>
<h4>calcurse &amp; remind + wyrd</h4>
<p><a href="http://calcurse.org/" target="_blank">calcurse</a>是一个命令行下的日历和日程软件。<a href="http://www.roaringpenguin.com/products/remind" target="_blank">remind</a> + <a href="http://pessimization.com/software/wyrd/" target="_blank">wyrd</a>也很类似。关于日历，我不得不提一个<a title="Linux的cycle日历（你懂的）" href="https://coolshell.cn/articles/3489.html" target="_blank">Linux的Cycle日历</a>，也是一个神器，呵呵。</p>
<div class="align_right">
<p><img decoding="async" loading="lazy" class="aligncenter" title="" src="https://coolshell.cn/wp-content/uploads/2012/07/calcurse_screenshot.png" alt="calcurse screenshot" width="400" height="326" /></p>
<p class="caption_text">
</div>
<h4>newsbeuter &amp; rsstail</h4>
<p><a href="http://newsbeuter.org/" target="_blank">newsbeuter </a>和 <a href="http://www.vanheusden.com/rsstail/" target="_blank">rsstail</a> 是命令行下RSS的阅读工具。</p>
<div class="align_right">
<p><img decoding="async" loading="lazy" class="aligncenter" title="" src="https://coolshell.cn/wp-content/uploads/2012/07/newsbeuter_screenshot.png" alt="newsbeuter screenshot" width="400" height="326" /></p>
<p class="caption_text">
</div>
<h4>powertop</h4>
<p><a title="做个环保主义的程序员" href="https://coolshell.cn/articles/7186.html" target="_blank">做个环保的程序员</a>，看看自己的电脑里哪些程序费电。<a href="https://01.org/powertop/" target="_blank">PowerTOP</a> 是一个让 Intel 平台的笔记本电脑节省电源的 Linux 工具。此工具由 Intel 公司发布。它可以帮助用户找出那些耗电量大的程序，通过修复或者关闭那些应用程序或进程，从而为用户节省电源。</p>
<div class="align_right">
<p><img decoding="async" loading="lazy" class="aligncenter" title="" src="https://coolshell.cn/wp-content/uploads/2012/07/powertop_screenshot.png" alt="powertop screenshot" width="400" height="326" /></p>
<p class="caption_text">
</div>
</div>
<div class="omega grid_6">
<h4>htop &amp; iotop</h4>
<p><a href="http://htop.sourceforge.net/" target="_blank">htop</a> 和 <a href="http://guichaz.free.fr/iotop/" target="_blank">iotop</a>  用来查看进程，内存和IO负载。</p>
<div class="align_right">
<p><img decoding="async" loading="lazy" class="aligncenter" title="" src="https://coolshell.cn/wp-content/uploads/2012/07/htop_screenshot.png" alt="htop screenshot" width="400" height="326" /></p>
</div>
<h4>ttyrec &amp; ipbt</h4>
<p><a href="http://0xcc.net/ttyrec/index.html.en" target="_blank">ttyrec</a> 是一个 tty 控制台录制程序，其所录制的数据文件可以使用与之配套的 ttyplay 播放。不管是你在 tty 中的各种操作，还是在 tty 中耳熟能详的软件，都可进行录制。</p>
<p><a href="http://www.chiark.greenend.org.uk/~sgtatham/ipbt/" target="_blank">ipbt</a> 是一个用来回放 ttyrec 所录制的控制台输入过程的工具。</p>
<p>与此类似的还有<a href="http://shelr.tv/" target="_blank">Shelr</a> 和 <a href="http://sourceforge.net/projects/termrec/" target="_blank">termrec </a></p>
<div class="align_right">
<p><img decoding="async" loading="lazy" class="aligncenter" title="" src="https://coolshell.cn/wp-content/uploads/2012/07/ipbt_screenshot.png" alt="ipbt screenshot" width="400" height="326" /></p>
<p class="caption_text">
</div>
<h4>rsync</h4>
<p>通过SSH进行文件同步的经典工具（<a title="rsync 的核心算法" href="https://coolshell.cn/articles/7425.html" target="_blank">核心算法</a>）</p>
<div class="align_right">
<p><img decoding="async" loading="lazy" class="aligncenter" title="" src="https://coolshell.cn/wp-content/uploads/2012/07/rsync_screenshot.png" alt="rsync screenshot" width="400" height="326" /></p>
<p class="caption_text">
</div>
<h4>mtr</h4>
<p><a href="http://www.bitwizard.nl/mtr/" target="_blank">MTR</a> &#8211; traceroute 2.0，其是把 traceroute 和 ping 集成在一块的一个小工具 用于诊断网络。</p>
<div class="align_right">
<p><img decoding="async" loading="lazy" class="aligncenter" title="" src="https://coolshell.cn/wp-content/uploads/2012/07/mtr_screenshot.png" alt="mtr screenshot" width="400" height="326" /></p>
<p class="caption_text">
</div>
<h4>socat &amp; netpipes</h4>
<p><a href="http://www.dest-unreach.org/socat/" target="_blank">socat</a>是一个多功能的网络工具，名字来由是” Socket CAT”，可以看作是netcat的N倍加强版。</p>
<p><a href="http://web.purplefrog.com/~thoth/netpipes/" target="_blank">netpipes</a> 和socat一样，主要是用来在命令行来进行socket操作的命令，这样你就可以在Shell脚本下行进socket网络通讯了。</p>
<div class="align_right">
<p><img decoding="async" loading="lazy" class="aligncenter" title="" src="https://coolshell.cn/wp-content/uploads/2012/07/socat_screenshot.png" alt="socat screenshot" width="400" height="326" /></p>
<p class="caption_text">
</div>
<h4>iftop &amp; iptraf</h4>
<p><a href="http://www.ex-parrot.com/~pdw/iftop/" target="_blank">iftop</a>和<a href="http://iptraf.seul.org/" target="_blank">iptraf</a>可以用来查看当前网络链接的一些流量情况。</p>
<div class="align_right">
<p><img decoding="async" loading="lazy" class="aligncenter" title="" src="https://coolshell.cn/wp-content/uploads/2012/07/iftop_screenshot.png" alt="iftop screenshot" width="400" height="326" /></p>
<p><img decoding="async" loading="lazy" class="aligncenter size-full wp-image-7867" title="iptraf-tcpudp" src="https://coolshell.cn/wp-content/uploads/2012/07/iptraf-tcpudp.gif" alt="" width="562" height="354" srcset="https://coolshell.cn/wp-content/uploads/2012/07/iptraf-tcpudp.gif 562w, https://coolshell.cn/wp-content/uploads/2012/07/iptraf-tcpudp-300x188.gif 300w" sizes="(max-width: 562px) 100vw, 562px" /></p>
<p class="caption_text">
</div>
<h4>siege &amp; tsung</h4>
<p><a href="http://www.joedog.org/siege-home/" target="_blank">Siege</a>是一个压力测试和评测工具，设计用于WEB开发这评估应用在压力下的承受能力：可以根据配置对一个WEB站点进行多用户的并发访问，记录每个用户所有请求过程的相应时间，并在一定数量的并发访问下重复进行。</p>
<p><a href="http://tsung.erlang-projects.org/" target="_blank">Tsung</a> 是一个压力测试工具，可以测试包括HTTP, WebDAV, PostgreSQL, MySQL, LDAP, and XMPP/Jabber等服务器。针对 HTTP 测试，Tsung 支持 HTTP 1.0/1.1 ，包含一个代理模式的会话记录、支持 GET、POST 和 PUT 以及 DELETE 方法，支持 Cookie 和基本的 WWW 认证，同时还支持 SSL。</p>
<p>参看：<a title="十个免费的Web压力测试工具" href="https://coolshell.cn/articles/2589.html" target="_blank">十个免费的Web压力测试工具</a></p>
<div class="align_right">
<p><img decoding="async" loading="lazy" class="aligncenter" title="" src="https://coolshell.cn/wp-content/uploads/2012/07/siege_screenshot.png" alt="siege screenshot" width="400" height="326" /></p>
<p class="caption_text">
</div>
<h4>ledger</h4>
<p><a href="http://ledger-cli.org/" target="_blank">ledger</a> 一个命令行下记帐的小工具。</p>
<div class="align_right">
<p><img decoding="async" loading="lazy" class="aligncenter" title="" src="https://coolshell.cn/wp-content/uploads/2012/07/ledger_screenshot.png" alt="ledger screenshot" width="400" height="326" /></p>
<p class="caption_text">
</div>
<h4>taskwarrior</h4>
<p><a href="http://taskwarrior.org/projects/show/taskwarrior" target="_blank">TaskWarrior</a> 是一个基于命令行的 TODO 列表管理工具。主要功能包括：标签、彩色表格输出、报表和图形、大量的命令、底层API、多用户文件锁等功能。</p>
<div class="align_right">
<p><img decoding="async" loading="lazy" class="aligncenter" title="" src="https://coolshell.cn/wp-content/uploads/2012/07/taskwarrior_screenshot.png" alt="taskwarrior screenshot" width="400" height="326" /></p>
<p>下图是TaskWarrior 2.0的界面：</p>
<p><img decoding="async" loading="lazy" class="aligncenter size-full wp-image-7868" title="TaskWarrior2.0" src="https://coolshell.cn/wp-content/uploads/2012/07/TaskWarrior2.0.png" alt="" width="395" height="480" srcset="https://coolshell.cn/wp-content/uploads/2012/07/TaskWarrior2.0.png 395w, https://coolshell.cn/wp-content/uploads/2012/07/TaskWarrior2.0-246x300.png 246w" sizes="(max-width: 395px) 100vw, 395px" /></p>
<p class="caption_text">
</div>
<h4>curl</h4>
<p><a href="http://curl.haxx.se/" target="_blank">cURL</a>是一个利用URL语法在命令行下工作的文件传输工具，1997年首次发行。它支持文件上传和下载，所以是综合传输工具，但按传统，习惯称cURL为下载工具。cURL还包含了用于程序开发的libcurl。cURL支援的通訊協定有FTP、FTPS、HTTP、HTTPS、TFTP、SFTP、Gopher、SCP、Telnet、DICT、FILE、LDAP、LDAPS、IMAP、POP3、SMTP和RTSP。</p>
<div class="align_right">
<p><img decoding="async" loading="lazy" class="aligncenter" title="" src="https://coolshell.cn/wp-content/uploads/2012/07/curl_screenshot.png" alt="curl screenshot" width="400" height="326" /></p>
<p class="caption_text">
</div>
<h4>rtorrent &amp; aria2</h4>
<p><a href="http://libtorrent.rakshasa.no/" target="_blank">rTorrent</a> 是一个非常简洁、优秀、非常轻量的BT客户端. 它使用了 ncurses 库以 C++ 编写, 因此它完全基于文本并在终端中运行. 将 rTorrent 用在安装有 GNU Screen 和 Secure Shell 的低端系统上作为远程的 BT 客户端是非常理想的。</p>
<p><a href="http://aria2.sourceforge.net/">aria2</a> 是 Linux 下一个不错的高速下载工具。由于它具有分段下载引擎，所以支持从多个地址或者从一个地址的多个连接来下载同一个文件。这样自然就大大加快了文件的下载速度。aria2 也具有断点续传功能，这使你随时能够恢复已经中断的文件下载。除了支持一般的 http(s) 和 ftp 协议外，aria2 还支持 BitTorrent 协议。这意味着，你也可以使用 aria2 来下载 torrent 文件。</p>
<div class="align_right">
<p class="caption_text"> <img decoding="async" loading="lazy" class="aligncenter" title="" src="https://coolshell.cn/wp-content/uploads/2012/07/rtorrent_screenshot.png" alt="rtorrent screenshot" width="400" height="326" /></p>
</div>
<h4>ttytter &amp; earthquake</h4>
<p><a href="http://www.floodgap.com/software/ttytter" target="_blank">TTYtter</a> 是一个Perl写的命令行上发Twitter的工具，可以进行所有其他平台客户端能进行的事情，当然，支持中文。脚本控、CLI控、终端控、Perl控的最愛。</p>
<p><a href="https://github.com/jugyo/earthquake" target="_blank">Earthquake</a>也是一个命令行上的Twitter客户端。</p>
<div class="align_right">
<p><img decoding="async" loading="lazy" class="aligncenter" title="" src="https://coolshell.cn/wp-content/uploads/2012/07/ttytter_screenshot.png" alt="ttytter screenshot" width="400" height="326" /></p>
<p><img decoding="async" loading="lazy" class="aligncenter size-full wp-image-7870" title="earthquake" src="https://coolshell.cn/wp-content/uploads/2012/07/earthquake.jpg" alt="" width="550" height="314" srcset="https://coolshell.cn/wp-content/uploads/2012/07/earthquake.jpg 550w, https://coolshell.cn/wp-content/uploads/2012/07/earthquake-300x171.jpg 300w" sizes="(max-width: 550px) 100vw, 550px" /></p>
<p class="caption_text">
</div>
<h4>vifm &amp; ranger</h4>
<p><a href="http://vifm.sourceforge.net/" target="_blank">Vifm</a> 基于ncurses的文件管理器，DOS风格，用键盘操作。</p>
<p><img decoding="async" loading="lazy" class="aligncenter" title="" src="https://coolshell.cn/wp-content/uploads/2012/07/vifm_screenshot.png" alt="vifm screenshot" width="400" height="326" /></p>
<p><a href="http://savannah.nongnu.org/projects/ranger" target="_blank">Ranger</a>用 Python 完成，默认为使用 Vim 风格的按键绑定，比如 hjkl（上下左右），dd（剪切），yy（复制）等等。功能很全，扩展/可配置性也非常不错。类似MacOS X下Finder（文件管理器）的多列文件管理方式。支持多标签页。实时预览文本文件和目录。</p>
<div class="align_right">
<p class="caption_text"><img decoding="async" loading="lazy" class="aligncenter size-full wp-image-7871" title="ranger" src="https://coolshell.cn/wp-content/uploads/2012/07/ranger.png" alt="" width="668" height="340" srcset="https://coolshell.cn/wp-content/uploads/2012/07/ranger.png 668w, https://coolshell.cn/wp-content/uploads/2012/07/ranger-300x152.png 300w" sizes="(max-width: 668px) 100vw, 668px" /></p>
</div>
<h4>cowsay &amp; sl</h4>
<p><a href="http://www.nog.net/~tony/warez/cowsay.shtml" target="_blank">cowsay </a> 不说了，如下所示，哈哈哈。还有xcowsay，你可以自己搜一搜。</p>
<div class="align_right">
<p><img decoding="async" loading="lazy" class="aligncenter" title="" src="https://coolshell.cn/wp-content/uploads/2012/07/cowsay_screenshot.png" alt="cowsay screenshot" width="400" height="326" /></p>
<p class="caption_text"> sl是什么？ls？，呵呵，你会经常把ls 打成sl吗？如果是的话，这个东西可以让你娱乐一下，你会看到一辆火车呼啸而过~~，相当拉风。你可以使用sudo apt-get install sl 安装。</p>
<p class="caption_text"><img decoding="async" loading="lazy" class="aligncenter size-full wp-image-7872" title="sl" src="https://coolshell.cn/wp-content/uploads/2012/07/sl.jpg" alt="" width="500" height="254" srcset="https://coolshell.cn/wp-content/uploads/2012/07/sl.jpg 500w, https://coolshell.cn/wp-content/uploads/2012/07/sl-300x152.jpg 300w" sizes="(max-width: 500px) 100vw, 500px" /></p>
<p class="caption_text">最后，再介绍一个命令中linuxlogo，你可以使用 sudo apt-get install linuxlogo来安装，然后，就可以使用linuxlogo -L<br />
来看一下各种Linux的logo了</p>
<p class="caption_text"><img decoding="async" loading="lazy" class="aligncenter" title="linuxlogo" src="https://coolshell.cn/wp-content/uploads/2012/07/linuxlogo.jpg" alt="" width="450" height="371" /></p>
<p class="caption_text">（全文完）</p>
</div>
</div>
<p><!--



<p align="center"><a href= target=_blank><img decoding="async" src=""></a></p>





<p align="center"><img decoding="async" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg"> <img decoding="async" loading="lazy" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg" width="300" height="300"> <br />关注CoolShell微信公众账号和微信小程序</p>

 

--></p>
<div style="margin-top: 15px; font-size: 16px;color: #cc0000;">
<p align="center"><strong>（转载本站文章请注明作者和出处 <a href="https://coolshell.cn/">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>
</div>

<div class="wp_rp_wrap  wp_rp_vertical_m" ><div class="wp_rp_content"><h3 class="related_post_title">相关文章</h3><ul class="related_post wp_rp"><li ><a href="https://coolshell.cn/articles/8883.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2013/01/linux-bash-300x225-150x150.jpg" alt="应该知道的Linux技巧" width="150" height="150" /></a><a href="https://coolshell.cn/articles/8883.html" class="wp_rp_title">应该知道的Linux技巧</a></li><li ><a href="https://coolshell.cn/articles/18360.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg" alt="程序员练级攻略（2018)  与我的专栏" width="150" height="150" /></a><a href="https://coolshell.cn/articles/18360.html" class="wp_rp_title">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href="https://coolshell.cn/articles/17998.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2017/07/systemd-1-150x150.jpeg" alt="Linux PID 1 和 Systemd" width="150" height="150" /></a><a href="https://coolshell.cn/articles/17998.html" class="wp_rp_title">Linux PID 1 和 Systemd</a></li><li ><a href="https://coolshell.cn/articles/12103.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2014/11/tux-fork-150x150.gif" alt="vfork 挂掉的一个问题" width="150" height="150" /></a><a href="https://coolshell.cn/articles/12103.html" class="wp_rp_title">vfork 挂掉的一个问题</a></li><li ><a href="https://coolshell.cn/articles/11847.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2014/08/puzzle-150x150.png" alt="谜题的答案和活动的心得体会" width="150" height="150" /></a><a href="https://coolshell.cn/articles/11847.html" class="wp_rp_title">谜题的答案和活动的心得体会</a></li><li ><a href="https://coolshell.cn/articles/9104.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2013/02/sed-superman-150x150.png" alt="sed 简明教程" width="150" height="150" /></a><a href="https://coolshell.cn/articles/9104.html" class="wp_rp_title">sed 简明教程</a></li></ul></div></div>The post <a href="https://coolshell.cn/articles/7829.html">28个Unix/Linux的命令行神器</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></content:encoded>
					
					<wfw:commentRss>https://coolshell.cn/articles/7829.html/feed</wfw:commentRss>
			<slash:comments>120</slash:comments>
		
		
			</item>
		<item>
		<title>少即是极多</title>
		<link>https://coolshell.cn/articles/7771.html</link>
					<comments>https://coolshell.cn/articles/7771.html#comments</comments>
		
		<dc:creator><![CDATA[Tim Shen]]></dc:creator>
		<pubDate>Thu, 05 Jul 2012 00:12:25 +0000</pubDate>
				<category><![CDATA[编程语言]]></category>
		<category><![CDATA[C++]]></category>
		<category><![CDATA[Go]]></category>
		<category><![CDATA[Unix]]></category>
		<guid isPermaLink="false">http://coolshell.cn/?p=7771</guid>

					<description><![CDATA[<p>【感谢网友 @innocentim (Twitter) 投稿】 这是一篇翻译练习。力图保留原意。若有不准确处，求速速指出。猛击此处（墙）看原文。作者为Rob P...</p>
<p class="read-more"><a class="btn btn-default" href="https://coolshell.cn/articles/7771.html"> Read More<span class="screen-reader-text">  Read More</span></a></p>
The post <a href="https://coolshell.cn/articles/7771.html">少即是极多</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></description>
										<content:encoded><![CDATA[<p><script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158"
     crossorigin="anonymous"></script>【<span style="color: #cc0000;"><strong>感谢网友 <a href="https://twitter.com/#!/innocentim" target="_blank">@innocentim</a></strong> (Twitter)<strong> 投稿</strong></span>】</p>
<p>这是一篇翻译练习。力图保留原意。若有不准确处，求速速指出。<a href="http://commandcenter.blogspot.com/2012/06/less-is-exponentially-more.html" target="_blank">猛击此处</a>（墙）看原文。作者为Rob Pike，贝尔实验室来的大牛，现在就职于Google。他主导了Go语言的创建工作。下面是正文——</p>
<p style="text-align: center; font-size: 9pt;"><span style="color: #999999;">——————————————正文分隔线——————————————</span></p>
<p><img decoding="async" loading="lazy" class="alignright size-full wp-image-7818" title="Less is More" src="https://coolshell.cn/wp-content/uploads/2012/06/Less-is-More-Box-ShopTab-300x282.jpg" alt="" width="300" height="282" srcset="https://coolshell.cn/wp-content/uploads/2012/06/Less-is-More-Box-ShopTab-300x282.jpg 300w, https://coolshell.cn/wp-content/uploads/2012/06/Less-is-More-Box-ShopTab-300x282-287x270.jpg 287w" sizes="(max-width: 300px) 100vw, 300px" />这是我在2012年6月的Go SF上演讲的文本。</p>
<p>这是一个个人演讲。 我承认，虽然面前的团队让Go诞生并延续，但是我的观点并不代表任何其他Go语言小组成员的意见。 我也想感谢Go SF的组织者提供这个和你们交流的机会。</p>
<p>几星期前我被问起:“你在推出Go的过程中遇到的最大的惊奇是什么？&#8221;我立即意识到了答案: 虽然我们希望C++程序员意识到Go是个较好的选择，但是令人意外的是，大多数Go程序员来自Python和Ruby这样的动态语言，而很少有来自C++的。</p>
<p>我们——Ken，Robert和我——是C++程序员(译者: Ken也用C++？)，当时在为解决我们所写的这类软件产生的问题设计一个新的语言。 这似乎有点自相矛盾，因为别的C++程序员根本不关心这些问题，更不会去设计一个语言。</p>
<p>我今天想说的是关于那些激发我们创造Go的事情，和为什么它本不应令我们如此惊讶。 我保证这些内容更多与Go相关而不是C++，所以即使你不很了解C++你也能跟得上。</p>
<p>回答可以这样归结: 你认为&#8221;少即是多&#8221;呢，还是&#8221;少就是少&#8221;？</p>
<p>这里有个比喻，将以真实故事的形式给出。 贝尔实验室中心原来发放3位数号码: 物理研究是111，计算科学研究是127，如此这般。 1980年代早期，一个便笺飞过来说&#8221;鉴于你们对研究的理解有所加深，将为你们的号码多加上一位，以便更好地体现你们的工作&#8221;。 所以我们中心的号码变成了1127。 Ron Hardin半当真地开玩笑说如果我们真的理解我们的世界更好一点的话，我们将丢掉一位数字，将127变成27。 当然主管没听到这个笑话(这也不是我们希望的)，但是我想这里面有点值得思考的东西。 少即是多。 你理解得越好，你将变得越简洁。</p>
<p><span id="more-7771"></span></p>
<p>先记住这句话。</p>
<p>回到2007年9月，我在做一个庞大的Google C++项目的细微但核心的部分。 开发必须交互进行，但是我这部分在我们的Google编译集群上要编译45分钟。 同时，有个消息传过来说一群在C++社区的Google员工将开一场讲座，介绍即将到来的C++0x(现在称为C++11)。</p>
<p>在那场持续一小时的讲座中，我们听说了诸如计划中的35个新特性的说法——事实上还有更多，但是那场讲座只说有35个。 有些特性当然是细微的，但是讲座中谈到的至少是足够重要的。 提到的特性中，有些十分微妙并难以理解，比如右值引用(rvalue references); 有些特别符合C++范儿，比如可变参数模板(variadic templates); 还有些十分疯狂，比如用户定义的字面量(user-defined literals)。</p>
<p>那时候我问了自己一个问题: C++社区真的觉得C++错在没有足够多的特性么？ 显然，从Ron Hardin的笑话的角度看，简化语言将比添加新特性取得更好的效果。 当然，对C++来说这很不靠谱，但是先记住这点。</p>
<p>在这场讲座的几个月之前我做了一场讲座(你可以通过<a href="http://video.google.com/videoplay？docid=810232012617965344" target="_blank">YouTube</a>看到)，讲的是一个我1980年代做的一个玩具并发编程语言。 这个语言叫<a href="ftp://cs.bell-labs.com/cm/cs/who/rsc/thread/newsqueak.pdf" target="_blank">Newsqueak</a>，而且显然地，它成为了Go的前身。</p>
<p>在我在Google工作的过程中，我发现我丢掉了Newsqueak中的一些点子。 现在我将重新思考它们，所以我才做了那场讲座。 我相信它们会让服务器端编程变得更容易，而且Google能真正从中获益。</p>
<p>我真的尝试将这些点子加入到C++中，可惜失败了。 我实在难以将一组并发操作融入到C++的控制流程中去——当真融进去的话，它们将变得十分丑陋，从而难以看到优越性。 另外，C++将它变得十分臃肿(虽然我从来没真正发现C++苗条过)。 所以我放弃了这个想法。</p>
<p>但是C++0x的讲座使我再次思考。 一件事十分困扰我——我相信也困扰着Ken和Robert——C++的新内存模型居然新增了原子类型。 为这个不堪重负的类型系统加上这么个细致精巧到极致类型机制十分的不靠谱，不是么？ 将语言和今日的硬件绑在一起似乎有点目光短浅并且不明智，因为硬件过几年就有大变。</p>
<p>那场C++0x讲座结束之后，我们回到办公室。 我开始了另一个编译(译者笑)，转过转过我的椅子，面对Robert，然后开始问一些尖锐的问题。 在编译完成之前，我们拉拢了Ken，并决定做些什么。 我们再也不想写C++了，并且我们——尤其是我——在写Google代码时，想让并发拿来就用。 同时我们也想解决&#8221;大系统编程&#8221;的问题，容后细说。</p>
<p>我们在白板上写下一组我们需要的东西——迫切需要的那种。 我们规划出大体的轮廓，忽略了语法细节和语义。</p>
<p>我仍然有一条碉堡了的那周的邮件线索。 这是一些摘录:</p>
<blockquote><p><strong>Robert</strong>: 起点: C，修补一些显而易见的瑕疵，去除繁杂的东西。 新增一些特性。</p>
<p><strong>Rob</strong>: 命名为&#8217;go&#8217;。 你可以为这个名字编造各种理由，但是它确实拥有很多好的特性。 它短小，易于打出。 工具么: goc，gol，goa。 如果有个交互式调试器/解释器，可以直接叫&#8217;go&#8217;。 代码后缀是。go。</p>
<p><strong>Robert</strong>: 空接口: interface {}。 将被所有接口实现(译者: 原文如此)，并且可以取代void*。</p></blockquote>
<p>我们并没有立即全部设计出来。 比如我们花了一年多才设计出了数组(array)和切片(slice)。 不过相当一部分重要的设计在最初的几天中浮现。</p>
<p>注意到Robert说C是起点，并非C++。 对于这点我不是很确定，不过我相信他说的是C，因为Ken在场(译者笑)。 但是最后我们并没有从C开始，这倒是真的。 我们从最初的草稿开始，仅仅从其它语言中借鉴琐碎的东西，比如运算符，各种括号和一些常见的关键字。(当然我们也借鉴了我们所知道的语言中的思想。)不管怎么说，我们破而后立，从头做起，以此来响应C++。 我们并非想做一个更好的C++，甚至不是一个更好的C。 它仅仅是一个对我们所关心的软件来说更好的语言。</p>
<p>最后，我们得到了既不同于C也不同于C++的东西，甚至比许多人意识到的还要不同。 我列了一个对于C和C++的Go的重要的简化的列表:</p>
<ul>
<li>常规的语法(不需要一个符号表来辅助解析)</li>
<li>GC机制(仅仅是GC)</li>
<li>没有头文件</li>
<li>显式依赖关系</li>
<li>没有循环依赖</li>
<li>数字常量仅仅是数字(译者: 没有类型)</li>
<li>int和int32不是同种类型</li>
<li>字母大小写将确定可见性</li>
<li>任何类型都可以有方法(没有类)</li>
<li>没有子类型继承(没有子类)</li>
<li>包级别的初始化和良好定义的初始化顺序</li>
<li>同一个包的文件一起编译</li>
<li>包级别的全局定义可以以任意顺序进行</li>
<li>没有算术类型转换(常量可以弥补)</li>
<li>接口是隐式实现的(没有&#8221;implements&#8221;声明)</li>
<li>嵌入的结构体(没有类型提升和子类)</li>
<li>方法像函数一样定义(不必定义在特殊的地方)</li>
<li>方法就是函数</li>
<li>接口就是方法(没有数据)</li>
<li>方法仅仅靠名字匹配(不是靠类型)</li>
<li>没有构造函数和析构函数</li>
<li>后置增量/减量运算符仅仅是语句，而不是表达式</li>
<li>没有前置增量/减量运算符</li>
<li>赋值号是语句，不是表达式</li>
<li>表达式求值顺序在赋值和函数调用时确定(没有所谓的&#8221;sequence point&#8221;)</li>
<li>没有指针算术</li>
<li>内存总是初始化为0</li>
<li>对本地变量取地址是合法的</li>
<li>方法中没有叫this的指针</li>
<li>分段式栈</li>
<li>没有常量或其它类型的注记</li>
<li>没有模板</li>
<li>没有异常</li>
<li>内建字符串，切片和映射(map)</li>
<li>数组边界检查</li>
</ul>
<p>并且，我相信通过这一系列的简化，Go将比C或C++更具有表现力。 少即是多。</p>
<p>但是我们没法一下子把所有部分都做出来。 我们需要构建最基础的部分，比如说类型系统的表示，能良好应用于实际的语法，和一些无法形容的但能让库更容易相互操作的东西。</p>
<p>我们同样增加了C或C++中没有的东西，比如切片和映射，组合字面量(？)，文件顶层的表达式(这虽是件大事，但是几乎不为人知)，反射机制，GC等等。 自然，还有并发。</p>
<p>一个显眼的缺少的东西是类型的继承。 请允许我粗暴地对待它一分钟。</p>
<p>早先构建Go的时候有人跟我说，他无法想象用一门没有泛型的语言工作。 正如我在别处说明的那样，我觉得这是个很诡异的言论。</p>
<p>公平起见，他用自己的话说可能是他真的很喜欢C++中STL的那些容器。 以辩论为目的的话，我们来正面看看他的言论。</p>
<p>他说的意味着: 他发现写一个容器，比如以int为元素类型的链表，或字符串映射是一种不能忍的重负。 我发现这是个很诡异的言论，因为我几乎没把时间花在那些个问题上，即使我在用没有泛型的语言。</p>
<p>但是，更重要的是，他说的那些表示<em>类型系统</em>将会解除这种负担。 <em>类型系统</em>。 不是多态函数，或语言级原语，或其它类型的辅助手段(helpers)，而仅仅是<em>类型系统</em>。</p>
<p>这就是粘住我的那个细节。</p>
<p>从C++或Java来Go的程序员怀念和类型系统在一起的日子，特别是带继承和子类的那部分。 也许我在类型系统方面是粗暴了些，但是我绝不觉得那套玩意非常具有表现力。</p>
<p>我已故的朋友Alain Fournier一次告诉我说他认为学术工作的最底层是分类学。 然后信不信由你，类型继承正是分类学。 你必须决定哪个萝卜扔哪个坑里，每个类型的父类型，A是否继承B或者B是否继承A。 一个可排序的数组是一个带有sort方法的数组呢，还是一个长得像数组的排序器呢？ 如果你觉得类型系统能解决所有设计上的问题，你必须做出这个无意义的选择。</p>
<p>我相信对编程来说那是个荒诞的思路。 真正的重点不在于事物之间的继承关系，而在于它们能提供些什么。</p>
<p>因此，接口这个概念进入了Go。 但是它们都是主要部分——真正的Go之道——的一部分。</p>
<p>如果C++和Java注重类型继承和类型系统的分类学，那末Go就注重组合。</p>
<p>Doug Mcilroy，Unix管道的最终发明人，在1964年(!)写道:</p>
<blockquote><p>我们应该有一些机制能将程序耦合(串)起来，像花园软管那样——当我们需要另一种方式传送数据时，拧紧另外一段即可。 I/O也可以这么做。</p></blockquote>
<p>这也是Go所提倡的道路。 Go吸收这个观点，然后把它推进得十分远。 这是一门关于(功能上的)组合和(调用上的)耦合的语言。</p>
<p>一个显然的例子是接口是组合各部分的途径。 关键是，那些部分是什么并不重要，如果某类型实现了M方法我就可以把这个方法填到接口里去。</p>
<p>另一个重要的例子是如何让并发性提供给我们不同的独立计算部分的组合。</p>
<p>并且还有一种不同寻常(但十分简单)的类型组合形式: 嵌入。</p>
<p>————————————————————————</p>
<p>我想提一个和之前不太相关的Go设计: Go被设计为大型团队用来写大型程序的语言。</p>
<p>这里有个概念是&#8221;大型编程&#8221;，并且不知何故C++和Java主宰了这个领域。 我相信这只是因为其历史巧合，或者是工业上的巧合。 但是被广泛接纳的观点是他们和面向对象设计有关。</p>
<p>我压根不相信这点。 大型软件需要确定的方法，但是更重要的是它需要强依赖性管理，干净的接口抽象和优越的文档工具。 C++没一点做得好的(虽然Java明显要好很多)。</p>
<p>我们还不知道Go语言能做到何种程度，因为现在还没有足够的软件是用Go写的。 但是我非常有信心于Go将会成为一个优越的大型编程语言。 时间会说明一切的。</p>
<p>————————————————————————</p>
<p>现在，回到我们演讲开始提的那个问题:</p>
<p>为什么Go，作为从头被设计为符合C++使用者习惯的语言，没有吸引很多C++程序员？</p>
<p>严肃点说，我觉得是因为Go和C++在哲学方面有着巨大的不同。</p>
<p>C++是将所有东西提到你指尖上(译者: 即多范式)。 我在C++11的FAQ上找到了这段引用:</p>
<blockquote><p>C++能优雅地，灵活地，零损耗地(相比于手工操纵代码)表达抽象的能力大幅提升了。</p></blockquote>
<p>Go并非这种&#8221;围绕式&#8221;的。 你并不需要所有的东西都内建好。 你不需要对每个执行细节进行精细的控制。 比如，你不需要RAII，但你拥有一个垃圾回收器，也意味着你不需要执行释放内存的操作。</p>
<p>你得到的是一组非常强有力但易于理解，易于用来构建积木的功能，这些积木可以用来组合出一个你需要的问题的解法。 这并不意味着它能像别的一些语言创造的解法一样快速，复杂，或带来思想上的激励，但是它总能保证易于书写，易于阅读，易于理解，易于维护，而且可能更安全。</p>
<p>从另一个角度说，这当然算作过度简化:</p>
<p>Python和Ruby程序员转到Go，因为他们不需要牺牲表达能力，却获得了性能的提升，并且能好好玩并发系统了。</p>
<p>C++程序员<em>并没有</em>转到Go是因为他们好不容易获得了对程序的精细控制，并且不想牺牲它们的任何一部分。 对他们而言，写软件不仅包括把事情做完，而且包括用特定的方式完成。</p>
<p>关键是，在将来，Go的成功将会颠覆他们的世界观。</p>
<p>并且从一开始我们就应该意识到这点。 对于C++11的新特性很兴奋的人们并不关心一个拥有如此少特性的语言。 即使最后他提供了如此多。</p>
<p>谢谢。</p>
<p>（全文完）<!--



<p align="center"><a href= target=_blank><img decoding="async" src=""></a></p>





<p align="center"><img decoding="async" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg"> <img decoding="async" loading="lazy" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg" width="300" height="300"> <br />关注CoolShell微信公众账号和微信小程序</p>

 

--></p>
<div style="margin-top: 15px; font-size: 16px;color: #cc0000;">
<p align="center"><strong>（转载本站文章请注明作者和出处 <a href="https://coolshell.cn/">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>
</div>

<div class="wp_rp_wrap  wp_rp_vertical_m" ><div class="wp_rp_content"><h3 class="related_post_title">相关文章</h3><ul class="related_post wp_rp"><li ><a href="https://coolshell.cn/articles/18360.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg" alt="程序员练级攻略（2018)  与我的专栏" width="150" height="150" /></a><a href="https://coolshell.cn/articles/18360.html" class="wp_rp_title">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href="https://coolshell.cn/articles/8088.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg" alt="对技术的态度" width="150" height="150" /></a><a href="https://coolshell.cn/articles/8088.html" class="wp_rp_title">对技术的态度</a></li><li ><a href="https://coolshell.cn/articles/7965.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2012/07/fork01jpg-150x150.jpg" alt="一个fork的面试题" width="150" height="150" /></a><a href="https://coolshell.cn/articles/7965.html" class="wp_rp_title">一个fork的面试题</a></li><li ><a href="https://coolshell.cn/articles/4990.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2011/07/programmer-150x150.png" alt="程序员技术练级攻略" width="150" height="150" /></a><a href="https://coolshell.cn/articles/4990.html" class="wp_rp_title">程序员技术练级攻略</a></li><li ><a href="https://coolshell.cn/articles/4102.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/25.jpg" alt="如何学好C语言" width="150" height="150" /></a><a href="https://coolshell.cn/articles/4102.html" class="wp_rp_title">如何学好C语言</a></li><li ><a href="https://coolshell.cn/articles/1761.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2009/11/spell_it_with_e-150x150.jpg" alt="Go语言源码的一个改动" width="150" height="150" /></a><a href="https://coolshell.cn/articles/1761.html" class="wp_rp_title">Go语言源码的一个改动</a></li></ul></div></div>The post <a href="https://coolshell.cn/articles/7771.html">少即是极多</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></content:encoded>
					
					<wfw:commentRss>https://coolshell.cn/articles/7771.html/feed</wfw:commentRss>
			<slash:comments>50</slash:comments>
		
		
			</item>
	</channel>
</rss>
