Percona Xtrabackup备份mysql大数据库(完整备份与增量备份)

Xtrabackup简介

Percona XtraBackup是开源免费的MySQL数据库热备份软件,它能对InnoDB和XtraDB存储引擎的数据库非阻塞地备份(对于MyISAM的备份同样需要加表锁)。XtraBackup支持所有的Percona Server、MySQL、MariaDB和Drizzle。

XtraBackup优势 :
1、无需停止数据库进行InnoDB热备
2、增量备份MySQL
3、流压缩到传输到其它服务器
4、能比较容易地创建主从同步
5、备份MySQL时不会增大服务器负载

Xtrabackup安装

这里只介绍Ubuntu-12.04下的安装方法,其它系统的安装方法请参考http://www.percona.com/doc/percona-xtrabackup/2.1/installation.html

  1. apt-key adv –keyserver keys.gnupg.net –recv-keys 1C4CBDCDCD2EFD2A

在/etc/apt/sources.list加入:

  1. deb http://repo.percona.com/apt precise main
  2. deb-src http://repo.percona.com/apt precise main

执行update及安装操作

  1. apt-get update
  2. apt-get install percona-xtrabackup

注:precise是Ubuntu-12.04的版本代号,如果是其它系统版本,需要更换。

Xtrabackup工具介绍

安装XtraBackup后,其实会有几个工具:
innobackupex:
这个是其实是下面三个工具的一个perl脚本封装,可以备份MyISAM, InnoDB, XtraDB表。
xtrabackup:
一个由C编译而来的二进制文件,只能备份InnoDB和XtraDB数据。
xbcrypt:
用来加密或解密备份的数据。
xbstream:
用来解压或压缩xbstream格式的压缩文件。
建议使用perl封装的innobackupex来作数据库备份,因为比较容易使用。所以下面只介绍innobackupex的使用。其它的使用参考:http://www.percona.com/doc/percona-xtrabackup/2.1/manual.html

innobackupex使用方法

完整的选项使用请执行innobackupex –help,这里只介绍使用常用的选项进行完整备份及增量备份和还原。

完整备份及还原

假如我们要备份centos和mysql数据库。
备份:

  1. innobackupex –user=root –password=root –defaults-file=/etc/mysql/my.cnf –include="centos.<em>|mysql.</em>"    /data/mysql_backup

这样就会在/data/mysql_backup生成一个带时间的目录,如果不需要带时间,可以使用选项–no-timestamp。
如果想备份成压缩文件,可以使用如下语句:

  1. innobackupex –user=root –password=root –defaults-file=/etc/mysql/my.cnf –include="centos.<em>|mysql.</em>" –no-timestamp –stream=tar ./ | gzip – > devops.webres.wang.tar.gz

还原:
假如完整备份的路径为/data/mysql_backup/full_backup,如果完整备份是压缩文件,需要先解压再还原。

  1. innobackupex –apply-log  /data/mysql_backup/centos_full_backup
  2. innobackupex –copy-back –defaults-file=/etc/mysql/my.cnf /data/mysql_backup/centos_full_backup
  3. chown -R mysql:mysql /var/lib/mysql

增量备份及还原

增量备份是建立在完整备份的基础上的,所以首先确保已经完整备份了一次。
完整备份:

  1. innobackupex –user=root –password=root –include="centos.*"  –no-timestamp /data/mysql_backup/centos_full_backup

现在完整备份的目录为/data/mysql_backup/centos_full_backup。
增量备份:
第一次增量备份:

  1. innobackupex –incremental /data/mysql_backup/inc/20130906 –no-timestamp –incremental-basedir=/data/mysql_backup/centos_full_backup –user=root –password=root –defaults-file=/etc/mysql/my.cnf

第二次增量备份:

  1. innobackupex –incremental /data/mysql_backup/inc/20130907 –no-timestamp –incremental-basedir=/data/mysql_backup/inc/20130906 –user=root –password=root –defaults-file=/etc/mysql/my.cnf

选项–incremental是指定作增量备份 –incremental-basedir选项是指定上一次增量备份的目录(如果是第一次作增量备份,则为完整备份的目录)。
还原:
增量备份的还原操作跟完整的还原有点不一样,首先必须使用–apply-log –redo-only对完整备份的目录及所有增量备份目录进行操作,然后就可以像还原完整备份时的操作了。
对每个备份目录作apply-log redo-only操作(最后一个增量备份不需要redo-only)

  1. innobackupex –apply-log –redo-only /data/mysql_backup/centos_full_backup  –user=root –password=root
  2. innobackupex –apply-log –redo-only /data/mysql_backup/centos_full_backup  –incremental-dir=/data/mysql_backup/inc/20130906 –user=root –password=root
  3. innobackupex –apply-log  /data/mysql_backup/centos_full_backup  –incremental-dir=/data/mysql_backup/inc/20130907 –user=root –password=root

下面就跟还原完整备份时一样了:

  1. innobackupex-1.5.1 –apply-log /data/mysql_backup/centos_full_backup –use-memory=1G –user=root –password=root
  2. innobackupex –copy-back –defaults-file=/etc/mysql/my.cnf /data/mysql_backup/centos_full_backup

nginx反向代理缓存配置

这里给出示例,并详解。

  1. http {
  2. […]
  3. […]
  4.  
  5. proxy_cache_path  /data/nginx/cache/one  levels=1:2   keys_zone=one:10m max_size=10g;
  6. proxy_cache_key  "$host$request_uri";
  7.  
  8. server {
  9.     server_name devops.webres.wang webres.wang;
  10.     root /home/devops.webres.wang/web;
  11.     index index.php index.html index.htm;
  12.     location / {
  13.         proxy_pass http://127.0.0.1:8080;
  14.         proxy_set_header  Host "devops.webres.wang";
  15.         proxy_set_header  X-Forwarded-For $proxy_add_x_forwarded_for;
  16.         #开启反向代理缓存,并使用zone name为one的缓存。
  17.         proxy_cache one;
  18.         #设置状态码为200 302过期时间为10分钟
  19.         proxy_cache_valid  200 302  10m;
  20.         #设置状态码404的过期时间为1分钟
  21.         proxy_cache_valid  404      1m;
  22.     }
  23.     #清除缓存
  24.     location ~ /purge(/.*) {
  25.         #允许的IP
  26.         allow 127.0.0.1;
  27.         deny all;
  28.         proxy_cache_purge one $host$1$is_args$args;
  29.     }
  30. }
  31.  
  32. }

反向代理的缓存主要涉及以下几个命令:
proxy_cache_path proxy_cache_key proxy_cache proxy_cache_valid。

proxy_cache_path
这个是设置缓存的目录,语法如下:
proxy_cache_path path [ levels = levels ] keys_zone = name : size [ inactive = time ] [ max_size = size ] [ loader_files = number ] [ loader_sleep = time ] [ loader_threshold = time ]
可放置的上下文:
http
参数解释:
[ levels = levels ]:
设置缓存目录层数,如levels=1:2,表示创建两层目录缓存,最多创建三层。第一层目录名取proxy_cache_key md5的最后一个字符,第二层目录名取倒数2-3字符,如:
proxy_cache_key md5为b7f54b2df7773722d382f4809d65029c,则:
levels=1:2为/data/nginx/cache/c/29/b7f54b2df7773722d382f4809d65029c
levels=1:2:3为/data/nginx/cache/c/29/650/b7f54b2df7773722d382f4809d65029c

keys_zone = name : size:
定义缓存区域名称及大小,缓存名称用于proxy_cache指令设置缓存放置在哪,如proxy_cache one,则把缓存放在zone名称为one的缓存区,即proxy_cache_path指定的具体位置。

proxy_cache_key
这个指令是设置以什么参数md5得到缓存的文件名,默认为$scheme$proxy_host$request_uri,即以协议、主机名、请求uri(包含参数)作md5得出缓存的文件名。
proxy_cache_key与下面的清缓存功能(purge cache)有很大关系。
可放置在上下文,http server location

proxy_cache
反向代理缓存设置指令,语法proxy_cache zone | off,默认为off。上下文:http server location。
可以放到指定location,这样匹配此location的url才被缓存。

proxy_cache_valid
设置指定状态码的缓存时间,语法proxy_cache_valid [ code …] time。

