背景:关于Linux的流概念,比如在调试C代码时,出错了,或是想捕获输出的错误到同一个文件,这些都要用到流知识,输入,输出,错误分别是:0,1,2,于是有了 >2 >2&1,该文章都有详细解答讲解,写的非常好,写的非常好,难得的好干货,拨云见雾………我已经无力吐槽。
如:
[root@iZ25dcp92ckZ multepoolserver]# cat aaa.txt
cat: aaa.txt: 没有那个文件或目录
[root@iZ25dcp92ckZ multepoolserver]# cat aaa.txt > notexist.txt 2>&1
[root@iZ25dcp92ckZ multepoolserver]# cat notexist.txt
cat: aaa.txt: 没有那个文件或目录
————————————————————————————————————
./multepoolser > echoOut.txt
./tianxia > appleOut.txt 2>&1  

————————————————————————————————————————————————————————
内容简介

1、第三部分第二课:流、管道、重定向,三管齐下

2、第三部分第三课预告:监视系统活动,滴水不漏

流、管道、重定向,三管齐下

这一课我们来学一些非常有用的内容,而且相当有意思,而且内容很多,而且有可能颠覆你的三《观》(毕竟三管齐下,不颠覆三观也难)。

今天的标题中的三个名称,听上去就怪怪的。什么流,管道,重定向,都啥玩意啊。不过希望学完这课,大家能够有拨云见雾的感觉。

到目前为止,我们已经学习了不少Linux的命令了,也已经比较熟悉命令行的用法了。其最基本用法是这样的:

在终端输入命令(比如输入ls命令)。

命令的运行结果显示在终端中。

但是我们还不知道的是:其实我们可以重定向命令的运行结果。

重定向,是什么意思呢?简单来说,就是我们可以把本来要显示在终端的命令结果,输送到别的地方:

到文件中或者作为其他命令的输入(命令的链接,或者叫命令管道)。

把两个命令连起来使用,一个命令的输出作为另一个命令的输入,这就构成了管道。

管道的英语是pipeline。你可以想象一个个水管,连接起来。这一个水管流出来的水(输出),如果接上另一个水管,水是不是就流入另一个水管,成为另一个水管的输入了?

当然了,在计算机科学中,流(英语是stream)的含义是比较难理解的,也比较丰富,不同的情况下含义也不太一样。如果把它比作水流可能不完全。

知乎上就有一篇帖子,大家可以看看:

《如何理解编程语言中「流」(stream)的概念?》

http://www.zhihu.com/question/27996269

我们常可以看到这样的字样:位元流,字节流,资料流,视频流,音频流,流媒体,流算法,流处理,数据流挖掘,等等。

在维*基百科中,流的简单定义是这样的,供大家参考:

《In computer science, a stream is a sequence of data elements made available over time. A stream can be thought of as items on a conveyor belt being processed one at a time rather than in large batches.》

以上这段英语翻译过来大致意思是(有点难翻译...翻得不好不要拍我):

《在计算机科学中,流是时间上可用的一系列数据元素。我们可以把流比喻成传送带上的物件,每个时间点传输一个,而不是多个打包传输。》

(翻得连我自己都快吐了...如果要扔我鸡蛋请再多扔几个面团过来,这样我可以做鸡蛋灌饼)

前面说了,我们会学习如何把命令的输出结果重定向到其他地方:

哪里:文件或者另一个命令的输入。

如何实现:通过在命令间插入特定的符号(这些符号可以被称为《重定向流》符号)。下面我们会学到,有好几种符号。

可以用下图来做一个小结:

Linux探索之旅第三部分第二课:流、管道、重定向,三管齐下

可以看到,命令的输出可以有三个去向:

终端

文件

其他命令的输入

到目前为止,我们只用过第一种形式:把命令的输出结果显示在终端。

我们岂能就此罢休呢对吧,肯定要乘胜追击啊。一不做二不休啊。

重定向流从Unix时代就已经是很重要的概念了,后来Linux出现,重定向流的原理依旧沿用。

重定向流将会改变我们看待终端命令行的方式,所以这课很重要。

借着这一课,我们将把我们的命令行的造诣提升到另一个层级。如果之前是命令行的小学,那么这一课学完就基本可以初中毕业了。

你也许看到过一些Linux高手输入命令,经常会输入一大串,然后回车。这一大串命令中很可能就用到了我们今天学的流,管道和重定向。

>和>>:重定向到文件

我们先从最简单的开始。最简单的操作就是把命令的输出结果重定向到文件中,就不会在终端显示命令运行结果了。

准备工作:再谈cut命令

在开始学习>和>>这两个符号的用法之前,我们需要创建一些文件。

在创建文件之前,我们来谈谈cut命令的进阶用法。cut是英语《剪切》的意思,用于从文件中剪切出来一部分内容。

上一课《【Linux探索之旅】第三部分第一课:数据处理,慢条斯理》中我们学习了cut命令,但没有深入讲解,只讲了比较基本的用法(-c参数:根据字符数来剪切)。

我们再来学习一下cut命令的其他参数。

cut命令进阶:根据分隔符来剪切

我们来看一种特殊的文件形式:CSV格式。

CSV是Comma Separated Values的缩写,翻成中文是《逗号分隔值》。

以下摘自百度百科:

=================

《逗号分隔值(Comma-Separated Values,CSV,有时也称为字符分隔值,因为分隔字符也可以不是逗号),其文件以纯文本形式存储表格数据(数字和文本)。纯文本意味着该文件是一个字符序列,不含必须像二进制数字那样被解读的数据。CSV文件由任意数目的记录组成,记录间以某种换行符分隔;每条记录由字段组成,字段间的分隔符是其它字符或字符串,最常见的是逗号或制表符。通常,所有记录都有完全相同的字段序列。》

=================

CSV文件的后缀名是.csv,通常可以被Excel等软件打开,打开之后会把分隔符隔开的各个数值填充到表格里。

我们来构建一个CSV文件。因为我们要用逗号作为分隔符,来学习cut命令的进阶使用:根据分隔符来剪切。

假设我们有一个人数不多的班级,作为老师我们需要统计新近一次考试的成绩。我们为此制作了一个表格,并按照顺序把数据写入表格。

我们的CSV文件的内容可以如下,我们用Nano这样的文本编辑器来编写这个CSV文件,并且取名:notes.csv (note是英语《成绩》的意思)。

Mack,95 / 100,很不错

Matthew,30 / 100,跟平时一样水

Louise,70 / 100,有进步

Luke,54 / 100,接近平均分了

John,68 / 100,不错,但还可以更好

Samuel,100 / 100,总是那么完美

David,40 / 100,退步挺大呀

Linux探索之旅第三部分第二课:流、管道、重定向,三管齐下

如上图所示,我们的notes.csv文件中每一行由三部分组成,每部分由一个逗号分隔:

学生名字

成绩(满分是100分)

评语

现在假如我们要从notes.csv文件中提取名字那一列,怎么办呢?我们不能用cut命令的-c参数啊,毕竟每个名字的字符数不相等。

聪明如你应该想到了。是的,我们看到文件中每一行的每一部分是用分隔符来隔开的,所以我们可以这样做:

需要用到两个参数:

-d参数:d是delimiter的缩写,是《分隔符》的意思。用于指定用什么分隔符(比如逗号,分号,双引号等等)。

-f参数:f是field的缩写,是《区域》的意思。表示剪切下用分隔符分隔的哪一块或哪几块区域。

我们的notes.csv文件是用逗号来分隔三个部分的,我们要剪切下来的是名字那一列,也就是第一部分。因此我们可以这样使用:

cut -d , -f 1 notes.csv

Linux探索之旅第三部分第二课:流、管道、重定向,三管齐下

怎么样,很不错吧。我们通过cut命令的两个参数就实现了从notes.csv文件中剪切下第一部分(名字)的想法。

那如果我们只想剪切下评语部分呢?评语是第三部分:

cut -d , -f 3 notes.csv

Linux探索之旅第三部分第二课:流、管道、重定向,三管齐下

那如果我们要第一和第三部分呢?可以这样:

cut -d , -f 1,3 notes.csv

Linux探索之旅第三部分第二课:流、管道、重定向,三管齐下

同样地,我们可以用 cut -d , -f 2- notes.csv 来剪切第二部分直到最后的内容:

Linux探索之旅第三部分第二课:流、管道、重定向,三管齐下

好了,现在准备工作做好了,我们可以来学习重定向流符号了。

>:重定向到新的文件

我们知道虽然我们刚才用cut命令从notes.csv文件中剪切出来一些部分,但原始的notes.csv文件是不变的。

我们现在想要将剪切出来的部分储存到一个文件中,而不是像之前那样显示在终端里。

(为了方便演示,我们在家目录下新建一个redirect目录,redirect是英语《重定向》的意思。将之前的notes.csv文件放到这个目录下,再用cd redirect命令定位到这个目录)

我们需要用到>这个神奇的符号,如果你是美式键盘,那么可以在句号那个键找到这个符号,在句号的上面。所以要输入这个符号,可以用Shift加上句号那个键。做网络前端开发的程序员应该对这个符号不陌生,因为HTML语言里到处是<>这对符号。

这个符号可以将命令的输出结果重定向到你自己选择的文件中。例如:

cut -d , -f 1 notes.csv > students.txt

student是英语《学生》的意思。如果你运行上述命令,那么终端不会有任何显示。因为我们将cut命令的运行结果(剪切了名字那一列)重定向到students.txt文件中了。

Linux探索之旅第三部分第二课:流、管道、重定向,三管齐下

可以看到,我们用cat命令打印了student.txt文件的内容,正是cut -d , -f 1 notes.csv 这句命令的运行结果。

我们用ls命令查看redirect目录下的文件,发现多了一个students.txt文件。

怎么样,>这个符号很有用吧。不过使用时要小心,因为>符号会把输出重定向到文件中,如果此文件不存在,则新建一个文件;如果此文件已经存在,那就会把文件内容覆盖掉(清除原有内容,然后写入文件),而且是不会征求用户确认的。

