竹磬网-邵珠庆の日记 生命只有一次,你可以用它来做些更多伟大的事情–Make the world a little better and easier


2111月/160

深入理解ob_flush和flush的区别和用法)

发布在 邵珠庆

有关PHP的ob_flush()与flush()使用方法

注意:ob_flush()和flush()这两个函数一般要一起使用,顺序是先ob_flush(),然后flush(),它们的作用是刷新缓冲区。
这里具体的说下什么时候要用到刷新缓冲区和为什么要刷新缓冲区。

一、什么时候要刷新缓冲区

当程序中用到file_get_contents()和file_put_contens()这两个函数时,或程序中执行类似的“读写”功能或向浏览器执行输出操作时,会用到ob_flush()和flush()来刷新缓冲区。

二、为什么要刷新缓冲区

用file_get_contents()和file_put_content()为例进行讲解。

file_get_contents()和file_put_conents()这两个函数分别执行读取数据和写入数据操作,数据是先被读到内存中然后在写入文件中的,因为读取的速度比写入的速度要快,所以当你的数据被读完的时候不代表数据也写入完毕,这个时候多读的内容就会被暂时放到缓冲区中(内存),在这里需要强调一下,其实数据读取和写入是两个非常快的动作哦。

还用一种解释(当程序向浏览器执行输出操作时),个别web服务器程序,特别是Win32下的web服务器程序,在发送结果到浏览器之前,仍然会缓存脚本的输出,直到程序结束为止。如果你不想让程序执行完毕才向浏器输出,那么你也可以用到ob_flush()和flush()来刷新缓存。

其实,flush()还有一种用途,就是在没结束程序之前就进行输出,即一个循环还没结束就可以把部分结果输出到浏览器上,这个效果很类似 ajax的异步传输效果。

深入理解ob_flush和flush的区别

ob_flush/flush在手册中的描述, 都是刷新输出缓冲区, 并且还需要配套使用, 所以会导致很多人迷惑… 

其实, 他们俩的操作对象不同, 有些情况下, flush根本不做什么事情.. 

ob_*系列函数, 是操作PHP本身的输出缓冲区. 

所以, ob_flush是刷新PHP自身的缓冲区. 

而flush, 严格来讲, 这个只有在PHP做为apache的Module(handler或者filter)安装的时候, 才有实际作用. 它是刷新WebServer(可以认为特指apache)的缓冲区. 

在apache module的sapi下, flush会通过调用sapi_module的flush成员函数指针, 间接的调用apache的api: ap_rflush刷新apache的输出缓冲区, 当然手册中也说了, 有一些apache的其他模块, 可能会改变这个动作的结果.. 

有些Apache的模块,比如mod_gzip,可能自己进行输出缓存,这将导致flush()函数产生的结果不会立即被发送到客户端浏览器。 

甚至浏览器也会在显示之前,缓存接收到的内容。例如 Netscape浏览器会在接受到换行或 html 标记的开头之前缓存内容,并且在接受到 </table> 标记之前,不会显示出整个表格。 

一些版本的 Microsoft Internet Explorer 只有当接受到的256个字节以后才开始显示该页面,所以必须发送一些额外的空格来让这些浏览器显示页面内容所以, 正确使用俩者的顺序是. 先ob_flush, 然后flush, 

当然, 在其他sapi下, 不调用flush也可以, 只不过为了保证你代码的可移植性, 建议配套使用.

 


buffer ---- flush()
buffer是一个内存地址空间,Linux系统默认大小一般为4096(1kb),即一个内存页。主要用于存储速度不同步的设备或者优先级不同的 设备之间传办理数据的区域。通过buffer,可以使进程这间的相互等待变少。这里说一个通俗一点的例子,你打开文本编辑器编辑一个文件的时候,你每输入 一个字符,操作系统并不会立即把这个字符直接写入到磁盘,而是先写入到buffer,当写满了一个buffer的时候,才会把buffer中的数据写入磁 盘,当然当调用内核函数flush()的时候,强制要求把buffer中的脏数据写回磁盘。
同样的道理,当执行echo,print的时候,输出并没有立即通过tcp传给客户端浏览器显示, 而是将数据写入php buffer。php output_buffering机制,意味在tcp buffer之前,建立了一新的队列,数据必须经过该队列。当一个php buffer写满的时候,脚本进程会将php buffer中的输出数据交给系统内核交由tcp传给浏览器显示。所以,数据会依次写到这几个地方echo/pring -> php buffer -> tcp buffer -> browser

php output_buffering --- ob_flush()

默认情况下,php buffer是开启的,而且该buffer默认值是4096,即1kb。你可以通过在php.ini配置文件中找到output_buffering配置.当echo,print等输出用户数据的时候,输出数据都会写入到php output_buffering中,直到output_buffering写满,会将这些数据通过tcp传送给浏览器显示。你也可以通过 ob_start()手动激活php output_buffering机制,使得即便输出超过了1kb数据,也不真的把数据交给tcp传给浏览器,因为ob_start()将php buffer空间设置到了足够大 。只有直到脚本结束,或者调用ob_end_flush函数,才会把数据发送给客户端浏览器。

这两个函数的使用怕是很多人最迷惑的一个问题,手册上对两个函数的解释也语焉不详,没有明确的指出它们的区别,似乎二者的功能都是刷新输出缓存。但在我们文章一开始的代码中如果讲fush()替换成ob_flush(),程序就再不能正确执行了。显然,它们是有区别的,否则也手册中直接说明其中一个是另外一个函数的别名即可了,没必要分别说明。那么它们的区别到底是什么呢?

在没有开启缓存时,脚本输出的内容都在服务器端处于等待输出的状态 ,flush()可以将等待输出的内容立即发送到客户端。

开启缓存后,脚本输出的内容存入了输出缓存中 ,这时没有处于等待输出状态的内容,你直接使用flush()不会向客户端发出任何内容。而 ob_flush()的作用就是将本来存在输出缓存中的内容取出来,设置为等待输出状态,但不会直接发送到客户端 ,这时你就需要先使用 ob_flush()再使用flush(),客户端才能立即获得脚本的输出。

一. flush和ob_flush的正确顺序,正确应是,先ob_flush再flush,如下: 
ob_flush();
flush();
如果Web服务器的操作系统是windows系统,那顺序颠倒或者不使用ob_flush()也不会出现问题。[有待求证 ] 但是在Linux系统上就无法刷新输出缓冲。

output buffering函数
1.bool ob_start ([ callback $output_callback [, int $chunk_size [, bool $erase ]]] )
激活output_buffering机制。一旦激活,脚本输出不再直接出给浏览器,而是先暂时写入php buffer内存区域。
php默认开启output_buffering机制,只不过,通过调用ob_start()函数据output_buffering值扩展到足够 大 。也可以指定$chunk_size来指定output_buffering的值。$chunk_size默认值是0,表示直到脚本运行结束,php buffer中的数据才会发送到浏览器。如果你设置了$chunk_size的大小 ,则表示只要buffer中数据长度达到了该值,就会将buffer中 的数据发送给浏览器。
当然,你可以通过指定$ouput_callback,来处理buffer中的数据。比如函数ob_gzhandler,将buffer中的数据压缩后再传送给浏览器。
第三个参数:是否擦除缓存,可选,默认是true,如果设置为false,则在脚本执行结束前,缓存都不会被清除。
2.ob_get_contents
获取一份php buffer中的数据拷贝。值得注意的是,你应该在ob_end_clean()函数调用前调用该函数,否则ob_get_contents()返回一个空字符中。