另外,清缓存需要安装插件ngx_cache_purge,安装方法如下:

  1. cd /tmp
  2. wget http://labs.frickle.com/files/ngx_cache_purge-2.1.tar.gz
  3. tar xzf ngx_cache_purge-2.1.tar.gz
  4. cd /tmp
  5. wget http://nginx.org/download/nginx-1.4.2.tar.gz
  6. tar xzf nginx-1.4.2.tar.gz
  7. cd nginx-1.4.2
  8. ./configure –prefix=/usr/local/nginx  –add-module=/tmp/ngx_cache_purge-2.1
  9. make && make install

参考:
http://wiki.nginx.org/HttpProxyModule
http://labs.frickle.com/nginx_ngx_cache_purge/

EZHTTP — LAMP,LNMP,LNAMP一键安装

EZHTTP简介

EZHTTP是集成了nginx apache php mysql memcached等web服务器软件的一键安装脚本(支持lnmp,lanmp,lanmp),它能自由选择安装任意的软件,自动选最优线路下载,定制容易。更详细的介绍请转到:devops.webres.wang/ezhttp/。下面详细介绍下如何使用(附:EZHTTP QQ群:153447657)。

如何安装

因为安装时间会比较久,建议安装前使用screen,screen使用方法如下:
1、首先得安装screen,ubuntu使用apt-get install screen,centos或redhat使用yum install screen安装
2、创建一个ezhttp的会话,screen -S ezhttp
3、如果终端意外断开,则可以使用screen -r ezhttp恢复

  1. screen -S ezhttp
  2. wget –no-check-certificate https://github.com/centos-bz/EZHTTP/archive/master.zip -O EZHTTP.zip
  3. unzip EZHTTP.zip
  4. cd EZHTTP-master
  5. chmod +x start.sh
  6. ./start.sh

执行start.sh脚本后,会出现如下界面:
Web服务器
这里分了两种安装方式:
第一种是快速安装,即直接读取安装包下的config文件,config文件也是一个bash脚本,脚本里预定义了安装什么软件,以及安装的路径,大家可以自行修改里面的参数而使下次可以快速安装,config脚本部分截图:
Web服务器
第二种是高级设置,即全部参数都需要自己定义,我们这里输入2继续安装:
Web服务器
首先是nginx的安装设置,这里有三个版本选择1) nginx-1.4.1 2) tengine-1.4.6 3) ngx_openresty-1.2.8.3,当然你也可以选择不安装,即4) do_not_install。 这里我们选择1继续:
Web服务器
选择1后,需要我们定义nginx安装的路径,如果直接回车,默认路径为/usr/local/nginx,当然你也可以输入自己安装的路径,比如/home/nginx,我们这里直接回车:
Web服务器
到了apache安装的设置,有两个版本可选1) httpd-2.2.25 2) httpd-2.4.4,这里建议选择2.4版本的安装,性能会好于2.2:
Web服务器
同样需要设置安装路径,默认是/usr/local/apache。
Web服务器
到了mysql的安装设置,有三个版本可选,以及mysqlclient的库安装,1) mysql-5.1.71 2) mysql-5.5.32 3) mysql-5.6.12 4) libmysqlclient18
1,2,3是版本的选择安装,而4是只安装mysqlclient的库,而不安装完成的mysql server,当你有独立的数据库服务器,而只想安装php连接这台独立的数据库服务器时,又不想在web服务器上安装整个mysql数据库,可以只选择安装libmysqlclient18,这将节省大量的安装时间。这里我们选3安装:
Web服务器
设置mysql的安装位置,默认/usr/local/mysql。我们这里直接回车。
Web服务器
设置mysql数据文件的安装位置,默认是安装在mysql安装路径的子目录data,我们这里直接回车。
Web服务器
设置mysql root密码,默认是root:
Web服务器
到了php的安装设置,可选版本有1) php-5.2.17 2) php-5.3.26 3) php-5.4.16,这里推荐php5.3:
Web服务器
这里需要选择由什么来解析php脚本,有两种选择,一种with_apache,即使用apache的mod_php模式来解析,另一种是fastcgi来解析php,当你前面选择安装nginx时,建议选2,当你前面选择安装apache时,就选1。这里我们选择2,fastcgi解析php。
Web服务器
php安装路径,默认/usr/local/php。
Web服务器
这里是php模块的选择安装,模块的可选安装列表会根据你上面所选的php版本,因为我们上面选的php版本为php5.3.26,所以可选安装的模块有:1) ZendGuardLoader 2) eaccelerator-0.9.6.1 3) imagick-3.0.1 4) ioncube_loaders 5) memcache-3.0.8,而如何你的php版本为php5.2.17,ZendGuardLoader则会变成ZendOptimizer。可以多选安装,如输入1 2 3,则是安装ZendGuardLoader、eaccelerator和imagick。不需要全部安装,根据你php程序的需求即可,这里我们选择2 5:
Web服务器
这里是选择安装其它的软件,可选安装有1) memcached-1.4.15 2) pure-ftpd-1.0.36 3) phpMyAdmin-4.0.4-all-languages,可以多选,这里我们选择安装全部,1 2 3。
Web服务器
选择安装软件后,还需要设置安装的路径,这里都有默认的设置,memcached默认路径为/usr/local/memcached,pureftpd为/usr/local/pureftpd,phpmyadmin默认安装到nginx默认网站根目录,如果你使用的是apache,则需要修改,apache默认网站路径为/usr/local/apache/htdocs。
Web服务器
输入完phpmyadmin的安装路径后,会显示全部的设置信息,如果确认设置无误,输入y开始安装,如何确认有误,则可以输入n重新设置。
安装完成后,会显示所有正在监听的端口:
Web服务器
如看见80,3306端口以及/tmp/php-cgi.sock,表示nginx或者apache、mysql,php-fpm安装成功,即可通过域名或者ip访问默认首页,centos-6需要先关闭iptables或者开放80端口访问,如:
Web服务器

EZ命令使用

ez nginx add(添加nginx虚拟主机)
Web服务器
执行此命令后,提示输入需要绑定的域名,多个域名以空格分隔,如devops.webres.wang webres.wang。
Web服务器
接着要求输入网站目录,默认为/home/domain/web,日志路径为/home/domain/logs,所以如果你输入的网站目录为/home/centos,根目录其实是/home/centos/web,日志路径为/home/centos/logs。
Web服务器
接着会问是否添加伪静态规则,输入y进入规则列表,可选有1、DEDECMS 2、Discuz_7 3、Discuz_X 4、ECshop 5、PHPCMS 6、PHPWind 7、Shopex 8、Typecho 9、WordPress。输入对应的数字选择。
Web服务器
接着会询问是否需要支持php,如果nginx只是提供静态页面的访问,则输入n,否则y。
Web服务器
这样nginx虚拟主机就添加完成了。
ez nginx list(列出nginx虚拟主机)
Web服务器
执行此命令会马上列出所有的nginx虚拟主机,信息包括域名及根目录。
ez nginx del(删除nginx虚拟主机)
Web服务器
删除虚拟主机只需要输入对应的域名,删除之前会自动重载nginx配置文件,无需手动。
ez apache add(新增apache虚拟主机)
Web服务器
添加apache虚拟主机只需要输入域名及网站目录。
ez apache list(列出apache虚拟主机)
Web服务器
ez apache del(删除apache虚拟主机)
Web服务器
ez mysql add(添加mysql数据库或用户)
此命令是用来添加mysql数据库或者用户的,操作方法如下:
执行ez mysql add,会提示输入root用户密码:
下面按提示操作即可。
ez mysql del(删除mysql数据库或用户)
用来删除mysql数据库或者用户。
ez mysql mod(修改mysql用户密码)
用来修改mysql用户密码
ez mysql reset(重置mysql root密码)
当mysql root用户的密码忘记时,这个命令可以帮助你重置。

定制EZHTTP软件版本

我想要安装最新版本的软件,我们这里以mysql5.6为例:
现在EZHTTP mysql5.6系列的安装版本为mysql-5.6.12,而现在官网的最新版本为mysql-5.6.13,如何更新EZHTTP的为最新的呢?
需要修改两个文件:
第一个文件init:
我们打开安装包下的init文件,找到mysql5.6系列的设置:
Web服务器
我们看到mysql5.6设置分为三个:
一是mysql5_6_filename变量,这个是设置mysql5.6的文件名,如mysql-5.6.12,我们这里更改为mysql-5.6.13
二是mysql5_6_baidupan_link,这个是百度网盘的链接,你可以利用网盘的离线下载来下载mysql5.6.13并获取链接地址。如果不想使用网盘下载,可以设置为mysql5_6_baidupan_link=””,设置为空。
三是mysql5_6_official_link,这个是下载mysql5.6的直链,不一定要是官方的链接,可以是其它下载的直链。
第二个文件conf/md5.txt
最后一步是设置mysql5.6.13的md5信息到安装包下的conf/md5.txt文件。
在文件的任何一行添加:

  1. aff97b406da871f020af84b7085cbdbb  mysql-5.6.13.tar.gz