有时候,我们既不想将命令的输出显示在终端,又不想将其储存到文件中,怎么办呢?

Linux中有一个俗称《黑洞》的文件,就是 /dev/null

null是英语《无,空》的意思。

/dev/null 文件是特殊文件,不是一个目录。此文件具有唯一的属性:它总是空的。它能使发送到/dev/null 的任何数据作废,就好像这些数据掉进了无底的黑洞一般。

因此,假如我们不需要在终端显示刚才那个cut命令的结果,也不想存储到文件里,那么可以这么做:

cut -d , -f 1 notes.csv > /dev/null

>>: 重定向到文件末尾

我们已经知道,单独一个>符号可以实现重定向到新的文件(覆盖文件内容),那么两个连在一起的>符号有什么作用呢?

>>的作用与>是类似的,不过它不会像>那么危险(如果文件已经存在,>符号会覆盖文件内容),而是将重定向的内容写入到文件末尾,起到追加的作用。如果文件不存在,也会被创建。

我们就来实践一下:

cut -d , -f 1 notes.csv >> students.txt

因为我们上一个例子中已经用>符号来重定向名字那列的内容到students.txt文件中了,所以上面的命令会追加同样内容到students.txt的末尾。

Linux探索之旅第三部分第二课:流、管道、重定向,三管齐下

我们用cat命令打印了students.txt文件的内容,可以看到有两遍notes.csv中名字列的内容。

>>符号在很多情况下非常有用,比如你人不在电脑前,而你又想让终端为你记录程序运行的结果,就可以在一个日志文件的末尾一直写入。例如:

command >> results.log

小结

我们方才学习了两个重定向流符号:

>:重定向到文件中。如果文件已存在,则覆盖文件内容;文件不存在,则创建文件。

>>:重定向到文件末尾。文件不存在,则创建文件。

可以用下图演示两个符号的原理:

Linux探索之旅第三部分第二课:流、管道、重定向,三管齐下

2>,2>>,2>&1:重定向错误输出

怎么样,流的重定向是不是很有趣?那么我们继续学习其他的重定向流符号。

我们首先来学点新知识。

stdin,stdout,stderr:标准输入,标准输出,标准错误输出

这三个又是什么东东?这一课新东西果然多。

一般学编程,到某个阶段,总应该会碰到这三位仁兄的。所以既然逃不了,不如勇敢来面对。

对于我们的终端命令行,我们从键盘向终端输入数据,这是标准输入,也就是stdin。

终端接收键盘输入的命令,会产生两种输出:

标准输出:stdout。指终端输出的所有信息(不包括错误信息)。

标准错误输出:stderr。指终端输出的错误信息。

第一个stdout就是我们到目前为止看到的那些Linux命令的正常运行结果,比如在终端中运行ls命令,我们以前也看到了它列出当前目录下所有文件。

那什么是标准错误输出呢?其实我们以前也看到过,只是见得不多而已。

我们用一个例子来说明。

假设,我们运行cat notes.csv命令,想要显示notes.csv文件的内容。将会有两种结果:

如果一切顺利(notes.csv文件存在于当前目录,而且我们有权限这么做),那么终端就会显示其内容。这是标准输出。

如果出错(比如notes.csv文件不存在),那么终端就会显示错误信息。这是标准错误输出。

Linux探索之旅第三部分第二课:流、管道、重定向,三管齐下

如上图所示,我们在redirect这个目录中运行cat notes.csv时,因为文件存在,而且我们有权限对其执行cat命令,所以打印出了文件内容。这就是标准输出。

接着,我们用cd .. 命令定位到了上一级目录,也就是用户的家目录,在这个目录中我们并没有创建notes.csv文件,所以cat notes.csv命令自然就输出了错误信息:No such file or directory,翻译过来就是《不存在此文件或目录》。这就是标准错误输出。

默认情况下,标准输出和标准错误输出都会显示在终端,这也是为什么我们之前对它们的区别并没有那么在意的原因,因为长得挺像的,都在终端输出。

我们可以用下图来演示:

Linux探索之旅第三部分第二课:流、管道、重定向,三管齐下

这三个你也可以把它们看作流:

stdin:标准输入流。standard input的缩写。标准输入是指输入至程序的数据(通常是文件)。程序要求以读(read)操作来传输数据。并非所有程序都要求输入。如dir或ls程序运行时不用任何输入。 除非重定向,输入是预期由键盘获取的。 标准输入的文件描述符为 0 (零);在POSIX <unistd.h> 的定义是 STDIN_FILENO;相对应的 <stdio.h> 变数为 FILE* stdin ;类似地, <iostream> 变数为 std::cin 。

stdout:标准输出流。standard output的缩写。标准输出是指程序写输出数据的流。程序要求数据传输使用写的运算。并非所有程序都要求输出。如mv或ren程序在成功完成时是没有输出的。 除非重导向,输出是预期显示在终端上的。 标准输出的文件描述符为 1 (一)。POSIX <unistd.h> 定义是 STDOUT_FILENO;相对应的 <stdio.h> 变数为 FILE* stdout ;类似地, <iostream> 变数为 std::cout 。

stderr:标准错误输出流。standard error的缩写。标准错误输出是另一个输出流,用于输出错误消息或诊断。它独立于标准输出,且标准输出和标准错误输出可以分别被重定向。标准错误输出的文件描述符为 2 (二);POSIX <unistd.h> 定义为 STDERR_FILENO;相对的 <stdio.h> 变数 FILE* stderr。C++ <iostream> 的变数为: std::cerr。

文件描述符  名字  解释
0  stdin  标准输入
1  stdout  标准输出
2  stderr  标准错误输出
那什么是文件描述符呢?

文件描述符的英语是File Descriptor,简称fd。

文件描述符是计算机科学中的一个术语,要完全讲清楚可能要用单独的一课。我们就不深究了。

文件描述符是一个用于表述指向文件的引用的抽象化概念。这定义本身也有点抽象。我们不需要太深入了解。大致只需要知道:

《文件描述符在形式上是一个非负整数。实际上,它是一个索引值,指向操作系统内核为每一个进程所维护的该进程打开文件的记录表。当程序打开一个现有文件或者创建一个新文件时,内核向进程返回一个文件描述符》。

文件描述符通常是Unix,Linux等系统的概念。在Windows中,也有类似的概念,但是Windows中称为《句柄》,就是handle。

好了,重新回到我们的话题。刚才我们已经学习了用>和>>两个符号可以将输出重定向到文件中,那么我们在出错的情况下是不是也可以用>和>>将标准错误输出也重定向到文件中呢?我们来试试:

cat not_exist_file.csv > results.txt

Linux探索之旅第三部分第二课:流、管道、重定向,三管齐下

如上图所示,因为 not_exist_file.csv 这个文件不存在(正如其名),所以产生了错误信息。但是这个错误信息却并没有如我们所愿的写入到results.txt文件中,而是仍旧在终端输出了。这是为什么呢?不是说好的>符号用于重定向输出到文件的吗?

其实,>和>>符号只是将标准输出重定向到文件。并不能将标准错误输出重定向到文件。

那么我们要重定向标准错误输出,该怎么办呢?

我们就要用到 2> 这个符号,是的,就是在>这个符号左边紧挨着写一个2。

为什么是2呢?记得上面说的吗?标准错误输出的文件描述符是2,所以这里的2表示标准错误输出。如果没有2,单独的>符号就是重定向标准输出(文件描述符为1)。

我们补充一下刚才的命令:

cat not_exist_file.csv > results.txt 2> errors.log

这个命令里有两个重定向:

> results.txt:将标准输出重定向到results.txt文件中。

2> errors.log:将标准错误输出重定向到errors.log文件中。

也就是说,假如 not_exist_file.csv这个文件确实存在,将其内容写入results.txt文件中;假如文件不存在,将错误信息写入errors.log文件中。

Linux探索之旅第三部分第二课:流、管道、重定向,三管齐下

如上图所示,我们运行了cat not_exist_file.csv > results.txt 2> errors.log这个命令,因为not_exist_file.csv这个文件不存在,所以results.txt文件是空的;errors.log文件的内容是错误信息。

类似地,2>>符号用于将标准错误输出重定向到文件末尾。

合并输出

上面我们学习了如何将标准输出和标准错误输出分别重定向到不同文件。但是有的时候,我们比较任性,我们就想把标准输出和标准错误输出都重定向到同一个地方。怎么做呢?

须要使用 2>&1 这个组合符号。

看着怪怪的对吧?由四个字符组成。这个符号的作用是:将标准错误输出重定向到与标准输出相同的地方。

我们用实例演示一下:

cat not_exist_file.csv > results.txt 2>&1

上面的命令的作用是:将cat not_exist_file.csv这个命令的所有输出(标准输出和标准错误输出)都重定向到results.txt文件中。

Linux探索之旅第三部分第二课:流、管道、重定向,三管齐下

如上图所示,运行cat not_exist_file.csv > results.txt 2>&1命令之后,因为not_exist_file.csv这个文件不存在,但又因为加了2>&1这个符号,所以标准输出(为空)和标准错误输出(cat: not_exist_file.csv: No such file or directory)都重定向到results.txt文件中了。

然后大家是否觉得要将标准输出和标准错误输出都重定向到文件末尾,应该是这样写:2>>&1 呢?

其实不然,这样是不对的。我们还是保持2>&1这个组合不变,只改变前面的符号就行了。例如:

cat not_exist_file.csv >> results.txt 2>&1

Linux探索之旅第三部分第二课:流、管道、重定向,三管齐下

可以看到,又追加了一条错误信息到results.txt文件中。

小结

2>:将标准错误输出重定向到文件。如果文件已经存在,则覆盖文件内容;如果不存在,则创建文件。

2>>:将标准错误输出重定向到文件末尾。如果文件不存在,则创建文件。

2>&1:将标准输出和标准错误输出都重定向到一个地方。

用下图来演示:

Linux探索之旅第三部分第二课:流、管道、重定向,三管齐下

上图中,我故意没有加>>和2>>,不然这图就太复杂了。其实>和>>,2>和2>>的区别就是前者覆盖文件内容,后者追加内容到文件。

<,<<:从文件或键盘读取

到目前为止,这一课我们只讲了如何重定向命令的输出,也就是决定命令输出的信息的去向。那么接着我们可以做一点相反的事情:决定命令的输入来自哪里。

当然了,上面也说了,不是所有的命令都有输入,也不是所有的命令都有输出。

到目前为止,我们的命令的输入都来自于后面接的参数,这些参数有些是文件名,有些是目录名,等等。

但我们其实可以使命令的输入来自于文件或者键盘输入。如下图所示:

Linux探索之旅第三部分第二课:流、管道、重定向,三管齐下

<:从文件中读取

看到这个<符号,是不是想到了之前的>符号呢?

是的,这对孪生兄弟,原理类似但是功能正相反。<符号用于指定命令的输入。

我们用一个简单的例子来演示:

cat < notes.csv

Linux探索之旅第三部分第二课:流、管道、重定向,三管齐下

如上图所示,cat < notes.csv的运行结果和cat notes.csv (不用重定向流符号)一模一样,都是在终端打印notes.csv的内容,那我们为什么需要<符号呢?

事实上,虽然cat < notes.csv的运行结果和cat notes.csv一样,但是原理却不一样:

cat notes.csv :这种情况下,cat命令接受的输入是notes.csv这个文件名,那么它要先打开notes.csv文件,然后打印出文件内容。

cat < notes.csv :这种情况下,cat命令接受的输入直接是notes.csv这个文件的内容,cat命令只负责将其内容打印。而打开文件,将文件内容传递给cat命令的工作则交给shell程序(也就是控制终端的程序)来完成。

所以,虽然结果看似一样,但是中间的过程确实不一样的。

<<:从键盘读取

<<符号的作用是将键盘的输入重定向为某个命令的输入,很多情况下都很有用。

我们用实例来说明:

sort -n << END

Linux探索之旅第三部分第二课:流、管道、重定向,三管齐下

如上图所示,输入这条命令之后,回车,终端就进入了键盘输入模式,看到那个>符号和其后闪动的光标了么?

就是让你输入数据的。

我们知道sort -n的作用是按照从小到大进行排列。那么我们就输入一些数值吧(每输一个数值,用回车键来换行,接着输入下一个数值,输入END来结束输入,END被称为结束字符串。当然了,你可以用其他字符串,比如haha,nihao,不一定要用END):

Linux探索之旅第三部分第二课:流、管道、重定向,三管齐下

可以看到,sort -n命令将我们输入的一串数值进行了由小到大的排序。

我们再试试其他命令与<<符号的配合,这次我们用wc命令吧,wc命令用于统计字符等,如果配合-m参数就是统计字符数。

wc -m << END

Linux探索之旅第三部分第二课:流、管道、重定向,三管齐下

可以看到,《How many characters are there in this sentence ?》这句话中有49个字符。

这句话翻成中文就是《这句话中有多少个字符?》

小结

<:将命令的输入重定向为文件内容。

<<:将命令的输入重定向为键盘输入,以逐行输入的模式(回车键换行)。所有输入的行都将在输入结束字符串(例如上面例子中的END)之后发送给命令。

Linux探索之旅第三部分第二课:流、管道、重定向,三管齐下

当然了,我们也可以将之前学习的输出重定向符号和这一节的输入重定向符号结合使用:

sort -n << END > numbers_sorted.txt 2>&1

Linux探索之旅第三部分第二课:流、管道、重定向,三管齐下

如上图所见,以上命令将sort -n命令对数值排序的结果都输入到numbers_sorted.txt文件中,如果有错误信息也输入。

|:管道

好了,终于来到本课的最后一节了。

是不是有点累呢?如果是的话,可以去烤一只烤鸭。为什么小编不像以前一样说烤鸡了呢?是因为最近有一对好友夫妇在法国Aix en Provence开了一家烤鸭店,他们的公众号发了几张诱人的烤鸭照片,看得小编直流哈喇子。但是吃不到啊,小编住在Nice附近,离Aix en Provence有近3个小时火车的路程。唉,以后去吧~

好了,不扯烤鸭了,言归正传。

这一节要学的符号,将会成为你以后Linux生涯中最常用的符号之一,使用率绝对高过这一课学的其他符号。

这个符号就是所谓的《管道符号》:|

是的,就是美式键盘上位于反斜杠那个键的那个符号,要输入这个符号,需要使用Shift + \

|符号既然被称为《管道符》,那么其作用就是《建立命令管道》咯。

是的,还记得本课开篇的时候我们提到的那个比喻吗?一个命令的输出可以作为另一个命令的输入,就好像将两根水管接起来一样,前一根水管流出来的水就会流入后一根水管了。

管道也算是重定向流的一种。

原理

将两个命令连成管道,这是什么意思呢?简单的说就是将一个命令的输出作为另一个命令的输入,如下图所示:

Linux探索之旅第三部分第二课:流、管道、重定向,三管齐下

大致来说:命令1的输出立即会变成命令2的输入。使用这个原理,我们可以用|符号连接无穷多个命令,构成很长的命令管道。

管道符绝对使Linux命令的威力增加N倍。之前我们也说了,Linux中的命令(很多都是从Unix时代承袭下来的),每一个的功能虽然有限,但是却在它们自己的岗位上尽忠职守,工作做得棒棒哒。所以单独一条Linux命令可能功能有限,但是一旦《铁索连环》,那可是会结合各个命令的功能,其强大不难想见。

实践

我们用几个实例来学习管道吧。

按学生名字排序

你应该还记得我们的notes.csv文件,其中有三部分,用逗号隔开的,第一部分是学生名字,第二部分是成绩,第三部分是评语。

之前我们用cut -d , -f 1 notes.csv 命令来剪切了名字那一列。如果你还记得我们前一课学的sort命令,它是用于排序文件内容。

那么为什么不把这两个命令用管道符连接起来呢?我们可以用sort命令对cut命令提取到的名字列进行排序:

cut -d , -f 1 notes.csv | sort

Linux探索之旅第三部分第二课:流、管道、重定向,三管齐下

如上图所见,我们用sort命令对名字列按照首字母的字典顺序进行了排序。

怎么样,管道是不是很有意思?

如果我们将上面的命令再扩充一下,配合之前学习的输出重定向符号,就变成了这样:

cut -d , -f 1 notes.csv | sort > sorted_names.txt

Linux探索之旅第三部分第二课:流、管道、重定向,三管齐下

可以看到,我们将sort命令排序的结果重定向到sorted_names.txt文件中了。

根据大小排序目录

之前我们学过,du命令可以深入遍历当前目录下每个子目录,把所有文件的大小都做一个统计。

我们可以用cd命令来回到我们的家目录。然后运行du命令。

问题是:du命令要运行挺久的,因为小编家目录下文件很多。

Linux探索之旅第三部分第二课:流、管道、重定向,三管齐下

单独用du命令的缺点我们也有目共睹了,一是可能要运行很久(如果文件很多),二是显示结果没有排序,杂乱无章。

但如果我们这样做就会清爽很多:

du | sort -nr | head

还记得head命令的用法么?如果不用-n参数指定显示行数,那么head会默认显示前10行。

Linux探索之旅第三部分第二课:流、管道、重定向,三管齐下

所以以上命令的作用是:

du 深入遍历当前目录下每个子目录,把所有文件的大小都做一个统计

sort -nr sort命令的-n参数是按以数值来排序(此处是文件大小)排序,默认是小的在前;-r参数是倒序排列,有了-r参数,-n参数就变成大的数值在前了

head 列出前十个最大的数值(这里是文件大小)

列出包含关键字的文件

还记得我们的好朋友grep命令吗?之前的课中有学过,这个命令很强大,可以在文件中查找关键字,并且显示关键字所在的行。但有时,我们会觉得grep显示的信息太冗长了。每一行不仅有文件名,还有关键字出现的那一行文本,等等。

我们可以运行以下命令试试:

sudo grep log -Ir /var/log | cut -d : -f 1 | sort | uniq

Linux探索之旅第三部分第二课:流、管道、重定向,三管齐下

这个命令做了什么呢?让我们一步步来分解:

sudo grep log -Ir /var/log :遍历/var/log这个目录及其子目录,列出所有包含log这个关键字的行。-I参数用于排除二进制文件。-r参数用于递归遍历。sudo命令是为了以root身份查找系统文件夹/var/log。

cut -d : -f 1 从命令1的输出结果中只剪切出文件名那一列(由冒号分隔的第一个区域)。

sort 将文件名的列以首字母的字典顺序进行排序。

uniq : 去掉重复的文件名。

小结

小结很简单,因为管道符的基本作用比较简单,就是将一个命令的输出重定向为另一个命令的输入。如下图所示:

Linux探索之旅第三部分第二课:流、管道、重定向,三管齐下

记住这个基本原理就好了。

好了,终于这一课结束了。是不是有点晕,如果觉得还没怎么掌握,那需要再读一遍本课程,参考一些课外读物,自己在终端上多练习。尽情发挥你的想象力,来创造出各种命令的组合吧。

总结

Linux命令的结果(标准输出),我们不一定要显示在终端里,也可以将其存放在一个文件里,只需要在命令后加上 > 符号,然后接文件名就可以了。例如 ls > file_list.txt 就会把ls命令的结果(当前目录所有文件的列表)存放到file_list.txt文件中,不在终端显示了。

>> 符号可以追加内容。>符号会把文件内容清空,再写入。如果文件已经存在,那么>>符号会在文件末尾追加写入内容。

2>和2>>符号用于重定向标准错误输出到文件中。2>&1符号用于将标准错误输出和标准输出重定向到相同地方。

<符号重定向命令的输入为文件内容,<<符号重定向命令的输入为键盘输入。