可以使用ob_get_contents()以字符串形式获取服务端缓存的数据,
使用ob_end_flush()则会输出被缓存起来的数据,并关闭缓存。
而使用ob_end_clean()则会静默的清除服务端缓存的数据,而不会有任何数据或其他行为。
服务端的缓存是堆叠起来的,也就是说你在开启了ob_start()后,关闭之前,在其内部还 可以开启另外一个缓存ob_start()。

不过你也要务必保证关闭缓存的操作和开启缓存的操作数量一样多。 
ob_start() 可以指定一个回调函数来处理缓存数据,如果一个ob_start()内部嵌套了另一个ob_start(),我们假定,外层的ob_start(),编号是A,内层的ob_start()编号是B,它们各自制定了一个回调函数分别是functionA和functionB,那么在缓存B中的数据输出时,它会先辈funcitonB回调函数处理,再交给外层的functionA回调函数处理,之后才能输出到客户端。

另外,手册说,对于某些web服务器,比如apache,在使用回调函数有可能会改变程序当前的工作目录,解决方法是在回调函数中自行手动把工作目录修改回来,用chdir函数,这点似乎不常遇到,遇到的时候记得去查手册吧。

3.ob_end_flush与ob_end_clean
这二个函数有点相似,都会关闭ouptu_buffering机制。但不同的是,ob_end_flush只是把php buffer中的数据冲(flush/send)到客户端浏览器,而ob_clean_clean将php bufeer中的数据清空(erase),但不发送给客户端浏览器。

ob_end_flush调用之前 ,php buffer中的数据依然存在,ob_get_contents()依然可以获取php buffer中的数据拷贝。

而ob_end_flush()调用之后 ob_get_contents()取到的是空字符串,同时浏览器也接收不到输出,即没有任何输出。

可以使用ob_get_contents()以字符串形式获取服务端缓存的数据,使用ob_end_flush()则会输出被缓存起来的数据,并关闭缓存。
而使用ob_end_clean()则会静默的清除服务端缓存的数据,而不会有任何数据或其他行为。

 

 

ob_start() 和 ob_end_flush() 是一对很好的搭档,可以实现对输出的控制。当成一对出现理解起来就没什么问题,但是当他们两个各自出现次数增加时,就比较难理解了.

 

<?php ob_start(); echo 'level 1<br/> '; ob_start(); echo 'level 2<br/> '; ob_start(); echo 'level 3<br/> '; ob_end_flush(); ob_end_flush(); ob_end_flush();

 

很明显,结果为:

 

level 1
level 2
level 3

当程序修改一下,修改一个ob_end_flush() 变成 ob_end_clean() 成为以下这个,你觉得结果会是怎样呢?附上这几个函数的讲解:

 

  • ob_clean — 清空(擦掉)输出缓冲区
  • ob_end_clean — 清空(擦除)缓冲区并关闭输出缓冲
  • ob_end_flush — 冲刷出(送出)输出缓冲区内容并关闭缓冲
  • ob_flush — 冲刷出(送出)输出缓冲区中的内容
  • ob_start — 打开输出控制缓冲

 

 

<?php ob_start(); echo 'level 1<br/> '; ob_start(); echo 'level 2<br/> '; ob_start(); echo 'level 3<br/> '; ob_end_clean();//修改处 ob_end_flush(); ob_end_flush();

 

结果:

 

level 1
level 2

可能你会认为ob_end_clean()会清除与他最近的ob_start()的输出;其实这个说法不是很全面,看下面的例子

 

<?php ob_start(); echo 'level 1<br/> '; ob_start(); echo 'level 2<br/> '; ob_start(); echo 'level 3<br/> '; ob_end_clean(); //第一次修改 ob_end_flush(); ob_end_clean(); //第二次修改

 

这次,什么都没有输出来。

 

中间不是有一个ob_flush()吗?按理来说应该是输出  level2 的。

其实造成这样的主要原因是输出的多级缓冲机制。这个程序例子有三个ob_start(),就意味着他有3个缓冲区A,B,C,而其实php程序本身也有一个最终输出的缓冲区,我们就把他叫做F。

在这个程序中他这几个缓冲区是有一定层次的,C->B->A->F,F层次最高,是程序最终的输出缓冲,我们按上面的程序来进行讲解。

 

刚开始。  F:null 

 

ob_start();

 

 新建缓冲区A。  A: null -> F:null

 

echo 'level 1<br/> ';

 

程序有输出,输出进入最低的缓冲区A  A: 'level 1<br/>' -> F:null

 ob_start(); 

新建缓冲区B 。 B:null  ->  A: 'level 1<br/>' -> F:null

 

echo 'level 2<br/> ';

 

程序有输出,输出进入最低的缓冲区B     B:'level 2<br/> ' ->  A: 'level 1<br/>' ->F:null

 

ob_start();

 

新建缓冲区C   C:null  B:'level 2<br/> '   A: 'level 1<br/>' -> F:null

 

echo 'level 3<br/> ';

 

程序有输出,输出进入最低的缓冲区C    C:'level 3<br/> '  ->  B:'level 2<br/> '  ->  A: 'level 1<br/>' -> F:null

 

ob_end_clean(); //第一次修改

 

缓冲区C被清空并关闭。  B:'level 2<br/> '  ->  A: 'level 1<br/>' -> F:null

 

ob_end_flush();

 

缓冲区B输出到上一级的缓冲区A并关闭。   A: 'level 1<br/>level 2<br/> ' -> F:null

ob_end_clean(); //第二次修改

 缓冲区A被清空并关闭。 此时缓冲区A的东西还没真正输出到最终的F中,因此也就整个程序也就没有任何的输出了。

 ob其他的函数还有很多,但只要能懂得这些机理应该也是不难懂的。附上其余函数

 

  • flush — 刷新输出缓冲
  • ob_clean — 清空(擦掉)输出缓冲区
  • ob_end_clean — 清空(擦除)缓冲区并关闭输出缓冲
  • ob_end_flush — 冲刷出(送出)输出缓冲区内容并关闭缓冲
  • ob_flush — 冲刷出(送出)输出缓冲区中的内容
  • ob_get_clean — 得到当前缓冲区的内容并删除当前输出缓。
  • ob_get_contents — 返回输出缓冲区的内容
  • ob_get_flush — 刷出(送出)缓冲区内容,以字符串形式返回内容,并关闭输出缓冲区。
  • ob_get_length — 返回输出缓冲区内容的长度
  • ob_get_level — 返回输出缓冲机制的嵌套级别
  • ob_get_status — 得到所有输出缓冲区的状态
  • ob_gzhandler — 在ob_start中使用的用来压缩输出缓冲区中内容的回调函数。ob_start callback function to gzip output buffer
  • ob_implicit_flush — 打开/关闭绝对刷送
  • ob_list_handlers — 列出所有使用中的输出处理程序。
  • ob_start — 打开输出控制缓冲
  • output_add_rewrite_var — 添加URL重写器的值(Add URL rewriter values)
  • output_reset_rewrite_vars — 重设URL重写器的值(Reset URL rewriter values)
910月/120

GA自定义变量的16个有趣用法

发布在 邵珠庆

 

16 interesting ways to use Google Analytics custom variables

Copyright © TechPad.co.uk

 

早期迹象表明Google Analytics(GA)用户可能将能够存储多达50个自定义变量。下面是填充这些数值的一些主意!

设置自定义变量相对于GA中的其他事情来说稍微更复杂些,因为你需要懂一点服务器端的编程和SQL查询。

然而,这是非常有用并且值得的。自定义变量会给GA的配置文件中增加大量额外的数据,因为设置这些是值得的。

如果把高级细分和自定义变量配合使用,你会发现GA的作用增强上百倍。

如果你想尝试这个,查看我们的这个“通过PHP创建自定义变量”的指南。