此md5需要用md5软件算出,linux下可以使用md5sum mysql-5.6.13.tar.gz得出。
完成这两步就完成了mysql5.6版本的升级,够简单吧。

Web压力测试工具-http_load

介绍个http_load压力测试工具,http_load,类似的工具还有webbench、ab、Siege。

1、下载

官方网站:http://acme.com/software/http_load/

  1. cd /root
  2. wget http://acme.com/software/http_load/http_load-12mar2006.tar.gz
  3. tar xzf http_load-12mar2006.tar.gz

2、安装

  1. cd http_load-12mar2006
  2. make

执行完make,会在当前目录生成一个http_load二进制文件。

3、使用方法

  1. root@www:~/http_load-12mar2006# ./http_load –help
  2. usage:  ./http_load [-checksum] [-throttle] [-proxy host:port] [-verbose] [-timeout secs] [-sip sip_file]
  3.             -parallel N | -rate N [-jitter]
  4.             -fetches N | -seconds N
  5.             url_file
  6. One start specifier, either -parallel or -rate, is required.
  7. One end specifier, either -fetches or -seconds, is required.

主要参数说明:
-parallel 简写-p :含义是并发的用户进程数。
-rate 简写-r :含义是每秒的访问频率
-fetches 简写-f :含义是总计的访问次数
-seconds简写-s :含义是总计的访问时间

选择参数时,-parallel和-rate选其中一个,-fetches和-seconds选其中一个。
示例:

  1. http_load -parallel 50 -s 10 urls.txt

这段命令行是同时使用50个进程,随机访问urls.txt中的网址列表,总共访问10秒。

  1. http_load -rate 50 -f 5000 urls.txt

每秒请求50次,总共请求5000次停止。

Bash Shell字符串操作小结