管道符号|可以将命令连接起来,好像一个个对接的管道一样,前一个命令的输出成为后一个命令的输入。

第三部分第三课预告

今天的课就到这里,一起加油吧!

下一课我们学习:监视系统活动,滴水不漏

新朋友请关注「程序员联盟」微信搜公众号 ProgrammerLeague

程序员联盟官网:

coderunity点com

程序员联盟论坛:

coderunity点com/bbs/

小编微信号: frogoscar

小编邮箱: enmingx@gmail.com


【Linux探索之旅第三部分第二课:流、管道、重定向,三管齐下】
http://toutiao.com/a6193788933316428033/?iid=3041659310&app=news_article&tt_from=android_share&utm_medium=toutiao_android&utm_campaign=client_share
背景:关于rpc,即远程过程调用,这个技术最早是由sun公司发明出来,后来在linux上默认有rpc服务,该功能我最早见在新浪的企业邮箱里有这样一个运用,在用户登录这块用到rpc,其服务端实现了与mysql长连接的技术,进而减少了重次重新连接,提高了高并发时的性能,而后来在鸟哥的一个php框架里实现了类似的php rpc框架,也是用c写的,但究其根源,还是来自于xdr这样一种数据结构,其可以跨平台使用,进而它在做一些高并发这块的的确确有较好的效果,最近rango兄弟的swoole里发布后,也有兄弟基于它做成了Dora Rpc,值得了解,应该性能还成,可能试用并使用,毕竟简单、效率、快才是王道。
Dora RPC
简介(Introduction)

用于复杂项目前后端分离,分离后项目都通过API工作可更好维护管理。

是一款基础于Swoole定长包头通讯协议的最精简的RPC
目前只提供PHP语言代码
后续有什么bug或者问题请提交Issue

功能支持(Function)
支持单API调用,多API并发调用
支持同步调用,异步任务下发
其他相关知识请参考Swoole扩展
客户端长链接,请求完毕后仍旧保留,减少握手消耗
guid收发一致性检测,避免发送和接收数据不一致

dora-rpc/server.php

使用最简单的方式实现的服务端
目前需要继承才能使用,继承后请实现dowork,这个函数是实际处理任务的函数参数为提交参数
做这个只是为了减少大家启用RPC的开发时间
返回结果是一个数组 分两部分,第一层是通讯状态(code),第二层是处理状态(code)


使用方法(Example)

客户端(Client)


服务端(Server)



_____________________________________________________________________________
经过24小时持续压力测试,目前接口仍旧工作正常

使用的vagrant虚拟进行压测的分配了1G内存和1核CPU(Mac 2.2 GHz Intel Core i7)


压测进程:目前只开了10个php进程疯狂发送请求
并发性能:TPS 2100上下(比直接使用curl快很多)
响应时间:0.02~0.04s 偶尔出现0.4s
后端代码为:查询一次数据库后返回结果
CPU使用:10~25%
内存使用:一个PHP task 16M 目前开了30个进程
PHP版本:5.4.41
压测时使用端口个数:10个(长连接)

测试代码使用的使用客户端示范程序无限循环,服务端直接返回一个数组。
每次接口会请求一次api接口调用后再下发一个请求内含两个并发任务

其他资源情况如下:

Dora <wbr>RPC <wbr>虚拟机下实测性能

此开源使用Swoole特性制作
客户端使用长链接,处理请求结束后连接也不会断开,再次使用的时候会自动找回
服务端自动管理task及进程通讯
通过task处理业务
如果使用更高速的序列化函数取代serialize会更快一些
支持单api请求,多api并发请求,此功能可取代发展越来越怪的gearman
如果有持久化请求需求,可以考虑在此基础上自行封装下(会降性能的哦)

过几天增加个中间件,可以检测后端服务压力状态自动负载均衡~

github地址
https://github.com/xcl3721/Dora-RPC


有一个哥们测试了一下,地址:http://blog.sina.com.cn/s/blog_54ef39890102vkgh.html
背景:有时需要查一些函数,比如分隔函数,不知道那个分割符号是放第几位了,怎么办? 有时想看php.ini文件在哪儿了,怎么办?有时想看这个扩展如:swoole扩展的相关扩展信息怎么看?有时想看这个扩展的类名怎么看?有时想看这个扩展的函数有哪些怎么了解,这些都是问题,于是有该文。


php -i ,php -i|grep php.ini  php --ri swoole  php --ri memcache

零)php -i|grep php.ini
[root@localhost htdocs]# php -i|grep php.ini
Configuration File (php.ini) Path => /usr/local/php/etc
Loaded Configuration File => /usr/local/php/etc/php.ini

  --rf <name>      Show information about function <name>.
  --rc <name>      Show information about class <name>.
  --re <name>      Show information about extension <name>.
  --ri <name>      Show configuration for extension <name>.


一)php --rf explode
[root@localhost htdocs]# php --rf explode
Function [ <internal:standard> function explode ] {

  - Parameters [3] {
    Parameter #0 [ <required> $separator ]
    Parameter #1 [ <required> $str ]
    Parameter #2 [ <optional> $limit ]
  }
}

二)php --ri memcache
memcache
memcache support => enabled
Version => 3.0.6
Revision => $Revision: 310129 $

Directive => Local Value => Master Value
memcache.allow_failover => 1 => 1
memcache.max_failover_attempts => 20 => 20
memcache.default_port => 11211 => 11211
memcache.chunk_size => 32768 => 32768
memcache.protocol => ascii => ascii
memcache.hash_strategy => consistent => consistent
memcache.hash_function => crc32 => crc32
memcache.redundancy => 1 => 1
memcache.session_redundancy => 2 => 2
memcache.compress_threshold => 20000 => 20000
memcache.lock_timeout => 15 => 15



三)--re <name>      Show information about extension <name>.
php --re memcache
Extension [ <persistent> extension #39 memcache version 3.0.6 ] {

  Method [ <internal:memcache, inherits MemcachePool> public method decrement ] {
        }

        Method [ <internal:memcache, inherits MemcachePool> public method close ] {
        }

        Method [ <internal:memcache, inherits MemcachePool> public method flush ] {
        }
      }



四)--rc <name>      Show information about class <name>.

php --rc memcache
Class [ <internal:memcache> class Memcache extends MemcachePool ] {

  - Constants [0] {
  }
    历史总是要通过这样决绝的悲情成就一段佳话,后来无数的传记这样记载那段故事,“1987年,任正非因为工作失误离开南油公司,被逼着走上了创业的道路。”
  1987年,也是一个草莽英雄起家的年代。

  1987年,宗庆后靠借来的14万元承包连年亏损的杭州上海校办企业经销部,并开始蹬三轮卖冰棍。1945年出生的宗庆后,这一年刚好42岁。

  1987年,两个日后引领中国经济转折点的人一个刚从清华毕业,赶赴MIT攻读硕士学位。一个即将从杭州师范毕业。他们恰好都出生在1964年,这一年,他们都恰好是23岁。

  去MIT的张朝阳遇见了尼葛洛庞帝,他们是最早投资中国互联网的人。杭州师范毕业的马云创办了海博翻译社,开始了曲折的试错过程。

  1987年,44岁的任正非被迫辞职,离开了转业后分配到的南油公司。

  是下面这段文字激发我研究1987年的深圳,1988年的中国和这两年的任正非。
   “一个44岁老男人,经营中被骗了200万,被国企南油集团除名。曾求留任遭拒绝,还背负还清200万债。妻子又离婚,他一个人带着老爹老娘弟弟妹妹在深圳住棚屋,借钱创立了华为公司。已过了冲锋势头,没有资本、没有人脉、没有资源、没有技术、没有市场经验,看谁都比他强的一个人,逆袭成功。用27年把华为带到通讯行业世界第一位置。如果是你也有类似,今天你在哪里呢。”
  我查了目前市面上的任正非传,基本上这段话的前半部分是有根据的。但后半部分有刻意曲解的成分。任正非当时是有一点点资本的,跟几个朋友一起创业也并非孤家寡人,他从南油退出时已经官至副总经理。而且他处在管制最为宽松的深圳,当时他跟王石一样,靠倒卖产品差价获得第一桶金。而且他们都赶上深圳最适合创业的年代,1988年,那不过是中国的“五月花号”把全中国最不安分的人都带到了这个国境线最南端的小渔港的时候。

  为了说明一个人的传奇其实并不必附会他的苦难过去,并非苦难越多越能衬托一个人的成功意义。

  44岁的任正非,并不比70多的褚时健缺乏励志性,那些触底反弹的故事只要让人们明白触底不一定会跟着反弹就够了,而不用一直强调底有多深多长。

  任何人,如果灾难足够长,困难足够多,命运足够惨一定是可以被打倒的,所谓活下来的人,三分之一靠努力,三分之一靠人品,三分之一靠运气,概莫能外,缺一不可。后代人写历史,不应老盯着人的意志和主观能动性,这会教坏年轻人,因为大部分走投无路的成功说到底不过是靠了运气之手在背后推了一把而已。
    若成功太容易,它的深刻性和参考性就可能不足。同样是成功者,陈天桥和史玉柱可能恰好只是在对的时间抓住了对的行业。“恰好”是统计学里的概率问题,不可能人人学会。

  倒是任正非这种不屈服和不放弃能成为年轻人的精神动力。所谓雄关漫道真如铁,而今迈步从头越,每一个有追求但身陷囹圄的年轻人,在最无助的时候想想任正非,你应该还没有他那么惨吧,至少你还有青春,还有宽松的社会环境,还有再来一次的可能。

  相信历史总不缺乏再一次的巧合,只是到那个时候,能不能问自己一句,在那个波澜壮阔的年份,当时的你在哪里?跟谁风云际会?

摘自:http://www.cfi.net.cn/p20150908000864.html
背景:转这个哥们的文章有两点:1)他的博客和我的样式几乎一样,有缘分。2)现在移动互联网如火如荼,虚火很大,很多人都往里面钻,但真正懂用户懂产品技术的还是少,这个哥们的语言有点调侃,描绘生动,心理活动也很丰富,集合现实的一些人情关系的描写,跃然纸上,转了吧。