50个自定义变量位置确实非常多,我想类似我这样的粉丝甚至很难填充满它们。然而,下面是着手去做的一些主意。

(注:后有博客更新:50个自定义变量只针对于商业版用户,免费GA用户仍然只能使用5个自定义变量)

1. 跟踪用户是否登录

当用户登录时,你的服务器端语言将创建一个变量会话来声明这点,以允许你识别用户是否已经登录。

如果你找到一个有效的变量会话,表明用户已经登录,创建一个自定义变量,该变量将会在结束时过期。

_gaq.push(['_setCustomVar',1,'Status','Logged-in',1]);

如果你找到一个有效的变量会话,表明用户未登录,创建一个自定义变量来标记他们为退出用户。

_gaq.push(['_setCustomVar',1,'Status','Logged-out',1]);

2. 跟踪会员与非会员

When a user successfully logs in, you know that they’re a member, so you create a custom variable which is persistent.当用户成功登录,你知道他们是会员,因此你创建一个持续性自定义变量。

_gaq.push(['_setCustomVar',2,'User','Member',2]);

下次他们返回时,即使他们没有登录,还是能够识别他们为会员。

对于其他人,你创建一个自定义变量表明他们是非会员。

_gaq.push(['_setCustomVar',2,'User','Non-Member',2]);

3. 跟踪作者的表现

想了解你网站的哪个作者吸引了最多的访客,与最多的用户互动并产生了最大的回报?

你可用通过创建一个包含作者名字的页面级自定义变量来实现。

_gaq.push(['_setCustomVar',3,'Author','Matt Clarke',3]);

4. 跟踪网站各个部分的表现

页面级自定义变量,正如第三条表明的是这个功能的最后一次声明,用它来归类网站各个部分表现的额外数据是非常方便的,而这单独通过标准的界面是很难跟踪的。

比如,如果你只想跟踪和细分你网站博客部分的访问(或者你网店鞋类产品部分客户的),创建一个类似这个的自定义变量。

_gaq.push(['_setCustomVar',4,'Section','Blog',3]);

类似的,如果你想存储正在浏览的子分类信息,只需创建一个这样的自定义变量。

_gaq.push(['_setCustomVar',5,'Subsection','Web analytics',3]);

5. 跟踪评论者

参与会话的用户与保持沉默的用户有何区别,你能从中学到什么以带动评论?

一旦他们发表一个评论,创建一个下面这样的自定义变量,然后你就可以跟踪他们他们整站的活动以及未来的访问。

_gaq.push(['_setCustomVar',6,'Commented','Yes',1]);

你将同时需要为那些未评论的人创建一个自定义变量,其中的值需要设置为NO,这允许你比较着两个组。

_gaq.push(['_setCustomVar',6,'Commented','No',1]);

6. 跟踪客户忠诚度

如果你在运营一个电子商务网站并且想基于购买次数分析客户的行为,那么尝试这个。

当这个客户达到支付页面,运行一段快速的SQL数据库查询以了解他们之前订购过多少订单。如果只是他们的首次下单,那么创建这个自定义变量。

_gaq.push(['_setCustomVar',7,'Customer','New customer',2]);

如果这是第二次下单,设置这个。

_gaq.push(['_setCustomVar',7,'Customer','Returning customer',2]);

如果他们下过两次订单或者更多,创建这个。

_gaq.push(['_setCustomVar',7,'Customer','Loyal customer',2]);

7.  跟踪单独的客户

这是一个 某些人觉得有些冒险的事情,但是它取决于你是否把客户ID,比如383328,当做可识别的个人信息(PII)。

Google坚持你不能存储可识别的个人信息(PII)到GA中,但是大多数人会说像3980933这样的客户ID,除非站长本身,其他人是无法识别为个人的,因此这并不会违反隐私条款或者带来安全隐患。

如果你想做这个,你只需要在客户登陆时把客户ID提取出来并添加到像这样的自定义变量中。

_gaq.push(['_setCustomVar',7,'CustomerID','5345435',2]);

8. Track Twitter users

要看看有多少Twitter用户价值,以及他们如何不同于其他客户或用户?当用户点击一个Tweet“关注”按钮,或从Twitter到您的网站,创建一个自定义的变量,以确定它们作为一个Twitter用户对自己网站未来的访问。

_gaq.push(['_setCustomVar',8,'Twitter user','Yes',2]);

9. 跟踪 Facebook用户

你可以做同样的事情,Facebook的用户,有一点稍微复杂的JavaScript。如果你能确定他们是否已经点击您喜欢或推荐按钮,或者已经通过Facebook.com被称为然后将它们添加到自定义的变量。

_gaq.push(['_setCustomVar',9,'Facebook user','Yes',2]);

10. 跟踪邮件订阅者

You could track email newsletter subscribers in a couple of ways.

You could bucket them after they sign up for your newsletter by writing the custom variable after they submit the form, or if you useutm_campaign tracking you could capture this from the URL, parse it and bucket them that way.

_gaq.push(['_setCustomVar',10,'Newsletter subscriber','Yes',2]);

11. 跟踪RSS订阅者

你的RSS订阅不同的反应呢?您可以跟踪他们在大致相同的方式,如果你标记您的电子邮件订阅你的RSS源与谷歌Analytics(分析)的广告系列跟踪变量。

_gaq.push(['_setCustomVar',11,'RSS subscriber','Yes',2]);

12. 跟踪促销活动表现

Running an offer on your home page and want to see whether users buy the product as a result? You could track the performance of this with event tracking if you use _trackEvent on the onclick.

However, the offer promotion won’t be recorded as the reason for the conversion if they buy the product on a subsequent visit. However, if you set a custom variable on the onclick it definitely could.

_gaq.push(['_setCustomVar',12,'Summer sale promo','Yes',2]);

13. 跟踪你帮助过的客户是否最终下单

 

大多数网站提供的在线客户服务,使客户可以联系他们通过在其网站上。

一些企业认为这是一个昂贵的服务提供,因为它可以出现,帮助这些客户并不总是导致的销售。

通过设置自定义变量时,提交表单你可以将它们标记为你的客户提供客户服务,通过您的电子邮件系统,你就可以将它们分组与其他测量响应。

_gaq.push(['_setCustomVar',13,'Customer service','Emailed',2]);

14. 跟踪投诉者是否回来

同样,如果你有一个客户服务的形式,其中包括一个下拉菜单,其中提供了各种选项和他们标志着他们的查询为投诉,您可以将它们添加到另一个自定义的变量。

_gaq.push(['_setCustomVar',14,'Complaint','Emailed',2]);

如果你结合起来,与_trackEvent(为便于跟踪),你就可以不仅能够监控你要多少宗投诉,但无论是客户抱怨以往任何时候都再次购物。

15.跟踪客户性别

如果你让用户告诉你他们是男性还是女性(未婚或已婚),你可以使用这个信息并把性别信息包含到设置的自定义变量中。

_gaq.push(['_setCustomVar',15,'Gender','Male',2]);

_gaq.push(['_setCustomVar',15,'Gender','Female',2]);

然后,你就可以下钻了解是否男性与女性会对你的网站做出不同的反应,这可能对你的营销团队或甚至你的设计师有用处。

16. 跟踪客户年龄

类似地,如果你询问用户,要求他们告诉你年龄(或者大概年龄),你还可以把这个值放入到自定义变量中。

_gaq.push(['_setCustomVar',16,'Age','16-25',2]);

是否某个年龄的女性喜欢你的某个产品,或者博客话题?把那个数据和其他自定义变量组合,你可以或者一些真正的买家数据透视。

1710月/1127

完全认识MySQL数据库中Show命令用法