1. 取长度

  1. str="abcd"
  2. expr length $str   # 4
  3. echo ${#str}       # 4
  4. expr "$str" : ".*" # 4

好像一般使用第二种

2. 查找子串的位置

  1. str="abc"
  2. expr index $str "a"  # 1
  3. expr index $str "b"  # 2
  4. expr index $str "x"  # 0
  5. expr index $str ""   # 0

3. 选取子串

  1. str="abcdef"
  2. expr substr "$str" 1 3  # 从第一个位置开始取3个字符, abc
  3. expr substr "$str" 2 5  # 从第二个位置开始取5个字符, bcdef
  4. expr substr "$str" 4 5  # 从第四个位置开始取5个字符, def
  5. echo ${str:2}           # 从第二个位置开始提取字符串, bcdef
  6. echo ${str:2:3}         # 从第二个位置开始提取3个字符, bcd
  7. echo ${str:(-6):5}        # 从倒数第二个位置向左提取字符串, abcde
  8. echo ${str:(-4):3}      # 从倒数第二个位置向左提取6个字符, cde

4. 截取子串

  1. str="abbc,def,ghi,abcjkl"
  2. echo ${str#a*c}     # 输出,def,ghi,abcjkl  一个井号(#) 表示从左边截取掉最短的匹配 (这里把abbc字串去掉)
  3. echo ${str##a*c}    # 输出jkl,             两个井号(##) 表示从左边截取掉最长的匹配 (这里把abbc,def,ghi,abc字串去掉)
  4. echo ${str#"a*c"}   # 输出abbc,def,ghi,abcjkl 因为str中没有"a*c"子串
  5. echo ${str##"a*c"}  # 输出abbc,def,ghi,abcjkl 同理
  6. echo ${str#*a*c*}   # 空
  7. echo ${str##*a*c*}  # 空
  8. echo ${str#d*f)     # 输出abbc,def,ghi,abcjkl,
  9. echo ${str#*d*f}    # 输出,ghi,abcjkl 
  10. echo ${str%a*l}     # abbc,def,ghi  一个百分号(%)表示从右边截取最短的匹配
  11. echo ${str%%b*l}    # a             两个百分号表示(%%)表示从右边截取最长的匹配
  12. echo ${str%a*c}     # abbc,def,ghi,abcjkl

可以这样记忆, 井号(#)通常用于表示一个数字,它是放在前面的;百分号(%)卸载数字的后面; 或者这样记忆,在键盘布局中,井号(#)总是位于百分号(%)的左边(即前面) 🙂

5. 字符串替换

  1. str="apple, tree, apple tree"
  2. echo ${str/apple/APPLE}   # 替换第一次出现的apple
  3. echo ${str//apple/APPLE}  # 替换所有apple
  4. echo ${str/#apple/APPLE}  # 如果字符串str以apple开头,则用APPLE替换它
  5. echo ${str/%apple/APPLE}  # 如果字符串str以apple结尾,则用APPLE替换它

6. 比较

  1. [[ "a.txt" == a* ]]        # 逻辑真 (pattern matching)
  2. [[ "a.txt" =~ .*.txt ]]   # 逻辑真 (regex matching)
  3. [[ "abc" == "abc" ]]       # 逻辑真 (string comparision)
  4. [[ "11" < "2" ]]           # 逻辑真 (string comparision), 按ascii值比较

7. 连接

  1. s1="hello"
  2. s2="world"
  3. echo ${s1}${s2}   # 当然这样写 $s1$s2 也行,但最好加上大括号

转自:http://my.oschina.net/aiguozhe/blog/41557

使用sed对nginx配置文件进行删除和列出虚拟主机操作

带着需要使用sed来对nginx配置文件进行操作的强烈需求,于是开始了学习sed的高级应用。虽然之前也一直在用sed,但也只是接触到了s替换命令,其它高级的命令没用到,所以没有动力去学。一直觉得要学到点东西,前提是你现在有一问题,需要用到这个技术来解决,而且有强烈的渴望要把这个问题解决,这时候你学习这项技术会事半功倍。否则学习起来会非常的枯燥无味,效率低,甚至会放弃。下面是我最近学sed得出的成果,备忘一下,以防失忆。
一、列出所有虚拟的server_name和对应的root

  1. sed -n "
  2. /servers*{/{
  3. H
  4. :loop
  5. n;H
  6. /}/{
  7. s/}//;x;H
  8. s/.*server_names*([^;]*);.*/1/p
  9. x
  10. s/.*roots*([^;]*);.*/1/p
  11. s/.*//g;x
  12. }
  13. /{/!b loop
  14. :loop1
  15. n;H
  16. /}/!b loop1
  17. b loop
  18. }
  19. " nginx-devops.webres.wang.conf

二、删除指定server_name的虚拟主机

  1. domain="devops.webres.wang"
  2. sed -i -n "
  3. /servers*{/{
  4. H
  5. :loop
  6. n;H
  7. /}/{
  8. s/}//;x
  9. /.*server_names*$domain.*/d
  10. p;d
  11. }
  12. /{/!b loop
  13. :loop1
  14. n;H
  15. /}/!b loop1
  16. b loop
  17. }
  18. p
  19. " nginx-devops.webres.wang.conf

代码有点多,就不具体分析了,具体说下如何找出server {} 代码块。

代码分析:
1、首先先关闭自动打印功能,即-n选项,因为下面会用到n命令,会自动打印,所以添加-n之后,使用n命令就不会自动打印了,需要打印时,使用p命令。
2、匹配 server {,如果匹配到,则把pattern space的内容添加到hold space,然后进入loop循环,执行n命令(n命令不必进入新的循环读取下一步到pattern space),再用H附加到hold space
3、判断是否找到代码块结束符},如果找到,进入server {}代码块处理阶段,处理完之后,进入下一个循环,继续找出下一个server{}。
4、如果找不到,再判断是否找到{,如果没有找到,进入loop循环
5、如果找到了{,表示存在if {},或者location {},则必要找到两个}才算是完整的server代码块。此时进入loop1循环,n命令读取下一行,附加到hold space。
6、再判断是否找到},如果没有找到,继续loop1循环,如果找到了,还需要一个},所以跳到loop循环。
7、如此循环下来,直接找到完整的server代码块。

关键的几点提示:
1、n命令是直接清空pattern space,并读取下一行到pattern space,而不需要进入新的循环。在这里的作用是,因为要把pattern space的内容附加到hold space,所以每读取一行,必需先清空pattern space,然后再附加到hold space,这样hold space才不会出现重复内容。要达到清空pattern space,读取下一行,进入下一循环可以做到,但进入下一循环后,必需重新匹配server {,而下一行肯定无法匹配,所以就达不到累积的效果。
2、hold space在这里很重要,它的内容来自pattern space对其进行附加操作。累积到完整的server代码块时,再与pattern space进行交换,交换之前必需把pattern space清空,这样交换后,hold space才能为空,为累积出下一个server代码块做准备。
最后还是建议使用sed的debug工具sedsed查看pattern space和hold space的变化。http://aurelio.net/projects/sedsed/

sed高级应用示例

最近需要使用sed来解析nginx配置文件,而之前使用sed仅限制于对文件的替换及添加文本,不过也基本能满足平时的bash shell脚本的编写工作。但这次需要解析nginx配置文件来对虚拟主机的代码块进行处理,比如对指定虚拟主机的删除,以及列出所有虚拟主机的信息,比如根目录是哪个。单靠简单的匹配是无法满足这个需求了,于是重读了一遍http://www.gnu.org/software/sed/manual/sed.htmlsed的教程,开始渐渐懂得sed的工作原理以及如何使用sed的高级功能。在分析高级应用的例子之前,我们来了解下sed的工作原理,这至关重要。

sed工作原理

sed是一个流文本处理工具,从文件头到文件尾读取,一次只读取一行,并完成一系列操作才继续读取下一行。sed维护两个数据缓冲区,一个是pattern space,一个是hold space。它们初始都为空。pattern space是活跃缓冲区,每一次循环都会清空再存入下一行内容。hold space一个辅助的空间,不会在完成一个循环后清空,会一直保持,它的内容来自使用h,H,g,G命令得来。

sed读取输入流的一行,在读取下一行之前,需要做如下操作(完成这些操作视为完成一个循环):sed从输入流读取一行,删除换行符,并把内容放到pattern space。然后命令开始对pattern space进行操作。每个命令可以有address关联,如/devops.webres.wang/a\hello,/devops.webres.wang/是搜索包括devops.webres.wang的pattern space,然后再执行a\hello增加hello字符操作,/devops.webres.wang/即为address,a为命令。只有address为真时,即匹配成功时,才执行后面的命令。

除非使用特殊的命令,如”D”,否则pattern space会在两个循环之间被清空。而hold space则会保持不变,hold space的内容可以使用‘h’, ‘H’, ‘x’, ‘g’, ‘G’的命令来操作。

高级应用示例分析

下面的例子来自http://www.gnu.org/software/sed/manual/sed.html
示例一
下面的脚本实现了每行80列宽中间对齐,假如文件中有aabb和ccccdddd两行。

  1. #!/usr/bin/sed -f
  2.     
  3.      # Put 80 spaces in the buffer
  4.      1 {
  5.        x
  6.        s/^$/          /
  7.        s/^.*$/&&&&&&&&/
  8.        x
  9.      }
  10.     
  11.      # del leading and trailing spaces
  12.      y/t/ /
  13.      s/^ *//
  14.      s/ *$//
  15.     
  16.      # add a newline and 80 spaces to end of line
  17.      G
  18.     
  19.      # keep first 81 chars (80 + a newline)
  20.      s/^(.{81}).*$/1/
  21.     
  22.      # 2 matches half of the spaces, which are moved to the beginning
  23.      s/^(.*)n(.*)2/21/

代码分析:

  1. 读取第一行时,pattern space为aabb,hold space为空。
  2. 以下命令分析:
  3. 1 {
  4.    x
  5.    s/^$/          /
  6.    s/^.*$/&&&&&&&&/
  7.    x
  8.    }
  9. 匹配第一行,执行如下命令,
  10. 执行x命令:交换pattern space和hold space的内容,结果是,pattern space内容为空,hold space为aabb。
  11. 执行s/^$/          /命令:pattern space为8个空格,hold space不变。
  12. 执行s/^.*$/&&&&&&&&/命令:现在pattern space的空格为80个,hold space不变。
  13. 执行x命令:交换它们的内容,pattern space内容为aabb,hold space为80个空格。
  14.  
  15. 继续执行如下命令:
  16. y/t/ /:替换tab为一个空格
  17. s/^ *//:删除行尾空格
  18. s/ *$//:删除行首空格
  19.  
  20. 执行G命令:pattern space附加一换行符,并附加hold space内容到pattern space,结果是,pattern space为aabb+n+80个空格,hold space保持不变。
  21.  
  22. s/^(.{81}).*$/1/命令:用s命令从行首至行尾取81个字符,包括了换行符。
  23.  
  24. s/^(.*)n(.*)2/21/命令:用正则把pattern space后面的空格分半,并移至行首,这样就实现了80列宽度中间对齐。
  25.  
  26. 继续下面的行读取时,hold space的内容会一直保持不变。

示例二
下面的例子实现了为数字加1的效果,比如一个文件number.txt,文件内容为:

  1. 6

sed代码:

  1. #!/usr/bin/sed -f
  2.     
  3.      /[^0-9]/ d
  4.     
  5.      # replace all leading 9s by _ (any other character except digits, could
  6.      # be used)
  7.      :d
  8.      s/9(_*)$/_1/
  9.      td
  10.     
  11.      # incr last digit only.  The first line adds a most-significant
  12.      # digit of 1 if we have to add a digit.
  13.      #
  14.      # The tn commands are not necessary, but make the thing
  15.      # faster
  16.     
  17.      s/^(_*)$/11/; tn
  18.      s/8(_*)$/91/; tn
  19.      s/7(_*)$/81/; tn
  20.      s/6(_*)$/71/; tn
  21.      s/5(_*)$/61/; tn
  22.      s/4(_*)$/51/; tn
  23.      s/3(_*)$/41/; tn
  24.      s/2(_*)$/31/; tn
  25.      s/1(_*)$/21/; tn
  26.      s/0(_*)$/11/; tn
  27.     
  28.      :n
  29.      y/_/0/

代码分析:

  1. 读取第一行6,放到pattern space,
  2. 执行/[^0-9]/ d:删除不是纯数字的行,pattern space为6
  3. 执行:d :标记下面的命令为子命令,用于跳转
  4. s/9(_*)$/_1/:替换9为_,此时pattern space 6不变。
  5. td :测试label d的子命令是否更改pattern space,如果更改,则跳回d标记处,否则继续往下执行。
  6.  
  7. s/^(_*)$/11/; tn
  8. s/8(_*)$/91/; tn
  9. s/7(_*)$/81/; tn
  10. 执行了以上命令,pattern space还是6
  11. s/6(_*)$/71/; tn
  12. 6替换成了7,此时pattern space为7
  13.  
  14. s/5(_*)$/61/; tn
  15. s/4(_*)$/51/; tn
  16. s/3(_*)$/41/; tn
  17. s/2(_*)$/31/; tn
  18. s/1(_*)$/21/; tn
  19. s/0(_*)$/11/; tn
  20. :n
  21. y/_/0/
  22. 执行以上几个命令,pattern space还是7,打印出7,继续下一循环。

示例三
此例子实现了每行倒序的效果。

  1. #!/usr/bin/sed -f
  2. /../! b
  3.  
  4. # Reverse a line.  Begin embedding the line between two newlines
  5. s/^.*$/n&n/
  6.  
  7. # Move first character at the end.  The regexp matches until
  8. # there are zero or one characters between the markers
  9. tx
  10. 😡
  11. s/(n.)(.*)(.n)/321/
  12. tx
  13.  
  14. # Remove the newline markers
  15. s/n//g

代码分析(以输入abcdef为例):

  1. 读取abcdef,
  2. pattern space为abcdef
  3. /../! b:如果只有一个字符,直接打印,中止此循环,进入下一循环。
  4.  
  5. s/^.*$/n&n/:用两个换行符包围abcdef,此刻pattern space为nabcdefn。
  6.  
  7. 😡
  8. s/(n.)(.*)(.n)/321/
  9. tx
  10.  
  11. 经过s命令后,1为na,2为bcde,3为fn,pattern space变为fnbcdena,此刻首尾字符换了位置。
  12. 执行tx命令,发现pattern space已经改变,跳转到x子命令,继续替换操作。
  13. 再次执行s命令后,换行符中间的字符又首尾调换了一次,pattern space为fencdnba
  14. 再一次s命令,pattern space为fednncba,
  15. 再一次s命令,pattern space不变,
  16. tx检测pattern space不变,于是往下执行,
  17. s/n//g:全局替换n,于是pattern space为fedcba,打印出fedcba。

示例四
下面的示例实现了以行为单位对文件进行倒序查看,相当于linux下的tac命令。

  1. #!/usr/bin/sed -nf
  2.  
  3. # reverse all lines of input, i.e. first line became last, …
  4.  
  5. # from the second line, the buffer (which contains all previous lines)
  6. # is *appended* to current line, so, the order will be reversed
  7. 1! G
  8.  
  9. # on the last line we’re done — print everything
  10. $ p
  11.  
  12. # store everything on the buffer again
  13. h

代码分析:
以文件内容为:
devops.webres.wang
www.baidu.com
www.qq.com
为例:

  1. 读取第一行:
  2. pattern space为devops.webres.wang
  3.  
  4. 1! G,如果不是第一行,执行G,附加一换行符到pattern space,再附加hold space内容到pattern space。此刻pattern space还是devops.webres.wang
  5.  
  6. $ p 如果到文件尾,则打印。
  7.  
  8. h命令:用pattern space内容替换hold space内容,现在pattern space和hold space都为devops.webres.wang。
  9.  
  10. 读取第二行
  11. pattern space为bb,hold space为devops.webres.wang
  12.  
  13. 1! G: pattern space为www.baidu.comndevops.webres.wang
  14. h命令:pattern space和hold space都为www.baidu.comndevops.webres.wang
  15.  
  16. 读取第三行
  17. pattern space为www.qq.com,hold space为www.baidu.comndevops.webres.wang
  18.  
  19. 1! G:pattern space为www.qq.comnwww.baidu.comndevops.webres.wang
  20. h命令:pattern space和hold space都为www.qq.comnwww.baidu.comndevops.webres.wang
  21.  
  22. $ p:已到文件尾,执行打印paatern space操作,结果为:
  23. www.qq.com
  24. www.baidu.com
  25. devops.webres.wang

gnu关于sed教程还有很多示例涉及到sed的高级功能,时间有限,先分析到这里。有空再继续。下篇日志贴出并分析使用sed解析nginx配置文件中的server {}代码块,实现列出虚拟主机的server_name root等信息,以及指定server_name的虚拟主机删除操作。
最后介绍一个很好用的sed debug工具,它可以显示出所有pattern space和hold space实时状态。http://aurelio.net/projects/sedsed/

修改SEO Smart Links兼容syntaxhighlighter

为了防止代码出错,之前一直是用coolcode来展示代码,这个是不支持bash高亮。之前也找过几次,也没找到支持bash的,就算支持,也不好用。coolcode虽然一直在用来展未代码防止出错,但没有高亮bash一直挺不爽,今天试用了SyntaxHighlighter Evolved,还不错,但不知道为什么无法开启工具条,就这样用着吧。安装好测试发现SEO Smart Links把高亮里的代码添加了链接,于是有了此文,下面是修改SEO Smart Links来兼容syntaxhighlighter的方法。
1、找到

  1. add_filter(‘the_content’,  array(&$this, ‘SEOLinks_the_content_filter’), 10);

把10修改为5,有两处。
2、找到

  1. $text = preg_replace(‘%(<h.*?>)(.*?)(</h.*?>)%sie’, "’\1′.insertspecialchars(‘\2’).’\3’", $text);

在此条代码下面添加:

  1. $text = preg_replace(‘%(【bash】)(.*?)(【/bash】)%sie’, "’\1′.insertspecialchars(‘\2’).’\3’", $text);

3、找到

  1. $text = preg_replace(‘%(<h.*?>)(.*?)(</h.*?>)%sie’, "’\1′.removespecialchars(‘\2’).’\3’", $text);

在下面添加:

  1. $text = preg_replace(‘%(【bash】)(.*?)(【/bash】)%sie’, "’\1′.removespecialchars(‘\2’).’\3’", $text);

注意:
1、我这里用使用【bash】和【/bash】来标识代码,如果你是使用其它标识,你修改2、3步骤代码。
2、请将【】更改为[],这里使用【】是保证能正常显示出来。

shell脚本制作俄罗斯方块游戏

下面的脚本功能强大,make by xhchen,收藏一下。

 

  1. #!/bin/bash
  2.  
  3. # Tetris Game
  4. # 10.21.2003 xhchen<[email][email protected][/email]>
  5.  
  6. #APP declaration
  7. APP_NAME="${0##*[\/]}"
  8. APP_VERSION="1.0"
  9.  
  10.  
  11. #颜色定义
  12. cRed=1
  13. cGreen=2
  14. cYellow=3
  15. cBlue=4
  16. cFuchsia=5
  17. cCyan=6
  18. cWhite=7
  19. colorTable=($cRed $cGreen $cYellow $cBlue $cFuchsia $cCyan $cWhite)
  20.  
  21. #位置和大小
  22. iLeft=3
  23. iTop=2
  24. ((iTrayLeft = iLeft + 2))
  25. ((iTrayTop = iTop + 1))
  26. ((iTrayWidth = 10))
  27. ((iTrayHeight = 15))
  28.  
  29. #颜色设置
  30. cBorder=$cGreen
  31. cScore=$cFuchsia
  32. cScoreValue=$cCyan
  33.  
  34. #控制信号
  35. #改游戏使用两个进程,一个用于接收输入,一个用于游戏流程和显示界面;
  36. #当前者接收到上下左右等按键时,通过向后者发送signal的方式通知后者。
  37. sigRotate=25
  38. sigLeft=26
  39. sigRight=27
  40. sigDown=28
  41. sigAllDown=29
  42. sigExit=30
  43.  
  44. #七中不同的方块的定义
  45. #通过旋转,每种方块的显示的样式可能有几种
  46. box0=(0 0 0 1 1 0 1 1)
  47. box1=(0 2 1 2 2 2 3 2 1 0 1 1 1 2 1 3)
  48. box2=(0 0 0 1 1 1 1 2 0 1 1 0 1 1 2 0)
  49. box3=(0 1 0 2 1 0 1 1 0 0 1 0 1 1 2 1)
  50. box4=(0 1 0 2 1 1 2 1 1 0 1 1 1 2 2 2 0 1 1 1 2 0 2 1 0 0 1 0 1 1 1 2)
  51. box5=(0 1 1 1 2 1 2 2 1 0 1 1 1 2 2 0 0 0 0 1 1 1 2 1 0 2 1 0 1 1 1 2)
  52. box6=(0 1 1 1 1 2 2 1 1 0 1 1 1 2 2 1 0 1 1 0 1 1 2 1 0 1 1 0 1 1 1 2)
  53. #所有其中方块的定义都放到box变量中
  54. box=(${box0[@]} ${box1[@]} ${box2[@]} ${box3[@]} ${box4[@]} ${box5[@]} ${box6[@]})
  55. #各种方块旋转后可能的样式数目
  56. countBox=(1 2 2 2 4 4 4)
  57. #各种方块再box数组中的偏移
  58. offsetBox=(0 1 3 5 7 11 15)
  59.  
  60. #每提高一个速度级需要积累的分数
  61. iScoreEachLevel=50        #be greater than 7
  62.  
  63. #运行时数据
  64. sig=0                #接收到的signal
  65. iScore=0        #总分
  66. iLevel=0        #速度级
  67. boxNew=()        #新下落的方块的位置定义
  68. cBoxNew=0        #新下落的方块的颜色
  69. iBoxNewType=0        #新下落的方块的种类
  70. iBoxNewRotate=0        #新下落的方块的旋转角度
  71. boxCur=()        #当前方块的位置定义
  72. cBoxCur=0        #当前方块的颜色
  73. iBoxCurType=0        #当前方块的种类
  74. iBoxCurRotate=0        #当前方块的旋转角度
  75. boxCurX=-1        #当前方块的x坐标位置
  76. boxCurY=-1        #当前方块的y坐标位置
  77. iMap=()                #背景方块图表
  78.  
  79. #初始化所有背景方块为-1, 表示没有方块
  80. for ((i = 0; i < iTrayHeight * iTrayWidth; i++)); do iMap[$i]=-1; done
  81.  
  82.  
  83. #接收输入的进程的主函数
  84. function RunAsKeyReceiver()
  85. {
  86.         local pidDisplayer key aKey sig cESC sTTY
  87.  
  88.         pidDisplayer=$1
  89.         aKey=(0 0 0)
  90.  
  91.         cESC=`echo -ne "33"`
  92.         cSpace=`echo -ne "40"`
  93.  
  94.         #保存终端属性。在read -s读取终端键时,终端的属性会被暂时改变。
  95.         #如果在read -s时程序被不幸杀掉,可能会导致终端混乱,
  96.         #需要在程序退出时恢复终端属性。
  97.         sTTY=`stty -g`
  98.  
  99.         #捕捉退出信号
  100.         trap "MyExit;" INT TERM
  101.         trap "MyExitNoSub;" $sigExit
  102.  
  103.         #隐藏光标
  104.         echo -ne "33[?25l"
  105.  
  106.  
  107.         while :
  108.         do
  109.                 #读取输入。注-s不回显,-n读到一个字符立即返回
  110.                 read -s -n 1 key
  111.  
  112.                 aKey[0]=${aKey[1]}
  113.                 aKey[1]=${aKey[2]}
  114.                 aKey[2]=$key
  115.                 sig=0
  116.  
  117.                 #判断输入了何种键
  118.                 if [[ $key == $cESC && ${aKey[1]} == $cESC ]]
  119.                 then
  120.                         #ESC键
  121.                         MyExit
  122.                 elif [[ ${aKey[0]} == $cESC && ${aKey[1]} == "[" ]]
  123.                 then
  124.                         if [[ $key == "A" ]]; then sig=$sigRotate        #<向上键>
  125.                         elif [[ $key == "B" ]]; then sig=$sigDown        #<向下键>
  126.                         elif [[ $key == "D" ]]; then sig=$sigLeft        #<向左键>
  127.                         elif [[ $key == "C" ]]; then sig=$sigRight        #<向右键>
  128.                         fi
  129.                 elif [[ $key == "W" || $key == "w" ]]; then sig=$sigRotate        #W, w
  130.                 elif [[ $key == "S" || $key == "s" ]]; then sig=$sigDown        #S, s
  131.                 elif [[ $key == "A" || $key == "a" ]]; then sig=$sigLeft        #A, a
  132.                 elif [[ $key == "D" || $key == "d" ]]; then sig=$sigRight        #D, d
  133.                 elif [[ "[$key]" == "[]" ]]; then sig=$sigAllDown        #空格键
  134.                 elif [[ $key == "Q" || $key == "q" ]]                        #Q, q
  135.                 then
  136.                         MyExit
  137.                 fi
  138.  
  139.                 if [[ $sig != 0 ]]
  140.                 then
  141.                         #向另一进程发送消息
  142.                         kill -$sig $pidDisplayer
  143.                 fi
  144.         done
  145. }
  146.  
  147. #退出前的恢复
  148. function MyExitNoSub()
  149. {
  150.         local y
  151.  
  152.         #恢复终端属性
  153.         stty $sTTY
  154.         ((y = iTop + iTrayHeight + 4))
  155.  
  156.         #显示光标
  157.         echo -e "33[?25h33[${y};0H"
  158.         exit
  159. }
  160.  
  161.  
  162. function MyExit()
  163. {
  164.         #通知显示进程需要退出
  165.         kill -$sigExit $pidDisplayer
  166.  
  167.         MyExitNoSub
  168. }
  169.  
  170.  
  171. #处理显示和游戏流程的主函数
  172. function RunAsDisplayer()
  173. {
  174.         local sigThis
  175.         InitDraw
  176.  
  177.         #挂载各种信号的处理函数
  178.         trap "sig=$sigRotate;" $sigRotate
  179.         trap "sig=$sigLeft;" $sigLeft
  180.         trap "sig=$sigRight;" $sigRight
  181.         trap "sig=$sigDown;" $sigDown
  182.         trap "sig=$sigAllDown;" $sigAllDown
  183.         trap "ShowExit;" $sigExit
  184.  
  185.         while :
  186.         do
  187.                 #根据当前的速度级iLevel不同,设定相应的循环的次数
  188.                 for ((i = 0; i < 21 – iLevel; i++))
  189.                 do
  190.                         sleep 0.02
  191.                         sigThis=$sig
  192.                         sig=0
  193.  
  194.                         #根据sig变量判断是否接受到相应的信号
  195.                         if ((sigThis == sigRotate)); then BoxRotate;        #旋转
  196.                         elif ((sigThis == sigLeft)); then BoxLeft;        #左移一列
  197.                         elif ((sigThis == sigRight)); then BoxRight;        #右移一列
  198.                         elif ((sigThis == sigDown)); then BoxDown;        #下落一行
  199.                         elif ((sigThis == sigAllDown)); then BoxAllDown;        #下落到底
  200.                         fi
  201.                 done
  202.                 #kill -$sigDown $$
  203.                 BoxDown        #下落一行
  204.         done
  205. }
  206.  
  207.  
  208. #BoxMove(y, x), 测试是否可以把移动中的方块移到(x, y)的位置, 返回0则可以, 1不可以
  209. function BoxMove()
  210. {
  211.         local j i x y xTest yTest
  212.         yTest=$1
  213.         xTest=$2
  214.         for ((j = 0; j < 8; j += 2))
  215.         do
  216.                 ((i = j + 1))
  217.                 ((y = ${boxCur[$j]} + yTest))
  218.                 ((x = ${boxCur[$i]} + xTest))
  219.                 if (( y < 0 || y >= iTrayHeight || x < 0 || x >= iTrayWidth))
  220.                 then
  221.                         #撞到墙壁了
  222.                         return 1
  223.                 fi
  224.                 if ((${iMap[y * iTrayWidth + x]} != -1 ))
  225.                 then
  226.                         #撞到其他已经存在的方块了
  227.                         return 1
  228.                 fi
  229.         done
  230.         return 0;
  231. }
  232.  
  233.  
  234. #将当前移动中的方块放到背景方块中去,
  235. #并计算新的分数和速度级。(即一次方块落到底部)
  236. function Box2Map()
  237. {
  238.         local j i x y xp yp line
  239.  
  240.         #将当前移动中的方块放到背景方块中去
  241.         for ((j = 0; j < 8; j += 2))
  242.         do
  243.                 ((i = j + 1))
  244.                 ((y = ${boxCur[$j]} + boxCurY))
  245.                 ((x = ${boxCur[$i]} + boxCurX))
  246.                 ((i = y * iTrayWidth + x))
  247.                 iMap[$i]=$cBoxCur
  248.         done
  249.  
  250.         #消去可被消去的行
  251.         line=0
  252.         for ((j = 0; j < iTrayWidth * iTrayHeight; j += iTrayWidth))
  253.         do
  254.                 for ((i = j + iTrayWidth – 1; i >= j; i–))
  255.                 do
  256.                         if ((${iMap[$i]} == -1)); then break; fi
  257.                 done
  258.                 if ((i >= j)); then continue; fi
  259.  
  260.                 ((line++))
  261.                 for ((i = j – 1; i >= 0; i–))
  262.                 do
  263.                         ((x = i + iTrayWidth))
  264.                         iMap[$x]=${iMap[$i]}
  265.                 done
  266.                 for ((i = 0; i < iTrayWidth; i++))
  267.                 do
  268.                         iMap[$i]=-1
  269.                 done
  270.         done
  271.  
  272.         if ((line == 0)); then return; fi
  273.  
  274.         #根据消去的行数line计算分数和速度级
  275.         ((x = iLeft + iTrayWidth * 2 + 7))
  276.         ((y = iTop + 11))
  277.         ((iScore += line * 2 – 1))
  278.         #显示新的分数
  279.         echo -ne "33[1m33[3${cScoreValue}m33[${y};${x}H${iScore}         "
  280.         if ((iScore % iScoreEachLevel < line * 2 – 1))
  281.         then
  282.                 if ((iLevel < 20))
  283.                 then
  284.                         ((iLevel++))
  285.                         ((y = iTop + 14))
  286.                         #显示新的速度级
  287.                         echo -ne "33[3${cScoreValue}m33[${y};${x}H${iLevel}        "
  288.                 fi
  289.         fi
  290.         echo -ne "33[0m"
  291.  
  292.  
  293.         #重新显示背景方块
  294.         for ((y = 0; y < iTrayHeight; y++))
  295.         do
  296.                 ((yp = y + iTrayTop + 1))
  297.                 ((xp = iTrayLeft + 1))
  298.                 ((i = y * iTrayWidth))
  299.                 echo -ne "33[${yp};${xp}H"
  300.                 for ((x = 0; x < iTrayWidth; x++))
  301.                 do
  302.                         ((j = i + x))
  303.                         if ((${iMap[$j]} == -1))
  304.                         then
  305.                                 echo -ne "  "
  306.                         else
  307.                                 echo -ne "33[1m33[7m33[3${iMap[$j]}m33[4${iMap[$j]}m[]33[0m"
  308.                         fi
  309.                 done
  310.         done
  311. }
  312.  
  313.  
  314. #下落一行
  315. function BoxDown()
  316. {
  317.         local y s
  318.         ((y = boxCurY + 1))        #新的y坐标
  319.         if BoxMove $y $boxCurX        #测试是否可以下落一行
  320.         then
  321.                 s="`DrawCurBox 0`"        #将旧的方块抹去
  322.                 ((boxCurY = y))
  323.                 s="$s`DrawCurBox 1`"        #显示新的下落后方块
  324.                 echo -ne $s
  325.         else
  326.                 #走到这儿, 如果不能下落了
  327.                 Box2Map                #将当前移动中的方块贴到背景方块中
  328.                 RandomBox        #产生新的方块
  329.         fi
  330. }
  331.  
  332. #左移一列
  333. function BoxLeft()
  334. {
  335.         local x s
  336.         ((x = boxCurX – 1))
  337.         if BoxMove $boxCurY $x
  338.         then
  339.                 s=`DrawCurBox 0`
  340.                 ((boxCurX = x))
  341.                 s=$s`DrawCurBox 1`
  342.                 echo -ne $s
  343.         fi
  344. }
  345.  
  346. #右移一列
  347. function BoxRight()
  348. {
  349.         local x s
  350.         ((x = boxCurX + 1))
  351.         if BoxMove $boxCurY $x
  352.         then
  353.                 s=`DrawCurBox 0`
  354.                 ((boxCurX = x))
  355.                 s=$s`DrawCurBox 1`
  356.                 echo -ne $s
  357.         fi
  358. }
  359.  
  360.  
  361. #下落到底
  362. function BoxAllDown()
  363. {
  364.         local k j i x y iDown s
  365.         iDown=$iTrayHeight
  366.  
  367.         #计算一共需要下落多少行
  368.         for ((j = 0; j < 8; j += 2))
  369.         do
  370.                 ((i = j + 1))
  371.                 ((y = ${boxCur[$j]} + boxCurY))
  372.                 ((x = ${boxCur[$i]} + boxCurX))
  373.                 for ((k = y + 1; k < iTrayHeight; k++))
  374.                 do
  375.                         ((i = k * iTrayWidth + x))
  376.                         if (( ${iMap[$i]} != -1)); then break; fi
  377.                 done
  378.                 ((k -= y + 1))
  379.                 if (( $iDown > $k )); then iDown=$k; fi
  380.         done
  381.  
  382.         s=`DrawCurBox 0`        #将旧的方块抹去
  383.         ((boxCurY += iDown))
  384.         s=$s`DrawCurBox 1`        #显示新的下落后的方块
  385.         echo -ne $s
  386.         Box2Map                #将当前移动中的方块贴到背景方块中
  387.         RandomBox        #产生新的方块
  388. }
  389.  
  390.  
  391. #旋转方块
  392. function BoxRotate()
  393. {
  394.         local iCount iTestRotate boxTest j i s
  395.         iCount=${countBox[$iBoxCurType]}        #当前的方块经旋转可以产生的样式的数目
  396.  
  397.         #计算旋转后的新的样式
  398.         ((iTestRotate = iBoxCurRotate + 1))
  399.         if ((iTestRotate >= iCount))
  400.         then
  401.                 ((iTestRotate = 0))
  402.         fi
  403.  
  404.         #更新到新的样式, 保存老的样式(但不显示)
  405.         for ((j = 0, i = (${offsetBox[$iBoxCurType]} + $iTestRotate) * 8; j < 8; j++, i++))
  406.         do
  407.                 boxTest[$j]=${boxCur[$j]}
  408.                 boxCur[$j]=${box[$i]}
  409.         done
  410.  
  411.         if BoxMove $boxCurY $boxCurX        #测试旋转后是否有空间放的下
  412.         then
  413.                 #抹去旧的方块
  414.                 for ((j = 0; j < 8; j++))
  415.                 do
  416.                         boxCur[$j]=${boxTest[$j]}
  417.                 done
  418.                 s=`DrawCurBox 0`
  419.  
  420.                 #画上新的方块
  421.                 for ((j = 0, i = (${offsetBox[$iBoxCurType]} + $iTestRotate) * 8; j < 8; j++, i++))
  422.                 do
  423.                         boxCur[$j]=${box[$i]}
  424.                 done
  425.                 s=$s`DrawCurBox 1`
  426.                 echo -ne $s
  427.                 iBoxCurRotate=$iTestRotate
  428.         else
  429.                 #不能旋转,还是继续使用老的样式
  430.                 for ((j = 0; j < 8; j++))
  431.                 do
  432.                         boxCur[$j]=${boxTest[$j]}
  433.                 done
  434.         fi
  435. }
  436.  
  437.  
  438. #DrawCurBox(bDraw), 绘制当前移动中的方块, bDraw为1, 画上, bDraw为0, 抹去方块。
  439. function DrawCurBox()
  440. {
  441.         local i j t bDraw sBox s
  442.         bDraw=$1
  443.  
  444.         s=""
  445.         if (( bDraw == 0 ))
  446.         then
  447.                 sBox="4040"
  448.         else
  449.                 sBox="[]"
  450.                 s=$s"33[1m33[7m33[3${cBoxCur}m33[4${cBoxCur}m"
  451.         fi
  452.  
  453.         for ((j = 0; j < 8; j += 2))
  454.         do
  455.                 ((i = iTrayTop + 1 + ${boxCur[$j]} + boxCurY))
  456.                 ((t = iTrayLeft + 1 + 2 * (boxCurX + ${boxCur[$j + 1]})))
  457.                 #33[y;xH, 光标到(x, y)处
  458.                 s=$s"33[${i};${t}H${sBox}"
  459.         done
  460.         s=$s"33[0m"
  461.         echo -n $s
  462. }
  463.  
  464.  
  465. #更新新的方块
  466. function RandomBox()
  467. {
  468.         local i j t
  469.  
  470.         #更新当前移动的方块
  471.         iBoxCurType=${iBoxNewType}
  472.         iBoxCurRotate=${iBoxNewRotate}
  473.         cBoxCur=${cBoxNew}
  474.         for ((j = 0; j < ${#boxNew[@]}; j++))
  475.         do
  476.                 boxCur[$j]=${boxNew[$j]}
  477.         done
  478.  
  479.  
  480.         #显示当前移动的方块
  481.         if (( ${#boxCur[@]} == 8 ))
  482.         then
  483.                 #计算当前方块该从顶端哪一行"冒"出来
  484.                 for ((j = 0, t = 4; j < 8; j += 2))
  485.                 do
  486.                         if ((${boxCur[$j]} < t)); then t=${boxCur[$j]}; fi
  487.                 done
  488.                 ((boxCurY = -t))
  489.                 for ((j = 1, i = -4, t = 20; j < 8; j += 2))
  490.                 do
  491.                         if ((${boxCur[$j]} > i)); then i=${boxCur[$j]}; fi
  492.                         if ((${boxCur[$j]} < t)); then t=${boxCur[$j]}; fi
  493.                 done
  494.                 ((boxCurX = (iTrayWidth – 1 – i – t) / 2))
  495.  
  496.                 #显示当前移动的方块
  497.                 echo -ne `DrawCurBox 1`
  498.  
  499.                 #如果方块一出来就没处放,Game over!
  500.                 if ! BoxMove $boxCurY $boxCurX
  501.                 then
  502.                         kill -$sigExit ${PPID}
  503.                         ShowExit
  504.                 fi
  505.         fi
  506.  
  507.  
  508.  
  509.         #清除右边预显示的方块
  510.         for ((j = 0; j < 4; j++))
  511.         do
  512.                 ((i = iTop + 1 + j))
  513.                 ((t = iLeft + 2 * iTrayWidth + 7))
  514.                 echo -ne "33[${i};${t}H        "
  515.         done
  516.  
  517.         #随机产生新的方块
  518.         ((iBoxNewType = RANDOM % ${#offsetBox[@]}))
  519.         ((iBoxNewRotate = RANDOM % ${countBox[$iBoxNewType]}))
  520.         for ((j = 0, i = (${offsetBox[$iBoxNewType]} + $iBoxNewRotate) * 8; j < 8; j++, i++))
  521.         do
  522.                 boxNew[$j]=${box[$i]};
  523.         done
  524.  
  525.         ((cBoxNew = ${colorTable[RANDOM % ${#colorTable[@]}]}))
  526.  
  527.         #显示右边预显示的方块
  528.         echo -ne "33[1m33[7m33[3${cBoxNew}m33[4${cBoxNew}m"
  529.         for ((j = 0; j < 8; j += 2))
  530.         do
  531.                 ((i = iTop + 1 + ${boxNew[$j]}))
  532.                 ((t = iLeft + 2 * iTrayWidth + 7 + 2 * ${boxNew[$j + 1]}))
  533.                 echo -ne "33[${i};${t}H[]"
  534.         done
  535.         echo -ne "33[0m"
  536. }
  537.  
  538.  
  539. #初始绘制
  540. function InitDraw()
  541. {
  542.         clear
  543.         RandomBox        #随机产生方块,这时右边预显示窗口中有方快了
  544.         RandomBox        #再随机产生方块,右边预显示窗口中的方块被更新,原先的方块将开始下落
  545.         local i t1 t2 t3
  546.  
  547.         #显示边框
  548.         echo -ne "33[1m"
  549.         echo -ne "33[3${cBorder}m33[4${cBorder}m"
  550.  
  551.         ((t2 = iLeft + 1))
  552.         ((t3 = iLeft + iTrayWidth * 2 + 3))
  553.         for ((i = 0; i < iTrayHeight; i++))
  554.         do
  555.                 ((t1 = i + iTop + 2))
  556.                 echo -ne "33[${t1};${t2}H||"
  557.                 echo -ne "33[${t1};${t3}H||"
  558.         done
  559.  
  560.         ((t2 = iTop + iTrayHeight + 2))
  561.         for ((i = 0; i < iTrayWidth + 2; i++))
  562.         do
  563.                 ((t1 = i * 2 + iLeft + 1))
  564.                 echo -ne "33[${iTrayTop};${t1}H=="
  565.                 echo -ne "33[${t2};${t1}H=="
  566.         done
  567.         echo -ne "33[0m"
  568.  
  569.  
  570.         #显示"Score"和"Level"字样
  571.         echo -ne "33[1m"
  572.         ((t1 = iLeft + iTrayWidth * 2 + 7))
  573.         ((t2 = iTop + 10))
  574.         echo -ne "33[3${cScore}m33[${t2};${t1}HScore"
  575.         ((t2 = iTop + 11))
  576.         echo -ne "33[3${cScoreValue}m33[${t2};${t1}H${iScore}"
  577.         ((t2 = iTop + 13))
  578.         echo -ne "33[3${cScore}m33[${t2};${t1}HLevel"
  579.         ((t2 = iTop + 14))
  580.         echo -ne "33[3${cScoreValue}m33[${t2};${t1}H${iLevel}"
  581.         echo -ne "33[0m"
  582. }
  583.  
  584.  
  585. #退出时显示GameOVer!
  586. function ShowExit()
  587. {
  588.         local y
  589.         ((y = iTrayHeight + iTrayTop + 3))
  590.         echo -e "33[${y};0HGameOver!33[0m"
  591.         exit
  592. }
  593.  
  594.  
  595. #显示用法.
  596. function Usage
  597. {
  598.         cat << EOF
  599. Usage: $APP_NAME
  600. Start tetris game.
  601.  
  602.   -h, –help              display this help and exit
  603.       –version           output version information and exit
  604. EOF
  605. }
  606.  
  607.  
  608. #游戏主程序在这儿开始.
  609. if [[ "$1" == "-h" || "$1" == "–help" ]]; then
  610.         Usage
  611. elif [[ "$1" == "–version" ]]; then
  612.         echo "$APP_NAME $APP_VERSION"
  613. elif [[ "$1" == "–show" ]]; then
  614.         #当发现具有参数–show时,运行显示函数
  615.         RunAsDisplayer
  616. else
  617.         bash $0 –show&        #以参数–show将本程序再运行一遍
  618.         RunAsKeyReceiver $!        #以上一行产生的进程的进程号作为参数
  619. fi

 

如何在Linux终端里用Shell和C输出带颜色的文字

我们知道,使用ls命令列出文件列表时,不同的文件类型会用不同的颜色显示。那么如何实现这样带颜色的文本输出呢?答案并不复杂,不管是用shell还是C语言。

一、shell下的实现方法

先来讲在shell下,如何实现。用echo命令就可以实现,参看以下例子:

echo -e “\033[32mHello, world!”

当你在终端里敲下这条命令后,是不是发现系统用绿色输出了”Hello,world!”,不止如此,连之后的命令提示符都变成了绿色?不要着急,听我继续说。echo命令-e选项的作用是激活终端对反斜线转义符(即\)的解释。引号内\033用于引导非常规字符序列,在这里的作用就是引导设置输出属性,后边的[32m就是将前景色设置为绿色,字母m表示设置的属性类别,数字代表属性值。设置可以单独使用,例如:

echo -e “\033[0m”

这行命令的作用是恢复属性为默认值,也就是说0m设置项用于恢复默认值。现在你的终端是不是又一切正常了?

理解了这些,剩下的就简单了。用这种命令,除了设置文本前景色,还可以设置很多属性。下边列出其他的设置项:

————————————————————————–

\033[0m 关闭所有属性
\033[1m 设置高亮度
\033[4m 下划线
\033[5m 闪烁
\033[7m 反显
\033[8m 消隐
\033[30m 至 \33[37m 设置前景色
\033[40m 至 \33[47m 设置背景色
\033[nA 光标上移n行
\033[nB 光标下移n行
\033[nC 光标右移n行
\033[nD 光标左移n行
\033[y;xH设置光标位置
\033[2J 清屏
\033[K 清除从光标到行尾的内容
\033[s 保存光标位置
\033[u 恢复光标位置
\033[?25l 隐藏光标
\033[?25h 显示光标

————————————————————————–

各数字所代表的颜色如下:

字背景颜色范围:40—-49
40:黑
41:深红
42:绿
43:黄色
44:蓝色
45:紫色
46:深绿
47:白色

字颜色:30———–39
30:黑
31:红
32:绿
33:黄
34:蓝色
35:紫色
36:深绿
37:白色

另外,同类的多种设置项可以组合在一起,中间用分号(;)隔开。如下:

echo -e “\033[20;1H\033[1;4;32mHello,world\033[0m”

这行命令首先\033[20;1H将光标移动到终端第20行第1列,之后的\033[1;4;32m将文本属性设置为高亮、带下划线且颜色为绿色,然后输出Hello,world;最后\033[0m将终端属性恢复为默认值,这样就不会看到连命令完成后的命令提示符也变了样儿了。

通过以上各种命令的组合就可以实现对终端输出地复杂控制。

二、如何在C编程中实现?

理解了以上在Shell中的实现方法,关于在C中如何实现就很简单了。可以说只需要用printf函数代替上边的echo -e就OK了。参见下例:

int color = 32;

printf(“\033[20;1H\033[1;4;%dmHello, world.\033[0m”, color);

这个例子类似上边shell中最后那个例子,只是这里颜色值通过变量color来指定(当然,也可以直接指定)。

三、联想

看到这里你可能会想,是不是在其他编程语言里也可以用类似的方法实现对终端输出的控制呢?答案是肯定的!比如在python中,可以如下输出:

color=32

print “\033[20;1H\033[1;4;%dHello, world.\033[0m”%color

这个例子的效果跟上边C的例子是相同的。
转自:http://blog.csdn.net/acmee/article/details/6613060