这是一个“如有雷同,纯属巧合”的故事,外加一些废话,大家请勿对号入座。开始了……

我有些尴尬地拿着水杯,正对面坐着来访的王总,他是在别处打拼的人,这几年据说收获颇丰,见移动互联网如火如荼,自然也想着要进来干一场,尽管王总从事的行当也算跟IT沾边,但毕竟太长时间不接触技术,有些东西不太熟,总要咨询下我这个在一线开发混了十几年的老程序员,十几年的开发,有好几种可能性,不过这不是重点,所以暂时忽略掉这个细节吧。

我之所以尴尬,是对王总的需求有些不知如何回答,仿佛陷入了某种习惯性的沉思中。

王总站了起来,把手机递到我面前,说:“你看看,就这样一个APP。”他不太熟练地在屏幕上划了几下,我并没有很认真地看,因为我知道这个问题很难,那就是所有的开发者都会被问,并且可能是被问得最频的一个问题:“开发这么一个APP需要多长时间?”我很想说不知道,这可能是最直截了当和准确的回答,但面对王总这位老朋友,我要是这么回答估计有些失礼,所以这个时候,我除了大致思量了一下他所指的那个APP大致涉及到哪些方面之外,还要组织下自己的语言,如何用非常得体的话告诉他,这个事情我估算不出。“你看,就这么简单的一个APP”,王总继续在屏幕上拨弄了几下,然后带着几分期待的眼神看着我。

我谨慎地说:“坦白说,我说不准,我这方面经验也不是很足,尽管做过APP开发,但又跟这个很不一样,得具体分析好所有的逻辑,才能估算出时间。”

王总对我的说法似乎不以为然,他晃了晃手机,说:“我要求不多,其实比这个还简单”,他指着屏幕上某些地方,继续说:“这个,这个,这个都可以不要,只需要这么一个列表,里面有详情,可以查看修改……”

我心里很自然地想到这是很典型的“想当然简单”的态度,我想我得让他认识到这个问题的复杂程度,我反问道:“需要登录吗?”

王总稍作停顿后,说:“那当然。”

“什么登录?用户名密码方式,还是手机登录,抑或像QQ,微博,微信这种可以借用的第三方登录?”

王总这回似乎想了一下:“作为移动互联网,我想手机登录肯定是要的,QQ,微博,对了,微信,微信最好也要……哦,你前面说用户名密码,这个应该也是要的吧。”

我很流利地接着问:“那总得有注册,如果你打算用手机登录,那得找个短信平台,还有微信登录,你得先做好企业身份认证,对了,有登录,有密码,那密码找回功能也得有吧。”

“这是肯定的。”

“同时有多种登录途径,你必须要想出一种合理的逻辑来将它们‘整合’,最常见的当然是账号绑定,例如给你的账号绑定手机号码,这样就能用手机号来登录同样一个账号,对微信登录也同理,但如今移动互联网的用户们都挺厌恶注册流程的,所以往往会要求直接手机登录或者直接微信登录,自动完成注册过程,那考虑这种情况,如果用户先用微信登录,然后再用手机登录,而不是绑定,那么就会产生两个不同的账号,而且无法将其再‘整合’起来,我们得想出一套比较完善的方案……”

王总对我所说的似乎有些缺乏耐心:“没必要这么复杂吧?你看看这个APP,这些不都有吗?”

“有没有我前面所描述的那个问题,你尝试过了吗?”

但王总似乎对问题并不关心,他只想知道做这么一个APP需要多长时间,当然要多少钱,这也是他关心的问题,他拿出了信心满满的语气:“有问题怕什么?困难算什么?这些我相信都能解决,但时间很要紧,得快,我们的竞争对手不会等我们,就这么一个东西,你想想看,要多久?”

看他的架势,像十足那种混得风生水起的成功人士,而我这种身份低微的程序员在他面前确实是有口难言,我本来还想继续告诉他细节的重要性,却被他打断:“不,不需要有多精确,你只需要估算一个范围,两个星期?或是两个月?”

我觉得我没必要再隐瞒什么了:“我真的不知道,也许一支优秀的团队两个星期就能做好(不过我自己可不相信有这么牛逼的团队),但我很明显不是那个能创造这种奇迹的人。”我心想其实就算说出了“两个星期到两年”这么一个开玩笑式的范围,也可能是错的。

王总似乎对我这样的回答很失望。但他是个执行力很强的人,想做一件事,就一定会行动,行动一定快,一定要有结果,这种雷厉风行的行事风格,确实,我挺欣赏,不过他的这个项目,我可真帮不上忙,但我还是出于礼貌,说道:“技术方面有什么问题,还是可以来问我的。”

====================== 不怎么华丽的分隔线 ======================

“做一个APP需要多长时间?”这个问题估计比测一个人还能活几天还难,一个条件如此不充分的问题,如何回答呢?

总体来说,需求越是明确,团队越是成熟,估算出来的时间就越是准确。而软件开发这个事情,不管发展多少年,不管提出了怎样的方法论,都没办法像传统制造业那样把“工时”算得那么精确,其内部错综复杂的逻辑关系使然,软件工程,绝无可能量产。

用户看到的只是一个APP,如果他用的是iOS系统,也许他根本就不会接触Android,不知道开发者除了iOS版之外,还需要做一个Android版,(有没可能还有Windows版?这样工作量无疑更大)或者,网页版搞定一切?也许你真正动手做过后就不会这么认为,再说微信小店那种模式真能适用于所有场合么?而且,如果不是网络出现异常的话,一般用户也不会注意到服务器的存在,服务器总是那么默默无闻地为用户全天候地工作,它的开发难度恐怕也不亚于APP本身,而负责APP运维的还需一些人力,大了之后甚至需要组建一个专业团队,他们需要一个“后台”,能随时查看和处理数据,如果需要随时随地都能查看和处理数据,恐怕还得给后台专门弄个APP。

这个道理就有点类似:我们看到了战机在天上华丽地完成了歼敌任务,以为只是战机本身很牛,往往忽视了战机相关的那些配套,如果没有娴熟的飞行员、作战指挥中心、地面雷达、预警机、补给、机场或航母、地勤人员等等,那么战机将失去战斗力。APP也一样,它不是一个只要能跑起来就完事的东西,支持它的配套设施和维护工作丝毫不比APP本身简单。

除开这些大的方面,细节上也带有许多的不确定性,所以一支成熟的团队尤为重要,一个经验丰富的开发者会知道,至少大致知道这个开发过程会遇到哪些问题,哪些问题比较简单,哪些问题则可能需要耗费大量的时间,这得依赖经验。我有一句话常常挂在嘴边,那就是:“没做过的东西别轻易说简单。”“想当然简单”的态度对项目没有任何好处,如果自己不确定,那么去咨询一个有这方面经验的人,就算得不到具体的答案也有大致的方向,沿着这些方向研究一下,就能知道会面临的那些问题,当然往往还不是全部。

关于“低估了难度”这事情,我过去的公司有个经典故事,当时有个小项目,就是准备把一套已经在仪器上使用的只支持英语的程序增加多语言支持,程序并不大,涉及内容也不算太多,工程师一开始认为这只是个简单的翻译工作,顶多两个星期就能完成,但一做下去就发现不简单,首先翻译得找专业人士来做,自己做不好,我们没人精通欧洲各国语言,接下来还有单位换算,有些国家用公制,有些用英制,这个得考虑,包括日期显示格式也得考虑,一下子不知道多了多少工作,这些都差不多了之后又发现了德语单词过长,我们的仪器的屏幕显示不下,超出范围,于是再调字体,做精简,前前后后开会讨论了N次,最后想Release的时候发现这么一改,程序的Size变大了很多,有些仪器的存储器装不下,这下大家可都傻了,优化呗,精简呗,程序开始有些凌乱不堪了,最后勉强通过质控部检验,总算发布了,发觉足足搞了半年。不过如今想想之所以耗费了这么多时间,一个很重要的原因是经验不足,对多语言,国际化这块不熟,走了不少弯路,所以我前面也提到,成熟的团队尤为重要。

我们在估算项目时间的时候,往往只算了“写代码的时间”,而把那些和老板或客户扯皮,做需求分析,设计,测试,和修复bug的时间不考虑进去,而这些时间加起来通常比写代码的时间多出不少,我个人是不轻易为了讨好老板而把完成时间说得很短的,为啥?——根本做不到嘛,干嘛要撒谎?如果一个需要一星期完成的新功能开发,我通常得把这个时间double,这已经算比较“不保守”的了。

即便只算写代码的时间,也往往会被低估,老板或客户对你开发的东西很可能不满意,或许你误解了他的功能需求,或者界面有点卡顿,或者这个图标颜色不好看,你是开发者,不是美工,虽然凑合可以当一下美工,但毕竟不专业,更重要的是做做UI设计,做做图这种事情,也得耗费不少时间,当你为“一个像素”焦头烂额的时候,是不是很渴望团队中有一名设计师?这时候得提醒下老板:你必须要在时间和功能之间,做点取舍。老板当然很不高兴,但也不得不在功能上做出了一些妥协。虽然这样做能让难产的项目早点上线,但却为来日项目的失败,给老板添加了一个很好的借口:我们的工程师太差了,没按我说的去做。

老板或客户除了会抱怨你做出来的东西不够好看之外,还会再提很多东西:这个界面能不能改成多选,能否增加通知功能,已读未读状态要有,界面能不能再流畅点,昨晚程序咋“闪退”了一次……需求只管提功能,但没说具体这个UI要多美观,也没说程序稳定性要好,更没涉及到要达到多大的吞吐量,当然,可能更重要的——安全性也没提,你心一惊:是啊,如果有黑客,不,只要稍微懂一点技术的恶意用户想刷爆我们的服务器,那简直太简单了,而这些防护措施我都没做!所幸的是项目名气太小,暂时无需考虑这个。(貌似大多数APP都活不到需要考虑这个的时候)