发布在 邵珠庆

MySQL中有很多的基本命令,show命令也是其中之一,在很多使用者中对show命令的使用还容易产生混淆,本文汇集了show命令的众多用法。

a. show tables或show tables from database_name; -- 显示当前数据库中所有表的名称。

b. show databases; -- 显示mysql中所有数据库的名称。

c. show columns from table_name from database_name; 或show columns from database_name.table_name; -- 显示表中列名称。

d. show grants for user_name; -- 显示一个用户的权限,显示结果类似于grant 命令。

e. show index from table_name; -- 显示表的索引。

f. show status; -- 显示一些系统特定资源的信息,例如,正在运行的线程数量。

g. show variables; -- 显示系统变量的名称和值。

h. show processlist; -- 显示系统中正在运行的所有进程,也就是当前正在执行的查询。大多数用户可以查看他们自己的进程,但是如果他们拥有process权限,就可以查看所有人的进程,包括密码。

i. show table status; -- 显示当前使用或者指定的database中的每个表的信息。信息包括表类型和表的最新更新时间。

j. show privileges; -- 显示服务器所支持的不同权限。

k. show create database database_name; -- 显示create database 语句是否能够创建指定的数据库。

l. show create table table_name; -- 显示create database 语句是否能够创建指定的数据库。

m. show engies; -- 显示安装以后可用的存储引擎和默认引擎。

n. show innodb status; -- 显示innoDB存储引擎的状态。

o. show logs; -- 显示BDB存储引擎的日志。

p. show warnings; -- 显示最后一个执行的语句所产生的错误、警告和通知。

q. show errors; -- 只显示最后一个执行语句所产生的错误。

r. show [storage] engines; --显示安装后的可用存储引擎和默认引擎。

 

查整个库的状态:

select concat(truncate(sum(data_length)/1024/1024,2),’MB’) as data_size, concat(truncate(sum(max_data_length)/1024/1024,2),’MB’) as max_data_size, concat(truncate(sum(data_free)/1024/1024,2),’MB’) as data_free, concat(truncate(sum(index_length)/1024/1024,2),’MB’) as index_size from information_schema.tables where TABLE_SCHEMA = ‘databasename’;

查数据库所有表信息:

SELECT * FROM `information_schema`.`TABLES` WHERE `TABLE_SCHEMA`='数据库名‘;

查单表:

select concat(truncate(sum(data_length)/1024/1024,2),’MB’) as data_size, concat(truncate(sum(max_data_length)/1024/1024,2),’MB’) as max_data_size, concat(truncate(sum(data_free)/1024/1024,2),’MB’) as data_free, concat(truncate(sum(index_length)/1024/1024,2),’MB’) as index_size from information_schema.tables where TABLE_NAME = ‘tablename’;

1210月/1118

Mysql导出表结构及表数据 mysqldump用法

发布在 邵珠庆

命令行下具体用法如下:  mysqldump -u用戶名 -p密码 -d 數據库名 表名 脚本名;

    1、导出數據库為dbname的表结构(其中用戶名為root,密码為dbpasswd,生成的脚本名為db.sql)
    mysqldump -uroot -pdbpasswd -d dbname >db.sql;

    2、导出數據库為dbname某张表(test)结构
    mysqldump -uroot -pdbpasswd -d dbname test>db.sql;

    3、导出數據库為dbname所有表结构及表數據(不加-d)
    mysqldump -uroot -pdbpasswd  dbname >db.sql;

    4、导出數據库為dbname某张表(test)结构及表數據(不加-d)
    mysqldump -uroot -pdbpasswd dbname test>db.sql;

 

mysql查表大小

查整个库的状态:
select concat(truncate(sum(data_length)/1024/1024,2),'MB') as data_size,
concat(truncate(sum(max_data_length)/1024/1024,2),'MB') as max_data_size,
concat(truncate(sum(data_free)/1024/1024,2),'MB') as data_free,
concat(truncate(sum(index_length)/1024/1024,2),'MB') as index_size
from information_schema.tables where TABLE_SCHEMA = 'databasename';

查单表:
select concat(truncate(sum(data_length)/1024/1024,2),'MB') as data_size,
concat(truncate(sum(max_data_length)/1024/1024,2),'MB') as max_data_size,
concat(truncate(sum(data_free)/1024/1024,2),'MB') as data_free,
concat(truncate(sum(index_length)/1024/1024,2),'MB') as index_size
from information_schema.tables where TABLE_NAME = 'tablename';

29月/1115

Memcache 的命令行用法

发布在 邵珠庆

1、启动Memcache 常用参数
memcached 1.4.3
-p <num>      设置端口号(默认不设置为: 11211)
-U <num>      UDP监听端口 (默认: 11211, 0 时关闭)  
-l <ip_addr>  绑定地址 (默认:所有都允许,无论内外网或者本机更换IP,有安全隐患,若设置为127.0.0.1就只能本机访问)
-d            独立进程运行
-u <username> 绑定使用指定用于运行进程 <username>
-m <num>      允许最大内存用量,单位M (默认: 64 MB)
-P <file>     将PID写入文件<file>,这样可以使得后边进行快速进程终止, 需要与 -d 一起使用
如:
在linux下:./usr/local/bin/memcached -d -u jb-mc -l 192.168.1.197 -m 2048 -p 12121
在window下:d:\App_Serv\memcached\memcached.exe -d RunService -l 127.0.0.1 -p 11211 -m 500
在windows下注册为服务后运行:
sc.exe create jb-Memcached binpath= “d:\App_Serv\memcached\memcached.exe -d RunService -p 11211 -m 500″ start= auto
net start jb-Memcached

2、连接:telnet 127.0.0.1 11211
不要说不会用这个?

3、写入memcache
<command name> <key> <flags> <exptime> <bytes>\r\n <data block>\r\n
a) <command name> 可以是”set”, “add”, “replace”。
“set”表示按照相应的<key>存储该数据,没有的时候增加,有的覆盖。
“add”表示按照相应的<key>添加该数据,但是如果该<key>已经存在则会操作失败。
“replace”表示按照相应的<key>替换数据,但是如果该<key>不存在则操作失败

b) <key> 客户端需要保存数据的key。

c) <flags> 是一个16位的无符号的整数(以十进制的方式表示)。
该标志将和需要存储的数据一起存储,并在客户端get数据时返回。
客户可以将此标志用做特殊用途,此标志对服务器来说是不透明的。

d) <exptime> 过期的时间。
若为0表示存储的数据永远不过时(但可被服务器算法:LRU 等替换)。
如果非0(unix时间或者距离此时的秒数),当过期后,服务器可以保证用户得不到该数据(以服务器时间为标准)。

e) <bytes> 需要存储的字节数(不包含最后的”\r\n”),当用户希望存储空数据时,<bytes>可以为0

f) 最后客户端需要加上”\r\n”作为”命令头”的结束标志。
<data block>\r\n

紧接着”命令头”结束之后就要发送数据块(即希望存储的数据内容),最后加上”\r\n”作为此次通讯的结束。

结果响应:reply
当以上数据发送结束之后,服务器将返回一个应答。可能有如下的情况:

a) “STORED\r\n”:表示存储成功
b) “NOT_STORED\r\n” : 表示存储失败,但是该失败不是由于错误。
通常这是由于”add”或者”replace”命令本身的要求所引起的,或者该项在删除队列之中。

如: set key 33 0 4\r\n
ffff\r\n

4、获取/检查KeyValue
get <key>*\r\n
a) <key>* 表示一个或者多个key(以空格分开)
b) “\r\n” 命令头的结束