所有这些,你说功能也好,细节也好,稳健性也好,都不是能自动从土里长出来的东西,都得需要花时间去想,去做,有些甚至还是个“系统工程”,如果头痛医头脚痛医脚去做的话,系统里到处充满“飞线”,无疑会给将来的维护留下了许多隐患。攻城狮的你,都考虑了吗?更别说老板为了节省成本而给你购置的低性能电脑让你整天抓狂这些“无关紧要”的事。

====================== 不怎么华丽的分隔线 ======================

话说王总告别我之后就以迅雷不及掩耳之势注册了公司,注册了域名,搞到了办公室,还一下子叫来了一帮子人风风火火地搞了起来,这种发展势头,这种干劲,我只有自叹不如。心底里真有些后悔怎么没跟他去干事业,不过这只是感性的一瞬间,理性又在接下来的几百毫秒里将我拉了回来:还是别去好,跟他沟通不来的。

王总的项目后来以一飞冲天之势迅猛发展,而他如今已经是一家估值几亿的公司的CEO,我嘛,越来越觉得自己是个Loser,独自坐在办公室里,还是拿着那个水杯,懊恼不已——打住!这样是不是比较有戏剧性?可虽然一开始我就声明此故事“如有雷同,纯属巧合”,但也不能胡乱瞎编,真正的结局是:确实风风火火弄了几个月,后来就突然杳无音讯了,本来想打电话问问王总究竟怎样,无奈他变成了另一个超级忙人,再无心思跟我聊家常了。嗯,结局还是差不多,我还是那个继续苦逼地坐在办公室里的程序员,唉,别想了,开工吧!


原文原创来自:http://www.cnblogs.com/guogangj/p/4676836.html
背景:这位兄弟做的lnmp包细节上不错,不支持hhvm,最主要是这位兄弟其服务意识好,之前有一个lnmp.org,我觉得都挺不错,这块主要是对nginx代码了apache后,apache如何获取ip这块是个大问题,需要一些模块来设置的一个配置,对来源IP有一个日志需求很正常,这块像Tencent的TGW,其实在这一块从一个大的IP进来分到后面的机器处理,到底落到哪台机器了,及IP是多少,也涉及到这些类似问题,对于此种架构来讲,没有过实际生产经验,仅供参考,一般直接从硬件如F5,或lvs代理到apache或nginx,我想这个哥们的应用场景可能不大一样吧。

Internet -> Nginx -> Apache
最近在将Apache-2.2和Apache-2.4添加到《lnmp一键安装包》中,Nginx作为前端,Apache作为后端的情况下,Apache只能获取到Nginx前端的ip地址(127.0.0.1),而无法获取到用户的真实ip地址,在这种情况下,后端是Apache如何获取用户真实IP地址?

Nginx配置如下:

location / {
        try_files $uri @apache;
        }

location @apache {
        internal;
        proxy_pass http://127.0.0.1:8080;
        include proxy.conf;
        }

location ~ .*\.(php|php5)?$  {
        proxy_pass http://127.0.0.1:8080;
        include proxy.conf;
        }
proxy_connect_timeout 300s;
proxy_send_timeout 900;
proxy_read_timeout 900;
proxy_buffer_size 32k;
proxy_buffers 4 64k;
proxy_busy_buffers_size 128k;
proxy_redirect off;
proxy_hide_header Vary;
proxy_set_header Accept-Encoding '';
proxy_set_header Referer $http_referer;
proxy_set_header Cookie $http_cookie;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
获取真实IP地址有Apache有2个模块:
mod_rpaf:Apache-2.2支持;Apache-2.4不支持。网上教程很多
mod_remoteip:Apache-2.4自带模块;Apache-2.2支持;推荐

Apache-2.2.25
mod_rpaf模块

wget http://stderr.net/apache/rpaf/download/mod_rpaf-0.6.tar.gz
tar -xzvf mod_rpaf-0.6.tar.gz
cd mod_rpaf-0.6/
/usr/local/apache/bin/apxs  -i -c -n mod_rpaf-2.0.slo mod_rpaf-2.0.c
添加Apache配置

vi  /usr/local/apache/conf/httpd.conf
Include conf/extra/httpd-rpaf.conf
vi /usr/local/apache/conf/extra/httpd-rpaf.conf

LoadModule rpaf_module        modules/mod_rpaf-2.0.so
RPAFenable On
RPAFsethostname On
RPAFproxy_ips 127.0.0.1 10.8.0.110 # 代理服务器的ip地址(记得做相应修改)
RPAFheader X-Forwarded-For
备注:RPAFproxy_ips后面添加代理服务器的ip地址,有几个填几个
测试

# /usr/local/apache/bin/apachectl -t
# /usr/local/apache/bin/apachectl restart
# 看日志
mod_remoteip
Apache-2.2下配置mod_remoteip如下:
安装

wget https://github.com/ttkzw/mod_remoteip-httpd22/raw/master/mod_remoteip.c
/usr/local/apache/bin/apxs -i -c -n mod_remoteip.so mod_remoteip.c
修改配置文件:

vi /usr/local/apache/conf/httpd.conf
Include conf/extra/httpd-remoteip.conf
vi /usr/local/apache/conf/extra/httpd-remoteip.conf
LoadModule remoteip_module modules/mod_remoteip.so
RemoteIPHeader X-Forwarded-For
RemoteIPInternalProxy 127.0.0.1
测试:

# /usr/local/apache/bin/apachectl -t
# /usr/local/apache/bin/apachectl restart
# 看日志
Apache-2.4配置mod_remoteip除了上面(自带mod_remoteip模块不需要安装),还需要修改日志格式(折腾很久)

LogFormat "%h %a %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined
LogFormat "%h %a %l %u %t \"%r\" %>s %b" common
LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\" %I %O" combinedi
在日志格式中加上%a

摘自:https://blog.linuxeye.com/378.html

Q:这里是老大的教程,但最后有一个修改日志格式的教程,请问日志格式在哪修改,文件路径是什么啊?
那最后面 Apache-2.4配置mod_remoteip除了上面(自带mod_remoteip模块不需要安装),还需要修改日志格式(折腾很久),这里的日志格式在哪里修改呢,谢谢?
A:请看这文章:http://blog.sina.com.cn/s/blog_672c5a470100xj7z.html
背景:想要做一个高性能的框架也好,其实还是站在内核上面跳舞,为此,其对内核的设置很重要,就以swoole来讲,其queque是基于epoll,进程间通讯用到内核的que,当然要设置它 了,再就是消息队列这块的通讯,是不是也得设置,这块还有高并发的句柄这块,也需要设置为更大。这块linux下的epoll和freebsd的kqueque都要设置一下,否则,用不上内核的能力也就问题大了,swoole的nginx配置到swoole的通讯用unix socket dgram,也需要配置,你可能说这个框架性能不行了,其实性能是一个综合体,不是一个框架就能说明的。

当使用消息队列作为进程间通信方式时,需要调整此内核参数

kernel.msgmnb = 4203520,消息队列的最大字节数
kernel.msgmni = 64,最多允许创建多少个消息队列
kernel.msgmax = 8192,消息队列单条数据最大的长度

————————————————————————————
ulimit设置

ulimit -n 要调整为100000甚至更大。 命令行下执行 ulimit -n 100000即可修改。如果不能修改,需要设置 /etc/security/limits.conf,加入

* soft nofile 262140
* hard nofile 262140
root soft nofile 262140
root hard nofile 262140
* soft core unlimited
* hard core unlimited
root soft core unlimited
root hard core unlimited
内核设置

net.unix.max_dgram_qlen = 100

swoole使用unix socket dgram来做进程间通信,如果请求量很大,需要调整此参数。系统默认为10,可以设置为100或者更大。
或者增加worker进程的数量,减少单个worker进程分配的请求量。

net.core.wmem_max

修改此参数增加socket缓存区的内存大小

net.ipv4.tcp_mem  =   379008       505344  758016
net.ipv4.tcp_wmem = 4096        16384   4194304
net.ipv4.tcp_rmem = 4096          87380   4194304
net.core.wmem_default = 8388608
net.core.rmem_default = 8388608
net.core.rmem_max = 16777216
net.core.wmem_max = 16777216
net.ipv4.tcp_tw_reuse

是否socket reuse,此函数的作用是Server重启时可以快速重新使用监听的端口。如果没有设置此参数,会导致server重启时发生端口未及时释放而启动失败

net.ipv4.tcp_tw_recycle

使用socket快速回收,短连接Server需要开启此参数

消息队列设置

当使用消息队列作为进程间通信方式时,需要调整此内核参数

kernel.msgmnb = 4203520,消息队列的最大字节数
kernel.msgmni = 64,最多允许创建多少个消息队列
kernel.msgmax = 8192,消息队列单条数据最大的长度

开启CoreDump

设置内核参数

kernel.core_pattern = /data/core_files/core-%e-%p-%t
通过ulimit -c命令查看当前coredump文件的限制

ulimit -c
如果为0,需要修改/etc/security/limits.conf,进行limit设置。

开启core-dump后,一旦程序发生异常,会将进程导出到文件。对于调查程序问题有很大的帮助
其他重要配置

net.ipv4.tcp_syncookies=1
net.ipv4.tcp_max_syn_backlog=81920
net.ipv4.tcp_synack_retries=3
net.ipv4.tcp_syn_retries=3
net.ipv4.tcp_fin_timeout = 30
net.ipv4.tcp_keepalive_time = 300
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_tw_recycle = 1
net.ipv4.ip_local_port_range = 20000 65000
net.ipv4.tcp_max_tw_buckets = 200000
net.ipv4.route.max_size = 5242880
查看配置是否生效

如:修改net.unix.max_dgram_qlen = 100后,通过

cat /proc/sys/net/unix/max_dgram_qlen
如果修改成功,这里是新设置的值。

摘自:http://wiki.swoole.com/wiki/page/11.html
本地socket--SOCK_DGRAM方式:http://blog.csdn.net/shanzhizi/article/details/16883601
背景:我是一个同事,操作一些命令后出现这个问题,经过chmod 755 / 问题得到解决。


客户一个oracle突然当机了,由于业务启动,客户下意识的重启了服务器,系统是起来了,准备切换到oracle用户下启动数据库,可以怎么都无法su切换,真是火上浇油呀,描述如下:
在root用户下,su到一个普通用户oracle,得到如下错误:

[root@localhost ~]# su -  oracle
su: warning: cannot change directory to /home/oracle: Permission denied
su: /bin/bash: Permission denied

而oracle用户也无法通过直接登录,出现同样错误。

这是一个非常奇怪的问题,到底是什么导致的呢?思路如下:
1,程序执行权限问题
2,程序依赖的共享库权限问题
3,目录权限问题
4,根空间问题。

检查/bin/bash,权限正确,检查/home/oracle权限正确,检查/lib/ld-***.so,权限也正确。

继续调试,检查/etc/passwd,将oracle的home设置为/tmp,把/tmp设置为777,这个权限应该是最宽松的。

而su出现同样的错误。

也就是oracle用户无法访问777权限的/tmp。

问题到底出现在哪里呢?

最后
通过star命令,看到了问题根本,
[root@localhost ~]#stat  /
输出如下:因为你ls是看不到的。
  File: “/”
  Size: 1024            Blocks: 2          IO Block: 1024   目录
Device: 803h/2051d      Inode: 2           Links: 22
Access: (0666/drw-rw-rw-)  Uid: (    0/    root)   Gid: (    0/    root)
Access: 2007-12-01 22:28:48.000000000 +0800
Modify: 2007-12-01 22:28:34.000000000 +0800
Change: 2007-12-01 23:17:35.000000000 +0800

问题出来了,这里的权限是错误的,X权限的丢失造成的。

[root@localhost ~]#chmod 755 /


修改后,问题消失。

产生上述问题的方法:
第一种,chmod 666 /,可以导致。

或者,
第二种,chmod 700 /lib/ld-xxxx.so,也可以导致su失败。

有兴趣可以自己试一下。

/ 权限的丢失对于各种运行在自己用户身份上的daemon也存在同样的影响。
原文链接: http://www.ixdba.net/a/mz/2010/0421/14.html
背景:c语言把整数写入文件,并读出,C语言写整数到文件用啥函数? 我看 fwrite 是写char*的。这个在调试队列时可以用上,有时入队出队不一致时,通过入队写了多少行出队写了多少行就知道到底队列程序在数据量大时有没有问题。




摘自:http://blog.163.com/song_0803/blog/static/4609759720112231158527/

入队写文件:


出队写文件:





整个换行到里面去,否则全是888不太好用vim 打开直接set nu看多少行:


w+以纯文本方式读写,而wb+是以二进制方式进行读写。

mode说明:
w 打开只写文件,若文件存在则文件长度清为0,即该文件内容会消失。若文件不存在则建立该文件。
w+ 打开可读写文件,若文件存在则文件长度清为零,即该文件内容会消失。若文件不存在则建立该文件。
wb 只写方式打开或新建一个二进制文件,只允许写数据。
wb+ 读写方式打开或建立一个二进制文件,允许读和写。
r 打开只读文件,该文件必须存在,否则报错。
r+ 打开可读写的文件,该文件必须存在,否则报错。
rb+ 读写方式打开一个二进制文件,只允许读写数据。
a 以附加的方式打开只写文件。若文件不存在,则会建立该文件,如果文件存在,写入的数据会被加到文件尾,即文件原先的内容会被保留。(EOF符保留)
a+ 以附加方式打开可读写的文件。若文件不存在,则会建立该文件,如果文件存在,写入的数据会被加到文件尾后,即文件原先的内容会被保留。 (原来的EOF符不保留)
ab+ 读写打开一个二进制文件,允许读或在文件末追加数据。
加入b 字符用来告诉函数库打开的文件为二进制文件,而非纯文字文件。
背景:有时PHP像python那样可交互能对小代码片段的验证提高效果,但默认编译好像是不行的。
[root@localhost htdocs]# php -i | grep readline
[root@localhost htdocs]# 主要的差異在於 Compile 時, 有沒有加入 "--with-readline",

试着安一个扩展:
readline 相关错误
现象:configure: error: Please reinstall libedit – I cannot find readline.h
解决办法:安装 Editline Library (libedit),官网:http://thrysoee.dk/editline/
下载最新版 libedit 编译安装即可。
直载后,直接:configure && make && make install

正确安装如下:
#php -a
Interactive shell

php > echo "hello" ;
hello

不对的输出如下,输入 php -a 之后,基本就没有反应了。:
#php -a
Interactive mode enabled

解决方法:
重新编译安装php,加入--with-readline选项。
php -i | grep "\-\-with\-readline" --color
不正确的输出是空,正确的输出编译项里有。
_____________________________________________________________________________
./configure --with-readline
configure: WARNING: You will need re2c 0.13.4 or later if you want to regenerate PHP parsers.


./configure
make && make install

[root@iZ25dcp92ckZ readline]# make install
Installing shared extensions:     /usr/local/php/lib/php/extensions/no-debug-non-zts-20100525/

安上了,但还是失败了:
[root@iZ25dcp92ckZ readline]# php -a
php > echo "helo";
php: symbol lookup error: /usr/local/php/lib/php/extensions/no-debug-non-zts-20100525/readline.so: undefined symbol: append_history

这儿有一个兄弟也在问一样的问题,说是:php 的 readline 模块和所使用的 readline 库的版本差异太大了。
摘自:http://segmentfault.com/q/1010000002455369
——————————————————————————————————————————————————————


0 === false???

php > var_dump( 0 === false);
bool(false)
php > $res =0;
php > var_dump( $res === false);
bool(false)



php -a
Interactive mode enabled

<?php
echo time() . "\n";
$a = 1;
echo $a;
?> Ctrl + d
1440560402
1





————————————————————————————————————————————————————
臨時要測個簡單的程式片段 或 Function, 一般都會於 CLI 寫來跑一跑, Interactive 模式 可以邊寫邊測試~ (互動模式, 寫完一行就 Compile 一行)

而 PHP CLI 的模式有兩種差異: (執行方法: $ php -a, 說明可見: PHP: Interactive shell)

Interactive shell 比較像是 互動式, 有問有答的執行程式.
Interactive mode enabled 則是輸入一整個區塊的程式碼, Ctrl-D 執行.

目前 (2012年) Ubuntu Linux 內建預設的是 Interactive shell, 而 Debian 是 Interactive mode enabled.

主要的差異在於 Compile 時, 有沒有加入 "--with-readline", 可使用 "php -i | grep readline" 查看.

Interactive mode enabled

$ php -a
Interactive mode enabled

<?php
echo time() . "\n";
$a = 1;
echo $a;
?> Ctrl + d
1330954647
1

Interactive shell

$ php -a
Interactive shell

php > echo time();
1330954675
php > $a = 1;
php > echo $a;
1

来自:http://blog.longwin.com.tw/2012/03/php-interactive-shell-2012/
解决办法就是重新编译并安装一次加上参数:
http://blog.csdn.net/pqhdp/article/details/9386185



在php Interactive mode里敲代码不需要<?php ?>的啊,直接输入echo "hello"; 回车就可以了。
还有php现在有一个还不错的repl实现
https://github.com/borisrepl/boris
比 php -a 要更舒服一些。

演示:http://segmentfault.com/a/1190000000353069
需要git。


—————————Centos安装git并装Boris不用重新编译PHP实现类python命令行交互。—————————
[root@localhost boris-1.0.10]# yum install git
Loading "fastestmirror" plugin
Loading mirror speeds from cached hostfile
* base: 10.64.5.100
* updates: 10.64.5.100
* addons: 10.64.5.100
* extras: 10.64.5.100
base                      100% |=========================| 1.1 kB    00:00    
updates                   100% |=========================| 1.9 kB    00:00    
primary.sqlite.bz2        100% |=========================| 504 kB    00:00    
addons                    100% |=========================| 1.9 kB    00:00    
extras                    100% |=========================| 2.1 kB    00:00    
Setting up Install Process
Parsing package install arguments
No package git available.
Nothing to do
[root@localhost boris-1.0.10]# cat /etc/redhat-release
CentOS release 5.2 (Final)
[root@localhost boris-1.0.10]# rpm -Uvh http://dl.fedoraproject.org/pub/epel/5/x86_64/epel-release-5-4.noarch.rpm
Retrieving http://dl.fedoraproject.org/pub/epel/5/x86_64/epel-release-5-4.noarch.rpm
warning: /var/tmp/rpm-xfer.Q9d5Ba: Header V3 DSA signature: NOKEY, key ID 217521f6
Preparing...                ########################################### [100%]
   1:epel-release           ########################################### [100%]
[root@localhost boris-1.0.10]# yum install git        
Loading "fastestmirror" plugin
Loading mirror speeds from cached hostfile

yum install git
Total download size: 7.5 M
Is this ok [y/N]: y
Downloading Packages:
(1/4): git-1.8.2.1-1.el5.  20% |=====                    | 1.5 MB    00:12 ETA

安git参看:http://www.letuknowit.com/post/19.html,为何要安?是因为想用它安装PHP的Boris——PHP也有REPL。
演示:http://segmentfault.com/a/1190000000353069  

你可以通过 Packagist 来安装 Boris。当然你也可以直接克隆它的 git 仓库:

git clone git://github.com/d11wtq/boris.git
cd boris
./bin/boris
Boris以MIT许可证发布。更多信息请访问Boris在GitHub上的项目主页。

[root@localhost local]# git clone git://github.com/d11wtq/boris.git
Cloning into 'boris'...
remote: Counting objects: 1061, done.
remote: Total 1061 (delta 0), reused 0 (delta 0), pack-reused 1061
Receiving objects: 100% (1061/1061), 204.32 KiB | 62 KiB/s, done.
Resolving deltas: 100% (506/506), done.