结果响应:reply
服务器端将返回0个或者多个的数据项。每个数据项都是由一个文本行和一个数据块组成。当所有的数据项都接收完毕将收到”END\r\n”
每一项的数据结构:
VALUE <key> <flags> <bytes>\r\n
<data block>\r\n

a) <key> 希望得到存储数据的key
b) <falg> 发送set命令时设置的标志项
c) <bytes> 发送数据块的长度(不包含”\r\n”)
d) “\r\n” 文本行的结束标志
e) <data block> 希望接收的数据项。
f) “\r\n” 接收一个数据项的结束标志。

如果有些key出现在get命令行中但是没有返回相应的数据,这意味着服务器中不存在这些项,这些项过时了,或者被删除了
如:get aa
VALUE aa 33 4
ffff
END

5、删除KeyValue:
delete <key> <time>\r\n

a) <key> 需要被删除数据的key
b) <time> 客户端希望服务器将该数据删除的时间(unix时间或者从现在开始的秒数)
c) “\r\n” 命令头的结束

6、检查Memcache服务器状态:
stats\r\n
在这里可以看到memcache的获取次数,当前连接数,写入次数,已经命中率等;

pid : 进程id
uptime :总的运行时间,秒数
time : 当前时间
version : 版本号
……
curr_items : 当前缓存中的KeyValue数量
total_items : 曾经总共经过缓存的KeyValue数量
bytes : 所有的缓存使用的内存量
curr_connections 当前连接数
….
cmd_get : 总获取次数
cmd_set : 总的写入次数
get_hits : 总的命中次数
miss_hits :  获取失败次数
…..
bytes_read : 总共读取的流量字节数
bytes_written : 总的写入流量字节
limit_maxbytes : 最大允许使用的内存量,字节

7、高级缓存细节查看方法:
stats reset
清空统计数据

stats malloc
显示内存分配数据

stats cachedump slab_id limit_num
显示某个slab中的前limit_num个key列表,显示格式如下
ITEM key_name [ value_length b; expire_time|access_time s]
其中,memcached 1.2.2及以前版本显示的是  访问时间(timestamp)
1.2.4以上版本,包括1.2.4显示 过期时间(timestamp)
如果是永不过期的key,expire_time会显示为服务器启动的时间

stats cachedump 7 2
ITEM copy_test1 [250 b; 1207795754 s]
ITEM copy_test [248 b; 1207793649 s]

stats slabs
显示各个slab的信息,包括chunk的大小、数目、使用情况等

stats items
显示各个slab中item的数目和最老item的年龄(最后一次访问距离现在的秒数)

stats detail [on|off|dump]
设置或者显示详细操作记录

参数为on,打开详细操作记录
参数为off,关闭详细操作记录
参数为dump,显示详细操作记录(每一个键值get、set、hit、del的次数)

8、清空所有键值
flush_all
注:flush并不会将items删除,只是将所有的items标记为expired,因此这时memcache依旧占用所有内存。

8、退出
quit\r\n

124月/100

awk 用法

发布在 邵珠庆