[root@localhost local]# cd boris
[root@localhost boris]# ./bin/boris

背景:从某种意义来讲用memcache做缓存已经相当成熟,对于中小型的一些高并发也是可以的,但是伴随微博的兴起(现在好像不行了),有了No-sql这样一种东西出来,但究其从底层来讲和memcache不同在memcache是基于libevent的主进程下用pipe进行通讯的多线程机制,可能是不需要锁的还提供原子锁,而那个redis呢是基于epoll啥的事件驱动的异步的(好像是柱塞的,我再查下:是非阻塞IO ,epoll加自己实现的事件框架,redis实际上是采用了线程封闭的观念,把任务封闭在一个线程,自然避免了线程安全问题,不过对于需要依赖多个redis操作的复合操作来说,依然需要锁,而且有可能是分布式锁,这块不太明白,反正有利有弊吧,有了锁性能肯定不是太好。)一个单进程在跑,其规避了memcache的全放内存,而是隔一会儿会刷新到那个较为活跃的数据到硬盘里,还支持一部分的数据结构,结构化数据可能会影响其性能,其稳定性和高效来讲,我个人觉得和memcache差不多(mm略高一丝),但对大的粒度的存储来讲,可能mm性能更好,而对于小粒度那当然是redis了,这是因为一个是基于多线程和单进程,二是epoll这玩意好像生来就有这个对大粒度的东西不是太在行,比如:apache之于用epoll写的nginx,对于长时间的重逻辑处理来讲还是apache稳定,至于为什么,这个是实践出来的。获取数据和设置数据在协议上memcache是字符的,redis是二进制???
好了,下载一个最新稳定版本的吧:
http://download.redis.io/releases/redis-3.0.3.tar.gz
http://download.redis.io/releases/redis-stable.tar.gz
解压缩: tar -zxvf redis-3.0.3.tar.gz
cd redis-3.0.3
$ make
$make install
$cp redis.conf /etc/


Q:默认目录:/usr/local/bin 我不想装这儿啊。怎么办?
A:make完  不要install了 手工做
make完了之后,cd utils,里面有个install_server.sh,执行的时候会问你要安装到哪个路径
./install_server.sh



参数介绍:
make install命令执行完成后,会在/usr/local/bin目录下生成本个可执行文件,分别是redis-server、redis-cli、redis-benchmark、redis-check-aof 、redis-check-dump
redis-server:Redis服务器的daemon启动程序
redis-cli:Redis命令行操作工具。也可以用telnet根据其纯文本协议来操作
redis-benchmark:Redis性能测试工具,测试Redis在当前系统下的读写性能
redis-check-aof:数据修复
redis-check-dump:检查导出工具
修改系统配置文件,执行命令
a) echo vm.overcommit_memory=1 >> /etc/sysctl.conf
b) sysctl vm.overcommit_memory=1 或执行echo vm.overcommit_memory=1 >>/proc/sys/vm/overcommit_memory
修改redis配置文件
a) $ cd /etc
b) vi redis.conf
c) 修改daemonize yes---目的使进程在后台运行
启动redis
a) $ cd /usr/local/bin
b) ./redis-server /etc/redis.conf
/usr/local/bin/redis-server /etc/redis.conf
/usr/local/redis/bin/redis-server /usr/local/redis/etc/redis.conf
7. 检查是否启动成功
a) $ ps -ef | grep redis

果然是单进程且是epoll:
[root@localhost bin]# ps -ef | grep redis
root       317     1  0 17:20 ?        00:00:00 ./redis-server *:6379
[root@localhost bin]# strace  -f -p 317
Process 317 attached with 3 threads - interrupt to quit
[pid   325] futex(0x6e6c54, FUTEX_WAIT_PRIVATE, 1, NULL <unfinished ...>
[pid   324] futex(0x6e6c24, FUTEX_WAIT_PRIVATE, 1, NULL <unfinished ...>
[pid   317] epoll_wait(3, {}, 10128, 3) = 0
[pid   317] open("/proc/317/stat", O_RDONLY) = 6
[pid   317] read(6, "317 (redis-server) R 1 317 317 0"..., 4096) = 233
[pid   317] close(6)                    = 0
[pid   317] epoll_wait(3, {}, 10128, 100) = 0
[pid   317] open("/proc/317/stat", O_RDONLY) = 6
[pid   317] read(6, "317 (redis-server) R 1 317 317 0"..., 4096) = 233
[pid   317] close(6)                    = 0
[pid   317] epoll_wait(3, {}, 10128, 100) = 0
[pid   317] open("/proc/317/stat", O_RDONLY) = 6
[pid   317] read(6, "317 (redis-server) R 1 317 317 0"..., 4096) = 233
[pid   317] close(6)                    = 0
[pid   317] epoll_wait(3,  <unfinished ...>
PHP是最好的语言,那安装个PHP扩展,选个自己的PHP版本的扩展:
https://github.com/nicolasff/phpredis/downloads
[root@localhost software]# php -v
PHP 5.3.10 (cli) (built: Jun  4 2013 11:51:25)
Win:https://github.com/downloads/phpredis/phpredis/php_redis-5.3-vc9-ts-73d99c3e.zip
*nix: https://github.com/phpredis/phpredis
        https://github.com/phpredis/phpredis/releases
Linux代码  收藏代码
$  tar zxvf nicolasff-phpredis-2.2.2-48-g7dfac44.tar.gz  
$ cd  nicolasff-phpredis-7dfac44/  
$ /usr/local/webserver/php/bin/phpize  
$ ./configure --with-php-config=/usr/local/webserver/php/bin/php-config  
$ make  
$ make install  
3 修改php.ini
    查看/usr/local/webserver/php/lib/php/extensions/no-debug-non-zts-20060613是否有redis.so
[root@localhost phpredis-2.2.7]# make install
Installing shared extensions:     /usr/local/php/lib/php/extensions/no-debug-non-zts-20090626/
在php.ini中 添加  
  extension=redis.so
/usr/local/php/lib/php/extensions/no-debug-non-zts-20090626/redis.so
[root@localhost phpredis-2.2.7]# php -i|grep php.ini
Configuration File (php.ini) Path => /usr/local/php/etc
Loaded Configuration File => /usr/local/php/etc/php.ini
[root@localhost phpredis-2.2.7]# vi /usr/local/php/etc/php.ini
4 重启php进程
  $  /usr/local/webserver/php/sbin/php-fpm restart
5 查看phpinfo() 是否有redis扩展

6 用PHP文件测试
Php代码

输出:Hello World
成功!
参考来自:http://alfred-long.iteye.com/blog/1685092
redis server安装部分摘自并参考:http://www.cnblogs.com/zhuhongbao/archive/2013/06/04/3117997.html

开机启动,没试过,需要实践:

把上述代码存为redis,放到/etc/init.d/下面
[html] view plaincopy在CODE上查看代码片派生到我的代码片
chmod +x /etc/init.d/redis  
设定开机启动服务
sudo chkconfig redis on  
启动,停止redis
service redis start   #或者 /etc/init.d/redis start  
service redis stop   #或者 /etc/init.d/redis stop  
测试redis
redis-cli  
redis 127.0.0.1:6379> set foo 123  
OK  
redis 127.0.0.1:6379> get foo  
"123"  
redis 127.0.0.1:6379> exit  
背景:做温度比较时有用。
PHP如何比较两个浮点数的大小?

在程序开发中,通常有会涉及到两个大浮点数的比较:如:1.9999999999999999999981.999999999999999999999这样两个大精度的数的比较方法除了以字符串的形式来比较,还有其它方式比较吗?php的bccomp比较出来是相等的。


注意第三个参数

$a = '1.999999999999999999998';
$b = '1.999999999999999999999';
$flag = bccomp($a, $b, 21);
var_dump($flag); // int(-1) 说明b比a大
exit;


From:http://baike.1688.com/doc/view-d35956820.html
背景:用树莓派下的chromium调试confirm阻止此页面创建其他对话框的解决方法







用Firefox操作弹出界面时总是遇到“firefox 阻止此页面创建其他对话框”,点击确定后,控制台就会报错误.

解决方法:

1. 在firefox里输入about:config

2. 在列表框里右键->新建->整数

3.输入选项名dom.successive_dialog_time_limit,值设为0,点确认。
echo exec('whoami')
背景:想在树莓派上使用,Raspberry Pi下的chromium没有声音,但在电脑上的FF和Chrome有声音。


Demo:


来自:http://blog.csdn.net/fdipzone/article/details/8170337
每天做得最多的就是用VIM打开文件、保存文件。使用VIM编辑文件时我习惯用'x'命令在保存并退出。而我的一个同事习惯用'wq'命令保存并退出,有一次他问我用'x'和'wq'有什么区别吗?我当时答不出来。

今天我在用'x'命令保存退出时发现了一个有趣的地方,保存退出后有时会显示 "filename" 1L, 10C 已写入  这么一行,有时又不显示。多试了几次才发现在没有修改文件时用'x'命令保存退出时不会显示 "filename" 1L, 10C 已写入 ,文件的修改时间也不会发生变化。有修改时则会显示这一行,而且文件的修改时间也会随之更新。接着我又使用'wq'命令保存退出时终于发现了点问题:不管我是否修改了文件内容,用'wq'命令保存退出时都会显示 "filename" 1L, 10C 已写入 ,并更新文件的修改时间。呵呵原来"x"与"wq"的的真正区别在这里:

wq   强制性写入文件并退出。即使文件没有被修改也强制写入,并更新文件的修改时间。
x    写入文件并退出。仅当文件被修改时才写入,并更新文件修改时间,否则不会更新文件修改时间。


From:http://blog.chinaunix.net/uid-12115233-id-3268894.html
分页: 49/272 第一页 上页 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 下页 最后页 [ 显示模式: 摘要 | 列表 ]