调用AWK
有三种方式调用awk,第一种是命令行方式,如:
awk [-F field-separator]'commands' input-files(s)
这里,commands是真正的awk命令。本章将经常使用这种方法。
上面例子中,[-F域分隔符]是可选的,因为awk使用空格作为缺省的域分隔符,因此如果要浏览域间有空格的文本,不必指定这个选项,但如果要浏览诸如passwd文件,此文件各域以冒号作为分隔符,则必须指明- F选项,如:
awk -F:'commands'input-file
第二种方法是将所有awk命令插入一个文件,并使awk程序可执行,然后用awk命令解释器作为脚本的首行,以便通过键入脚本名称来调用它。
第三种方式是将所有的awk命令插入一个单独文件,然后调用:
awk -f awk-script-file input-file(s)
-f选项指明在文件awk_script_file中的awk脚本,input_file(s)是使用awk进行浏览的文件名。
awk脚本
在命令中调用awk时,awk脚本由各种操作和模式组成。
如果设置了-F选项,则awk每次读一条记录或一行,并使用指定的分隔符分隔指定域,但如果未设置-F选项,awk假定空格为域分隔符,并保持这个设置直到发现一新行。当新行出现时,awk命令获悉已读完整条记录,然后在下一个记录启动读命令,这个读进程将持续到文件尾或文件不再存在。
参照表1-1,awk每次在文件中读一行,找到域分隔符(这里是符号#),设置其为域n,直至一新行(这里是缺省记录分隔符),然后,划分这一行作为一条记录,接着awk再次启动下一行读进程。
表1-1 awk读文件记录的方式

域1 分隔符 域2 分隔符 域3 分隔符 域4及换行
P.Bunny(记录1) # 02/99 # 48 # Yellow/n
J.Troll(记录2) # 07/99 # 4842 # Brown-3/n

模式和动作
任何awk语句都由模式和动作组成。在一个awk脚本中可能有许多语句。模式部分决定动作语句何时触发及触发事件。处理即对数据进行的操作。如果省略模式部分,动作将时刻保持执行状态。
模式可以是任何条件语句或复合语句或正则表达式。模式包括两个特殊字段BEGIN和END。使用BEGIN语句设置计数和打印头。BEGIN语句使用在任何文本浏览动作之前,之后文本浏览动作依据输入文件开始执行。END语句用来在awk完成文本浏览动作后打印输出文本总数和结尾状态标志。如果不特别指明模式,awk总是匹配或打印行数。
实际动作在大括号{}内指明。动作大多数用来打印,但是还有些更长的代码诸如if和循环(looping)语句及循环退出结构。如果不指明采取动作,awk将打印出所有浏览出来的记录。
下面将深入讲解这些模式和动作。
域和记录
awk执行时,其浏览域标记为$1,$2...$n。这种方法称为域标识。使用这些域标识将更容易对域进行进一步处理。
使用$1,$3表示参照第1和第3域,注意这里用逗号做域分隔。如果希望打印一个有5个域的记录的所有域,不必指明$1,$2,$3,$4,$5,可使用$0,意即所有域.Awk浏览时,到达一新行,即假定到达包含域的记录末尾,然后执行新记录下一行的读动作,并重新设置域分隔。
注意执行时不要混淆符号$和shell提示符$,它们是不同的。
为打印一个域或所有域,使用print命令。这是一个awk动作(动作语法用圆括号括起来)。
1. 抽取域
真正执行前看几个例子,现有一文本文件grade.txt,记录了一个称为柔道数据库的行信息。
$pg grade.txt

此文本文件有7个域,即(1)名字、(2)升段日期、(3)学生序号、(4)腰带级别、(5)年龄、(6)目前比赛积分、(7)比赛最高分。
2. 保存awk输出
有两种方式保存shell提示符下awk脚本的输出。最简单的方式是使用输出重定向符号>文件名,下面的例子重定向输出到文件wow。
$awk '{print $0}' grade.txt>wow
使用这种方法要注意,显示屏上不会显示输出结果。因为它直接输出到文件。只有在保证输出结果正确时才会使用这种方法。它也会重写硬盘上同名数据。
第二种方法是使用tee命令,在输出到文件的同时输出到屏幕。在测试输出结果正确与否时多使用这种方法。例如输出重定向到文件delete_me_and_die,同时输出到屏幕。使用这种方法,在awk命令结尾写入|tee delete_me_and_die。
$awk '{print $0}' grade.txt|tee delete_me_and_die
3. 使用标准输入
在深入讲解这一章之前,先对awk脚本的输入方法简要介绍一下。实际上任何脚本都是从标准输入中接受输入的。为运行本章脚本,使用awk脚本输入文件格式,例如:
$belts.awk grade_student.txt
也可替代使用下述格式:
使用重定向方法:
$belts.awk < grade2.txt
或管道方法:
$grade2.txt|belts.awk
4. 打印所有记录
$awk '{print $0}' grade.txt
awk读每一条记录。因为没有模式部分,只有动作部分{print $0}(打印所有记录),这个动作必须用花括号括起来。上述命令打印整个文件。
5. 打印单独记录
假定只打印学生名字和腰带级别,查看域所在列,可知为field-1和field-4,因此可以使用$1和$4,但不要忘了加逗号以分隔域。
$awk '{print $1,$4}' grade.txt
6. 打印报告头
上述命令输出在名字和腰带级别之间用一些空格使之更容易划分,也可以在域间使用tab键加以划分。为加入tab键,使用tab键速记引用符/t,后面将对速记引用加以详细讨论。也可以为输出文本加入信息头。本例中加入name和belt及下划线。下划线使用/n,强迫启动新行,并在/n下一行启动打印文本操作。打印信息头放置在BEGIN模式部分,因为打印信息头被界定为一个动作,必须用大括号括起来。在awk查看第一条记录前,信息头被打印。
$awk 'BEGIN {print "Name    Belt/n---------------------"}
{print $1"/t"$4}'grade.txt
Name        Belt
----------------------
M.Transley  Green
(省略)
7. 打印信息尾
如果在末行加入end of report信息,可使用END语句。END语句在所有文本处理动作执行完之后才被执行。END语句在脚本中的位置放置在主要动作之后。下面简单打印头信息并告之查询动作完成。
$awk 'BEGIN {print "Name/n--------"}{print $1}END{"end of report"}'grade.txt
Name
--------------
M.Transley
(……)
8. awk错误信息提示
在碰到awk错误时,可相应查找:
• 确保整个awk命令用单引号括起来。
• 确保命令内所有引号成对出现。
• 确保用花括号括起动作语句,用圆括号括起条件语句。
• 可能忘记使用花括号,也许你认为没有必要,但awk不这样认为,将按之解释语法。
元字符
这里是awk中正则表达式匹配操作中经常用到的字符。
/ ^ $ . [] | () * + ?
+,?这里没讲到,因为它们只适用于awk而不适用于grep或sed
+ 使用+匹配一个或多个字符。
? 匹配模式出现频率。例如使用/XY?Z/匹配XYZ或YZ。
条件操作符

操作符 描述
< 小于
<=(>=) 小于等于(大于等于)
== 等于
!= 不等于
~ 匹配正则表达式
!~ 不匹配正则表达式

1. 匹配
为使一域号匹配正则表达式,使用符号‘~’后紧跟正则表达式,也可以用if语句。awk中if后面的条件用()括起来。
观察文件grade.txt,如果只要打印brown腰带级别可知其所在域为field-4,这样可以写出表达式{if($4~/brown/)print}意即如果field-4包含brown,打印它。如果条件满足,则打印匹配记录行。可以编写下面脚本,因为这是一个动作,必须用花括号{}括起来。
$awk '{if($4~/brown/)print $0}' grade.txt
J.Troll   07/99   4842   Brown-3   12  26   26
(……)
匹配记录找到时,如果不特别声明,awk缺省打印整条记录。使用if语句开始有点难,但不要着急,因为有许多方法可以跳过它,并仍保持同样结果。下面例子意即如果记录包含模式brown,就打印它:
$awk '$0 ~ /Brown/' grade.txt
J.Troll   07/99   4842   Brown-3   12  26   26
(……)
2. 小于等于
$awk '{if($6<$7)print $1}'grade.txt
3.行首
$awk '/^48/'input-file
复合操作符:
&& AND : 语句两边必须同时匹配为真。
|| OR:语句两边同时或其中一边匹配为真。
! 非求逆
awk内置变量

内置变量 含义
ARGC 命令行参数个数
ARGV 命令行参数排列
ENVIRON 支持队列中系统环境变量的使用
FILENAME awk浏览的文件名
FNR 浏览文件的记录数
FS 设置输入域分隔符,等价于命令行-F选项
NF 浏览记录的域个数
NR 已读的记录数
OFS 输出域分隔符
ORS 输出记录分隔符
RS 控制记录分隔符

ARGC支持命令行中传入awk脚本的参数个数。ARGV是ARGC的参数排列数组,其中每一元素表示为ARGV[n],n为期望访问的命令行参数。
ENVIRON支持系统设置的环境变量,要访问单独变量,使用实际变量名,例如ENVIRON["EDITOR"] ="Vi"。
FILENAME支持awk脚本实际操作的输入文件。因为awk可以同时处理许多文件,因此如果访问了这个变量,将告之系统目前正在浏览的实际文件。
FNR支持awk目前操作的记录数。其变量值小于等于NR。如果脚本正在访问许多文件,每一新输入文件都将重新设置此变量。
FS用来在awk中设置域分隔符,与命令行中-F选项功能相同。缺省情况下为空格。如果用逗号来作域分隔符,设置FS=","。
NF支持记录域个数,在记录被读之后再设置。
OFS允许指定输出域分隔符,缺省为空格。如果想设置为#,写入OFS="#"。
ORS为输出记录分隔符,缺省为新行(/n)。
RS是记录分隔符,缺省为新行(/n)。
NF、NR和FILENAME
下面看一看awk内置变量的例子。
要快速查看记录个数,应使用NR。比如说导出一个数据库文件后,如果想快速浏览记录个数,以便对比于其初始状态,查出导出过程中出现的错误。使用N R将打印输入文件的记录个数。print NR放在END语法中。
$awk 'END {print NR}'grade.txt
以下例子中,所有学生记录被打印,并带有其记录号。使用NF变量显示每一条读记录中有多少个域,并在END部分打印输入文件名。
$awk '{print NF,NR,$0}END{print FILENAME}' grade.txt
7  1  M.Transley   05/99   48311   Green  8   40   44
7  2  J.Lulu       06/99   48317   green  9   24   26
(……)
grade.txt
在从文件中抽取信息时,最好首先检查文件中是否有记录。下面的例子只有在文件中至少有一个记录时才查询Brown级别记录。使用AND复合语句实现这一功能。意即至少存在一个记录后,查询字符串Brown,最后打印结果。
$awk '{if(NR>0 && $4~/Brown/)print $0}'grade.txt
NF的一个强大功能是将变量$PWD的返回值传入awk并显示其目录。这里需要指定域分隔符/。
$pwd
/usr/local/etc
$echo $pwd|awk -F/ '{print $NF}'
etc
awk操作符
1. 设置输入域到域变量名
在awk中,设置有意义的域名是一种好习惯,在进行模式匹配或关系操作时更容易理解。一般的变量名设置方式为name=$n,这里name为调用的域变量名,n为实际域号。例如设置学生域名为name,级别域名为belt,操作为name=$1;belts=$4。注意分号的使用,它分隔awk命令。下面例子中,重新赋值学生名域为name,级别域为belts。查询级别为Yellow的记录,并最终打印名称和级别。
$awk '{name=$1;belt=$4;if(belt ~Yellow/)print name"is belt"belts}'grade.txt
P.Bunny is belt Yellow.
2. 域值比较操作
有两种方式测试一数值域是否小于另一数值域。
1) 在BEGIN中给变量名赋值。
2) 在关系操作中使用实际数值。
通常在BEGIN部分赋值是很有益的,可以在awk表达式进行改动时减少很多麻烦。
使用关系操作必须用圆括号括起来。
下面的例子查询所有比赛中得分在27点以下的学生。
用引号将数字引用起来是可选的,“27”、27产生同样的结果。
$awk '{if{$6<27)print $0}'grade.txt
3. 修改数值域取值
当在awk中修改任何域时,重要的一点是要记住实际输入文件是不可修改的,修改的只是保存在缓存里的awk复本。awk会在变量NR或NF变量中反映出修改痕迹。
为修改数值域,简单的给域标识重赋新值,如: $1=$1+5,会将域1数值加5,但要确保赋值域其子集为数值型。
修改M.Tansley的目前级别分域,使其数值从40减为39,使用赋值语句$6=$6-1,当然在实施修改前首先要匹配域名。
$awk '{if($1=="M.Tansley") $6=$6-1;print $1,$6,$7}' grade.txt
4. 修改文本域
修改文本域即对其重新赋值。需要做的就是赋给一个新的字符串。在J.Troll中加入字母,使其成为J.L.Troll,表达式为$1="J.L.Troll",记住字符串要使用双引号(" "),并用圆括号括起整个语法。
$awk '{if($1==J.Troll")($1=J.L.Troll);print $1}' grade.txt
5. 只显示修改记录
上述例子均是对一个小文件的域进行修改,因此打印出所有记录查看修改部分不成问题,但如果文件很大,记录甚至超过100,打印所有记录只为查看修改部分显然不合情理。在模式后面使用花括号将只打印修改部分。取得模式,再根据模式结果实施操作,可能有些抽象,现举一例,只打印修改部分。注意花括号的位置。
$awk '{if($1==J.Troll"){$1=J.L.Troll ;print $1}}' grade.txt
6. 创建新的输出域
在awk中处理数据时,基于各域进行计算时创建新域是一种好习惯。创建新域要通过其他域赋予新域标识符。如创建一个基于其他域的加法新域{$4=$2+$3},这里假定记录包含3个域,则域4为新建域,保存域2和域3相加结果。
在文件grade.txt中创建新域8保存域目前级别分与域最高级别分的减法值。表达式为‘{$8=$7-$6}’,语法首先测试域目前级别分小于域最高级别分。新域因此只打印其值大于零的学生名称及其新域值。在BEGIN部分加入tab键以对齐报告头。
$awk 'BEGIN{print "Name/t Difference"}{if($6<$7){$8=$7-$6;print $1,$8}}' grade.txt
7. 增加列值
为增加列数或进行运行结果统计,使用符号+=。增加的结果赋给符号左边变量值,增加到变量的域在符号右边。例如将$1加入变量total,表达式为total+=$1。列值增加很有用。许多文件都要求统计总数,但输出其统计结果十分繁琐。在awk中这很简单,请看下面的例子。
将所有学生的‘目前级别分’加在一起,方法是tot+=$6,tot即为awk浏览的整个文件的域6结果总和。所有记录读完后,在END部分加入一些提示信息及域6总和。不必在awk中显示说明打印所有记录,每一个操作匹配时,这是缺省动作。
$ awk '(tot+=$6);END {print "Club student total points:" tot}' grade.txt
M.Transley   05/99   48311   Green  8   40   44
(……)
Club student total points:155
如果文件很大,你只想打印结果部分而不是所有记录,在语句的外面加上圆括号()即可。
$ awk '{(tot+=$6);END {print "Club student total points:" tot}}' grade.txt
Club student total points:155

内置的字符串函数
-----------------------------------------------------------
gsub(r,s)         在整个$0中用s替代r
gsub(r,s,t)       在整个t中用s替代r
index(s,t)        返回s中字符串t的第一位置
length(s)         返回s长度
match(s,r)        测试s是否包含匹配r的字符串
split(s,a,fs)     在fs上将s分成序列a
sprint(fmt,exp)   返回经fmt格式化后的exp
sub(r,s)          用$0中最左边最长的子串代替s
substr(s,p)       返回字符串s中从p开始的后缀部分
substr(s,p,n)     返回字符串s中从p开始长度为n的后缀部分
-----------------------------------------------------------
gsub函数有点类似于sed查找和替换。它允许替换一个字符串或字符为另一个字符串或字符,并以正则表达式的形式执行。第一个函数作用于记录$0,第二个gsub函数允许指定目标,然而,如果未指定目标,缺省为$0。
index(s,t)函数返回目标字符串s中查询字符串t的首位置。length函数返回字符串s字符长度。match函数测试字符串s是否包含一个正则表达式r定义的匹配。split使用域分隔符fs将字符串s划分为指定序列a。sprint函数类似于printf函数(以后涉及),返回基本输出格式fmt的结果字符串exp.sub(r,s)函数将用s替代$0中最左边最长的子串,该子串被(r)匹配。
sub(s,p)返回字符串s在位置p后的后缀。substr(s,p,n)同上,并指定子串长度为n。
1. gsub
要在整个记录中替换一个字符串为另一个,使用正则表达式格式, /目标模式/,替换模式/。例如改变学生序号4842到4899:
$ awk 'gsub(/4842/,4899){print $0}' grade.txt
J.Troll   07/99  4899  Brown-3  12   26   26
2. index
查询字符串s中t出现的第一位置。必须用双引号将字符串括起来。例如返回目标字符串Bunny中ny出现的第一位置,即字符个数。
$ awk 'BEGIN {print index("Bunny","ny")}' grade.txt
4
3. length
返回所需字符串长度,例如检验字符串J.Troll返回名字及其长度,即人名构成的字符个数。
$ awk '$1=="J.Troll" {print length($1) " " $1}' grade.txt
7 J.Troll
4. match
match测试目标字符串是否包含查找字符的一部分。可以对查找部分使用正则表达式,返回值为成功出现的字符排列数。如果未找到,返回0,第一个例子在ANCD中查找d。因其不存在,所以返回0。第二个例子在ANCD中查找D。因其存在,所以返回ANCD中D出现的首位置字符数。第三个例子在学生J.Lulu中查找u。
$ awk 'BEGIN {print match("ANCD",/d/)}'
0
$ awk 'BEGIN {print match("ANCD",/C/)}'
3
$ awk '$1=="J.Lulu" {print match($1,"u")}' grade.txt
4
5. split
使用split返回字符串数组元素个数。例如;
$ awk 'BEGIN{print split("123#456#789",myarray,#)}'
3
split返回数组myarray的下标数。数组myarray取值如下:
Myarray[1]=123
Myarray[2]=456
Myarray[3]=789
6. sub
使用sub发现并替换模式的第一次出现位置。学生J.Troll的记录有两个值一样,“目前级别分”与“最高级别分”。只改变第一个为29,第二个仍为24不动,操作命令为sub(/26/,"29",$0),只替换第一个出现24的位置。注意J.Troll记录需存在。
7. substr
substr是一个很有用的函数。它按照起始位置及长度返回字符串的一部分。如果给定长度值远大于字符串长度,awk将从起始位置返回所有字符,要抽取L.Tansley
的姓,只需从第3个字符开始返回长度为7。可以输入长度99,awk返回结果相同。
$ awk '$1=="L.Tansley" {print substr($1,3,99)}' grade.txt
Tansley
substr的另一种形式是返回字符串后缀或指定位置后面字符。这里需要给出指定字符串及其返回字串的起始位置。例如,从文本文件中抽取姓氏,需操作域1,并从第三个字符开始:
$ awk '{print substr($1,3)}' grade.txt
还有一个例子,在BEGIN部分定义字符串,在END部分返回从第t个字符开始抽取的子串。
$ awk 'BEGIN {STR="A FEW GOOD MEN"} END {print substr(STR,7)}'grade.txt
GOOD MEN
8. 从shell中向awk传入字符串
awk脚本大多只有一行,其中很少是字符串表示的。大多要求在一行内完成awk脚本,这一点通过将变量传入awk命令行会变得很容易。现就其基本原理讲述一些例子。例如:

字符串屏蔽序列
使用字符串或正则表达式时,有时需要在输出中加入一新行或查询一元字符。
打印一新行时,(新行为字符/n),给出其屏蔽序列,以不失其特殊含义,用法为在字符串前加入反斜线。例如使用/n强迫打印一新行。
如果使用正则表达式,查询花括号({}),在字符前加反斜线,如//{/,将在awk中失掉其特殊含义。

awk中使用的屏蔽序列
/b 退格键         /t tab键
/f 走纸换页       /ddd 八进制值
/n 新行           /c 任意其他特殊字符,例如//为反斜线符号
/r 回车键

awk输出函数printf
每一种printf函数(格式控制字符)都以一个%符号开始,以一个决定转换的字符结束。转换包含三种修饰符。printf函数基本语法是printf([格式控制符],参数),格式控制字符通常在引号里。

awk printf修饰符
-        左对齐
Width    域的步长,用0表示0步长
.prec    最大字符串长度,或小数点右边的位数

 

awk printf格式
%c     ASCII字符
%d     整数
%e     浮点数,科学记数法
%f     浮点数,例如(123.44)
%gawk  决定使用哪种浮点数转换e或者f
%o     八进制数
%s     字符串
%x     十六进制数

1. 字符转换
管道输出65到awk。printf进行ASCII码字符转换。这里也加入换行,因为缺省情况下printf不做换行动作。
$echo "65" | awk '{printf "%c/n",$0}'
A
当然也可以按同样方式使用awk得到同样结果。
$ awk 'BEGIN {printf "%c/n",65}'
A
所有的字符转换都是一样的,下面的例子表示进行浮点数转换后‘999’的输出结果。整数传入后被加了六个小数点。
$ awk 'BEGIN{printf "%f/n",999}
999.000000
2. 格式化输出
打印所有的学生名字和序列号,要求名字左对齐,15个字符长度,后跟序列号。注意/n换行符放在最后一个指示符后面。输出将自动分成两列。
$ awk '{printf "%-15s %s/n",$1,$3} grade.txt
3. 向一行awk命令传值
在查看awk脚本前,先来查看怎样在awk命令行中传递变量。
在awk执行前将值传入awk变量,需要将变量放在命令行中,格式如下:
awk 命令变量=输入文件值
下面的例子在命令行中设置变量AGE等于10,然后传入awk中,查询年龄在10岁以下的所有学生。
$ awk'{if($5<AGE) print $0}' AGE=10 grade.txt
要快速查看文件系统空间容量,观察其是否达到一定水平,可使用下面awk一行脚本。因为要监视的已使用空间容量不断在变化,可以在命令行指定一个触发值。首先用管道命令将df -k 传入awk,然后抽出第4列,即剩余可利用空间容量。使用$4 ~/^[0-9]/取得容量数值(1024块)而不是df的文件头,然后对命令行与‘ if($4<TRIGGER)’上变量TRIGGER中指定的值进行查询测试。

 

4. awk脚本文件
可以将awk脚本写入一个文件再执行它。命令不必很长(尽管这是写入一个脚本文件的主要原因),甚至可以接受一行命令。这样可以保存awk命令,以使不必每次使用时都需要重新输入。使用文件的另一个好处是可以增加注释,以便于理解脚本的真正用途和功能。
使用前面的几个例子,将之转换成awk可执行文件。像原来做的一样,将学生目前级别分相加awk '(tot+=$6)END{print "club student total points:"tot}'grade.txt。创建新文件student_tot.awk,给所有awk程序加入awk扩展名是一种好习惯,这样通过查看文件名就知道这是一个awk程序。如下:

第一行是!/bin/awk -f。这很重要,没有它自包含脚本将不能执行。这一行告之脚本系统中awk的位置。通过将命令分开,脚本可读性提高,还可以在命令之间加入注释。这里加入头信息和结尾的平均值。基本上这是一个一行脚本文件。
执行时,在脚本文件后键入输入文件名,但是首先要对脚本文件加入可执行权限。
系统中运用的帐号核实程序检验数据操作人的数据输入,不幸的是这个程序有一点错误,或者应该说是“非文本特征”。如果一个记录被发现包含一个错误,它应该一次只打印一行“ERROR*”,但实际上打印了许多这样的错误行。这会给帐号管理员造成误解,因此需要用awk脚本过滤出错误行的出现频率,使得每一个失败记录只对应一个错误行。
在awk实施过滤前先看看部分文件:

5. 在awk中使用FS变量
如果使用非空格符做域分隔符(FS)浏览文件,例如#或:,编写这样的一行命令很容易,因为使用FS选项可以在命令行中指定域分隔符。
$ awk -F:'awk {print $0}' input-file
使用awk脚本时,记住设置FS变量是在BEGIN部分。如果不这样做,awk将会发生混淆,不知道域分隔符是什么。下述脚本指定FS变量。脚本从/etc/passwd文件中抽取第1和第5域,通过分号“;”分隔passwd文件域。第1域是帐号名,第5域是帐号所有者。

文本包括了比实际命令更多的信息,没关系,仔细研读文本后,就可以精确知道其功能及如何调用它。
不要忘了增加脚本的可执行权限,然后将变量和赋值放在命令行脚本名字后、输入文件前执行。
$ age.awk AGE=10 grade.txt
同样可以使用前面提到的管道命令传值,下述awk脚本从du命令获得输入,并输出块和字节数。

awk数组
前面讲述split函数时,提到怎样使用它将元素划分进一个数组。这里还有一个例子:
$ awk 'BEGIN{print split("123#456#789",myarray,#)}'
3
在上面的例子中,split返回数组myarray下标数。实际上myarray数组为:
Myarray[1]=123
Myarray[2]=456
Myarray[3]=789
数组使用前,不必定义,也不必指定数组元素个数。经常使用循环来访问数组。下面是一种循环类型的基本结构:
For (element in array ) print array[element]
对于记录“123#456#678”,先使用split函数划分它,再使用循环打印各数组元素。操作脚本如下:

数组和记录
上面的例子讲述怎样通过split函数使用数组。也可以预先定义数组,并使用它与域进行比较测试,下面的例子中将使用更多的数组。
下面是从空手道数据库卸载的一部分数据,包含了学生级别及是否是成人或未成年人的信息,有两个域,分隔符为(#),文件如下:

脚本功能是读文件并输出下列信息。
1) 俱乐部中Yellow、Orange和Red级别的人各是多少。
2) 俱乐部中有多少成年人和未成年人。
查看文件,也许20秒内就会猜出答案,但是如果记录超过6 0个又怎么办呢?这不会很容易就看出来,必须使用awk脚本。
首先看看awk脚本,然后做进一步讲解。

BEGIN部分设置FS为符号#,即域分隔符,因为要查找Yellow、Orange和Red三个级别。然后在脚本中手工建立数组下标对学生做同样的操作。注意,脚本到此只有下标或元素,并没有给数组名本身加任何注释。初始化完成后,BEGIN部分结束。记住BEGIN部分并没有文件处理操作。
现在可以处理文件了。首先给数组命名为color,使用循环语句测试域1级别列是否等于数组元素之一(Yellow、Orange或Red),如果匹配,依照匹配元素将运行总数保存进数组。同样处理数组‘Senior_or_junior’,浏览域2时匹配操作满足,运行总数存入junior或senior的匹配数组元素。
END部分打印浏览结果,对每一个数组使用循环语句并打印它。
注意在打印语句末尾有一个/符号,用来通知awk(或相关脚本)命令持续到下一行,当输入一个很长的命令,并且想分行输入时可使用这种方法。运行脚本前记住要加入可执行权限。