Linux命令后台运行

转自北国的雨的cnblogs,我整理成markdown格式

有两种方式:

command & : 后台运行,你关掉终端会停止运行
nohup command & : 后台运行,你关掉终端也会继续运行

简介

Linux/Unix 区别于微软平台最大的优点就是真正的多用户,多任务。因此在任务管理上也有别具特色的管理思想。
我们知道,在 Windows 上面,我们要么让一个程序作为服务在后台一直运行,要么停止这个服务。而不能让程序在前台后台之间切换。而 Linux 提供了 fg 和bg 命令,让你轻松调度正在运行的任务。假设你发现前台运行的一个程序需要很长的时间,但是需要干其他的事情,你就可以用 Ctrl-Z ,挂起这个程序,然后可以看到系统提示:

[1]+ Stopped /root/bin/rsync.sh

然后我们可以把程序调度到后台执行:(bg 后面的数字为作业号)

#bg 1
[1]+ /root/bin/rsync.sh &

用 jobs 命令查看正在运行的任务:

#jobs    
[1]+ Running /root/bin/rsync.sh &

如果想把它调回到前台运行,可以用

#fg 1
/root/bin/rsync.sh

这样,你在控制台上就只能等待这个任务完成了。

& 将指令丢到后台中去执行
[ctrl]+z 將前台任务丟到后台中暂停
jobs 查看后台的工作状态
fg %jobnumber 将后台的任务拿到前台来处理
bg %jobnumber 将任务放到后台中去处理
kill 管理后台的任务

&

在Linux中,当在前台运行某个作业时,终端被该作业占据;而在后台运行作业时,它不会占据终端。可以使用&命令把作业放到后台执行。实际上,这样是将命令放入到一个作业队列中了:

$ ./test.sh &
[1] 17208

$ jobs -l
[1]+ 17208 Running                 ./test.sh &

在后台运行作业时要当心:需要用户交互的命令不要放在后台执行,因为这样你的机器就会在那里傻等。不过,作业在后台运行一样会将结果输出到屏幕上,干扰你的工作。如果放在后台运行的作业会产生大量的输出,最好使用下面的方法把它的输出重定向到某个文件中:

command >out.file 2>&1 &

在上面的例子中,2>&1表示所有的标准输出和错误输出都将被重定向到一个叫做out.file 的文件中。 当你成功地提交进程以后,就会显示出一个进程号,可以用它来监控该进程,或杀死它。
例:查找名为“httpd.conf”的文件,并把所有标准输出和错误输出重定向到find.dt的文件中:

# find /etc/httpd/ -name "httpd.conf" -print >find.dt 2>&1 & 
[2] 7832 

成功提交该命令之后,系统给出了它的进程号7832。 对于已经在前台执行的命令,也可以重新放到后台执行,首先按ctrl+z暂停已经运行的进程,然后使用bg命令将停止的作业放到后台运行,例如对正在前台执行的tesh.sh使用ctrl+z挂起它:

$ ./test.sh
[1]+ Stopped                 ./test.sh

$ bg %1
[1]+ ./test.sh &

$ jobs -l
[1]+ 22794 Running                 ./test.sh &

但是如上方到后台执行的进程,其父进程还是当前终端shell的进程,而一旦父进程退出,则会发送hangup信号给所有子进程,子进程收到hangup以后也会退出。如果我们要在退出shell的时候继续运行进程,则需要使用nohup忽略hangup信号,或者setsid将将父进程设为init进程(进程号为1)

$ echo $$
21734

$ nohup ./test.sh &
[1] 29016

$ ps -ef | grep test
515      29710 21734 0 11:47 pts/12   00:00:00 /bin/sh ./test.sh
515      29713 21734 0 11:47 pts/12   00:00:00 grep test
$ setsid ./test.sh &
[1] 409

$ ps -ef | grep test
515        410     1 0 11:49 ?        00:00:00 /bin/sh ./test.sh
515        413 21734 0 11:49 pts/12   00:00:00 grep test

上面的试验演示了使用nohup/setsid加上&使进程在后台运行,同时不受当前shell退出的影响。那么对于已经在后台运行的进程,该怎么办呢?可以使用disown命令:

$ ./test.sh &
[1] 2539

$ jobs -l
[1]+ 2539 Running                 ./test.sh &

$ disown -h %1

$ ps -ef | grep test
515        410     1 0 11:49 ?        00:00:00 /bin/sh ./test.sh
515       2542 21734 0 11:52 pts/12   00:00:00 grep test

另外还有一种方法,即使将进程在一个subshell中执行,其实这和setsid异曲同工。方法很简单,将命令用括号() 括起来即可:

$ (./test.sh &)

$ ps -ef | grep test
515        410     1 0 11:49 ?        00:00:00 /bin/sh ./test.sh
515      12483 21734 0 11:59 pts/12   00:00:00 grep test

注:本文试验环境为Red Hat Enterprise Linux AS release 4 (Nahant Update 5),shell为/bin/bash,不同的OS和shell可能命令有些不一样。例如AIX的ksh,没有disown,但是可以使用nohup -p PID来获得disown同样的效果。

还有一种更加强大的方式是使用screen,首先创建一个断开模式的虚拟终端,然后用-r选项重新连接这个虚拟终端,在其中执行的任何命令,都能达到nohup的效果,这在有多个命令需要在后台连续执行的时候比较方便:

$ screen -dmS screen_test

$ screen -list
There is a screen on:
        27963.screen_test       (Detached)
1 Socket in /tmp/uscreens/S-jiangfeng.

$ screen -r screen_test

nohup

如果你正在运行一个进程,而且你觉得在退出帐户时该进程还不会结束,那么可以使用nohup命令。该命令可以在你退出帐户之后继续运行相应的进程。nohup就是不挂起的意思( no hang up)。 该命令的一般形式为:

nohup conmmand &

如果使用nohup命令提交作业,那么在缺省情况下该作业的所有输出都被重定向到一个名为nohup.out的文件中,除非另外指定了输出文件:

nohup command > myout.file 2>&1 

在上面的例子中,输出被重定向到myout.file文件中。

、.*,?,[…],[!…]等

下面就是这些特殊字符:

  • *匹配文件名中的任何字符串,包括空字符串。
  • ? 匹配文件名中的任何单个字符。
  • […] 匹配[ ]中所包含的任何字符。
  • [!…] 匹配[ ]中非感叹号!之后的字符。
  • 当s h e l l遇到上述字符时,就会把它们当作特殊字符,而不是文件名中的普通字符,这样用户就可以用它们来匹配相应的文件名。

一些例子

  1. 列出以i或o开头的文件名:

    #ls [io]*
    
  2. 列出log.开头、后面跟随一个数字、然后可以是任意字符串的文件名:

    #ls log.[0-9]* 
    
  3. 与例二相反,列出log.开头、后面不跟随一个数字、然后可以是任意字符串的文件名:

    #ls log.[!0-9]* 
    
  4. 列出所有以LPS开头、中间可以是任何两个字符,最后以1结尾的文件名:

    #ls LPS??1
    
  5. 列出所有以大写字母开头的文件名:

    $ ls [A-Z]*
    
  6. 列出所有以. 开头的文件名(隐含文件,例如. profile、.rhosts、.histo ry等):

    $ ls .*
    

其他相关命令:

  • jobs:查看当前有多少在后台运行的命令
  • fg:将后台中的命令调至前台继续运行。如果后台中有多个命令,可以用 fg %jobnumber将选中的命令调出,%jobnumber是通过jobs命令查到的后台正在执行的命令的序号(不是pid)
  • bg:将一个在后台暂停的命令,变成继续执行。如果后台中有多个命令,可以用bg %jobnumber将选中的命令调出,%jobnumber是通过jobs命令查到的后台正在执行的命令的序号(不是pid)

杀死进程

杀死已经启动的程序和普通方式一样:

pkill -9 name
killall name
kill pid

Mac下配置iTerm2+oh-my-zsh

这篇帖子,记录一下这两天Mac下安装iTerm2,以及配置zsh + oh-my-zsh的一些步骤和坑

上个星期逛知乎的时候翻到一篇帖子:程序员如何优雅的使用Mac,看完之后对里面的iTerm2非常感兴趣,一通折腾。

安装iTerm2

非常简单,直接去官网下载最新的就好了。

安装zsh

zsh是一个可以替换bash的东西,拥有强大的功能,Mac就用万能的brew安装就好了

brew info zsh

安装完成后会生成zsh的配置文件,放在~/.zshrc。这个配置文件是zsh的入口,每次开启新的terminal的时候都会读取这里的配置。不过先不要着急改这个配置文件,待会它就会被omz替换掉了。

安装oh-my-zsh

oh-my-zsh是一套为方便配置zsh开发的开源框架,主要功能是增加了插件和主题。到目前为止,据官网所说,已经有180+个插件和140+个主题了。在官网下方可以方便的找到安装方法

通过Curl安装

$ curl -L https://raw.github.com/robbyrussell/oh-my-zsh/master/tools/install.sh | sh

通过Wget安装

$ wget https://raw.github.com/robbyrussell/oh-my-zsh/master/tools/install.sh -O - | sh

上面的任意一个命令会下载一个sh,然后运行它,运行完毕之后oh-my-zsh就安装完成了。omz 的安装会把之前提到的~/.zshrc备份为~/.zshrc.pre-oh-my-zsh,然后生成一个新的~/.zshrc替换掉原有的。打开这个新的看看里面的内容,有很大一部分注释,但是需要注意只有这几行:

export ZSH=/Users/zgr0629/.oh-my-zsh
ZSH_THEME="robbyrussell"
DEFAULT_USER="zgr0629"
plugins=(git brew node npm)
source $ZSH/oh-my-zsh.sh

第一行定义了一个ZSH的变量,记录了omz的根目录
ZSH_THEME就是omz的主题了
DEFAULT_USER这里填上你的用户名之后可以隐藏命令行前面的用户,洁癖患者必备。
plugins这里按照自己的需要,可以加载各种插件。
最后,source命令把上述配置好的变量一并带到omz的脚本里执行,从而达到配置zsh的目的。

主题!主题!主题!

agnoster主题
对,主题是一个非常重要的功能,谁希望整天对着黑白的shell做一整天。OMZ有非常多的主题,这个连接里有很多可以选,大部分都有截图,不过基本上都是用agnoster,配上强大的solarized配色方案,和powerline(提供git提示符),就会有下面的效果了。

使用这个agnoster主题很简单,先

vi ~/.zshrc

把里面的

ZSH_THEME="robbyrussell"

改为

ZSH_THEME="agnoster"

然后:wq,重新打开iTerm2就行了,当然,这个时候的效果还不会跟上图一样,因为还需要配置下面几个东东。

Powerline字体
如同上面看到的特殊三角或者是分支的图案,这个是patch到字体里面的,所以如果你要使用这种功能,必须把自己的字体给改了。你可以到这里下载别人已经patch好的字体,或者到这里下载程序,自己patch。得到字体文件之后直接双击,点击InstallFont安装到系统备用。比如我就是用的Mac原生终端自己Menlo的Powerline版本,Menlo字体看习惯了,没办法。

Solarized配色方案
这套配色方案看着不累,主页在这里,也可以从github上clone下来

$ git clone git://github.com/altercation/solarized.git

clone下来之后会看到很多文件夹,因为我们用的终端是iTerm2,所以找到solarized/iterm2-colors-solarized,里面有两个文件,一黑一白,双击就可以直接把这个Solarized的配色方案导入到iTerm2里面了。如果你需要其他的IDE或者终端也用这个配色方案,请找寻其他相应的文件夹。

iTerm2的设置

准备材料都完成了,最后需要配置终端了。打开iTerm2的Preference—Profile—点选你要修改的Profile—Colors—Load Persets。如果上面的配色方案导入成功,iTerm2就会出现这两个预设方案。

切换到Text,把Regular Font和Non-ASCII Font都改成刚才安装好的powerline字体。

最大的坑来了,一定记得把Text Rendering里的Drew bold text in bright colors给去掉,否则你ls的时候不论怎么设置都不会让列表着色。
其实不是因为没有着色,而是因为Solarized配色方案里面的Bright几个颜色就是这样的,可以翻回上面那张图看看。我整了一下午,终于发现不是设置原因而是配色方案本身有问题。。。

经过以上的设置,iTerm2终于被调教的顺眼一些了。
从此,作为程序员的你,被妹纸看到竟然用Mac编程的时候,也会显得更高端了,不是吗?咦,我忘了,因为首先。。。

Mac下免密码ssh远程主机

近来发现iTerm2实在酸爽,于是想把一切都配到iTerm2上。
之前我用的是SecureCRT登陆机器,比较方便,这下直接变成命令行了,研究了一下免密码登陆机器的方法,总结如下。

生成自己的公钥和私钥

$ ssh-keygen

一路回车,需要特别注意的是,passphrase这里是设置公钥加密的,请直接回车。不然ssh是免输密码,可使用公钥又要密码。
运行以上命令后,会在~/.ssh下生成两个文件,id_rsa.pub和id_rsa。第一个是你的公钥,第二个是你的私钥。

为Mac安装ssh-copy-id命令

接下来需要用ssh-copy-id命令把自己的公钥放到远程机上,让远程机认识你。但是Mac原生并没有Linux下的ssh-copy-id命令,使用万能的homebrew来安装一下。

$ brew install ssh-copy-id

拷贝公钥

安装完成后可以使用ssh-copy-id命令拷贝自己的公钥到远程机上

$ ssh-copy-id username@host.com

同样一路回车,这样就把你的公钥放到远程机器username用户的文件夹下,在~/.ssh/authorized_keys文件里。文件的最后会有你本机的名字。

大功告成

此后只需使用这个命令就可以免密码登陆远程主机。

$ ssh username@host.com

别忘了Alias

$ alias dev="ssh 'username@host.com'"

然后把上面的这段放到~./bash_profile里,于是我了解到这个世界上是没有最懒只有更懒的:)

Mac下通过HomeBrew安装Nginx的坑

Homebrew是Mac下一个非常方便的包管理器。
最近用brew自动升级了nginx之后发现原本可以自启动的nginx无法自启动了。
查console,发现是因为权限不够。想起来因为配置的服务器端口是80,小于1024需要root权限。
而brew的安装,是把/usr/local/opt/nginx软连接到真正的nginx目录。

ZGR-MacBook-Pro:~ zgr0629$ ll /usr/local/opt/nginx
lrwxr-xr-x  1 zgr0629  admin    21B Apr 20 16:52 /usr/local/opt/nginx@ -> ../Cellar/nginx/1.6.3
ZGR-MacBook-Pro:~ zgr0629$ 

于是对比了一下老版本nginx的bin文件权限,果然和现在的不一样。
用下面的命令改掉就好了。

sudo chown root:wheel /usr/local/Cellar/nginx/1.6.3/sbin/nginx
sudo chmod u+s /usr/local/Cellar/nginx/1.6.3/sbin/nginx

另外,由于Mac的防火墙无视软连接,只记录真实的地址,所以在防火墙设置里,还需要更新为新的nginx路径。否则其他人无法看到你的主机。

再来补充一个命令,能够方便的重启各种服务。
Github上gapple已经在这里写了一个第三方的命令行,让services命令回来了。

安装

brew tap homebrew/services

使用

brew install mysql
brew services start mysql
brew services stop mysql

PHP的单例模式

接上一次的《PHP实现树形结构》。由于菜单中的各种品类基本不变,跑同一次程序时候不会变化,所以只需在数据库查询一次,第一次初始化的时候查询好结果,以后每次取用即可直接返回。这里用php实现单例模式就能实现。

所谓单例模式是指整段程序中,类或者对象只会有一个实例的的设计模式。这种模式特别适合和数据库打交道的程序,比如上面所说的简历菜单树,因为只有一个实例,节省内资源,不用重复查询重复建立连接。

要实现单例模式,以下两点是重点:

1.他的构造函数必须声明为私有,否则其他地方可以随意new,失去了单例的意义。

2.由于外界不能new,所以内部要增加一个公共静态方法给外部取用这个实例。

另外,还可以注意一下以后的扩展。比如另外的树形结构也可以添加进来。

下面这段代码实现了菜单树的单例工厂模式,并且有一定的扩展性。文章最后会说明PHP单例模式的一些特点和不足。

/*
 * 树的单例工厂模式
 * @author zhengguorui
 */

class TreeFactory{

    // 工厂类初始化标记
    static public $instance;

    //声明私有构造方法为了防止外部代码使用new来创建对象。
    private function __construct(){}

    // menu私有唯一实例
    private $TreeOfMenu;

    // menu的初始化方法
    static private function initMenu(){
        self::$instance->TreeOfMenu = new TreeMenu ( '目录导航' );
        $tb = new IModel('category');
        $list = $tb->query("visibility=1", "*", "parent_id, sort, id");
        // 设置树形结构 
        for ($i = 0; $i < count($list); $i++) {
            self::$instance->TreeOfMenu->setNode ( $list[$i]["id"], $list[$i]["parent_id"], $list[$i]["name"] );
        }
        //设置每个节点的品牌列表
        $tb = new IModel('category_extend, brand, goods');
        $list = $tb->query("shop_goods.id = shop_category_extend.goods_id and shop_brand.id = shop_goods.brand_id and shop_goods.is_del=0", "DISTINCT category_id, shop_goods.brand_id, shop_brand.name", "category_id, brand_id"); 
        for ($i = 0; $i < count($list); $i++) {
            self::$instance->TreeOfMenu->setBrand ( $list[$i]["category_id"], $list[$i]["brand_id"], $list[$i]["name"] );
        }
    }

    // 工厂类和各种实例的初始化总入口
    static public function getinstance($instType){
        if(!self::$instance){
            // 第一次则新建并初始化对象
            self::$instance = new self();
        }

        // menu的初始化
        if($instType=="menu"){
            if(!self::$instance->TreeOfMenu){
                self::initMenu();
            }
            // 返回menu实例
            return self::$instance->TreeOfMenu;
        }

        return self::$instance;
    }

}

虽然PHP单例模式可以优化一部分问题,但由于PHP是一种解释型的脚本语言,在吐回页面之后,程序就会消亡。这一点上,php和c以及java这类常驻型程序是不同的。所以PHP的单例模式仅仅是在一次程序的执行中单例,而不能跨用户跨页面实现单例。这也使得PHP的单例模式显得那么“山寨”,只适合用在单页面中触发大量查询请求的页面上。若需要长连接或者处理大量用户查询的网关,还是得由c来接手,这也不得不说是PHP语言上的一种遗憾吧!

PHP实现树形结构

商城的菜单导航需要扩充大类,变成多级菜单。于是建立树形结构是比较好的方法。

商城菜单

一般来说,树形结构有两种方法存储,数组存储和节点指针方式。因为这次是给同事做底层库,菜单分类不多,撑死不会过一千。所以决定用数组方式,特点是快,易理解和调试。指针的调试怎么的也比数组来的难吧!

现有的数据库表里,存有每个分类(node节点)的自身id和上级id。

所以我想做成类似这种添加节点的方式:$tree->setnode(id, parentId, xxx);

于是乎觉得,用数组再合适不过了。

以下方法实现了最基本的树结构,包括添加node,取node值,遍历父节点路径,取儿子集合等等常用操作。可以很方便的扩展成适用自己需求的树,下面会提到。

/*
 * 实现树形结构的基类
 * @author zhengguorui
 */
class Tree {
    // 节点名称
    var $data = array ();
    // 以下三个为实现树结构的数组
    var $child = array (- 1 => array () );    // 节点的儿子
    var $parent = array ();                    // 节点的父亲
    var $layer = array (- 1 => - 1 );         // 节点的深度

    /*
     * 默认根节点为0,其父亲节点为-1
     */
    function Tree($value) {
        $this->setNode ( 0, - 1, $value );
    }

    /* 设置新节点。
     * (自身id,父节点id,名称)
     */
    function setNode($id, $parent, $value) {
        $parent = $parent ? $parent : 0;

        // 存节点名
        $this->data[$id] = $value;
        // 存子节点
        $this->child[$id] = array ();
        // 设置父节点的子为自己
        $this->child[$parent] [] = $id;
        // 设置父节点
        $this->parent [$id] = $parent;
        // 设置节点层级
        if (! isset ( $this->layer [$parent] )) {
            $this->layer [$id] = 0;
        } else {
            $this->layer [$id] = $this->layer [$parent] + 1;
        }
    }

    /*
     * 递归取子节点集合
     */
    function getList(&$tree, $root = 0) {
        foreach ( $this->child[$root] as $key => $id ) {
            // 取root的子节点
            $tree [] = $id;
            // 递归取子节点的子节点
            if ($this->child[$id])
                $this->getList ( $tree, $id );
        }
    }

    /*
     * 返回节点的名称
     */
    function getValue($id) {
        return $this->data[$id];
    }

    /*
     * 返回节点的深度
     */
    function getLayer($id, $space = false) {
        return $space ? str_repeat ( $space, $this->layer [$id] ) : $this->layer [$id];
    }

    /*
     * 返回节点的父亲
     */
    function getParent($id) {
        return $this->parent[$id];
    }

    /*
     * 返回节点的父节点路径,直到root
     */
    function getParents($id) {
        while ( $this->parent[$id] != - 1 ) {
            $id = $parent [$this->layer [$id]] = $this->parent [$id];
        }
        ksort($parent);
        reset($parent);
        return $parent;
    }

    /*
     * 返回直接子节点,仅一层
     */
    function getChild($id) {
        return $this->child[$id];
    }

    /*
     * 返回所有子节点
     */
    function getChilds($id = 0) {
        $child = array ($id );
        $this->getList ( $child, $id );
        return $child;
    }

}

上面是实现基本树结构的方法。但是对于本例中的菜单,每个节点(即分类)除了“名字”这个属性外,还有“该分类拥有的品牌”属性。getParents、getChilds等方法,返回的数组没有直接返回 id->name 这样的数组,实际使用中有些许不便。所以就利用PHP的extends方法重写成适合自己需求的类吧!

/*
 * 菜单树形结构类
 * 增加品牌列表属性
 * 和getset方法
 * @author zhengguorui
 */
class TreeMenu extends Tree{
    // 增加新属性,节点拥有的品类
    var $brands = array ();

    /*
     * 设置某节点的品类
     * (节点id,品类id,品类名称)
     */
    function setBrand($id = 0, $brand_id = -1, $brand_name = "null" ) {
        if( $iddata[$id]) )
            return;
        // 先给自己的 list 新增
        if (! isset ( $this->brands [$id] )) {
            $this->brands [$id] = array();
        }
        if( !isset($this->brands[$id][$brand_id]) ){
            $this->brands[$id][$brand_id] = $brand_name;
        }

        // 然后递归给自己的父节点新增
        self::setBrand( parent::getParent($id), $brand_id, $brand_name);
    }

    /*
     * 返回节点下拥有的品类列表
     */
    function getBrands($id = 0, $isList=false) {
        $origlist = array();
        if( isset($this->brands[$id]) ){
            $origlist = $this->brands[$id];
        }
        return self::brandtoarray($origlist, $isList );
    }

    /*
     * 返回父节点
     */
    function getParent($id, $isList=false) {
        if($id==0){
            return array();
        }
        $origlist = parent::getParent($id);
        return self::idtoarray( array("0"=>$origlist), $isList );
    }

    /*
     * 返回节点的父节点路径,直到root
     */
    function getParents($id, $isList=false) {
        $origlist = parent::getParents($id);
        return self::idtoarray($origlist, $isList );
    }

    /*
     * 返回直接子节点,仅一层
     */
    function getChild($id, $isList=false) {
        $origlist = parent::getChild($id);
        return self::idtoarray( $origlist, $isList );
    }

    /*
     * 返回所有子节点
     */
    function getChilds($id = 0, $isList=false) {
        $origlist = parent::getChilds($id);
        return self::idtoarray($origlist, $isList );
    }

    /*
     * 将 index->id 的数组方式转为 id->name 的方式
     */
    function idtoarray($origlist, $isList=false){
        $newlist = array();
        if(isset($origlist)){
            $newlist = array();
            foreach ( $origlist as $key => $value ) {
                if($isList){
                    array_push($newlist, array(
                        'id'    =>    $value,
                        'name'    =>    self::getValue($value),
                    ));
                }else{
                    $newlist[$value] = self::getValue($value);
                }
            }
        }
        return $newlist;
    }

    /*
     * 将 index->id 的数组方式转为 id->name 的方式
     */
    function brandtoarray($origlist, $isList=false){
        $newlist = array();
        if(isset($origlist)){
            $newlist = array();
            foreach ( $origlist as $key => $value ) {
                if($isList){
                    array_push($newlist, array(
                        'id'    =>    $key,
                        'name'    =>    $value,
                    ));
                }else{
                    $newlist[$key] = $value;
                }
            }
        }
        return $newlist;
    }

}

CSS3 实现折纸角效果

前两天去了一次 html5 分享沙龙,突然对 css3 的一些效果感兴趣起来。写了一个折纸角效果。

用到的主要是css3中旋转的属性rotate,还有强大的background中linear-gradient属性。

整个折角由两个部分组成,一个是折角体,一个是用来遮挡背景的块。

用到的主要是属性是linear-gradient,将矩形块的背景设置成以对角线分开,一边是背景色(紫色),一边是纸条的颜色。

然后利用rotate,将它旋转。

然后定位到合适位置,这样,就是一个折角了。

最后用小块把多余的背景盖住。一个折角就做好了。

这个做法的优点:用css伪类before和after生成折角,html结构中没有多余元素,干净。

缺点:在折角的上方会多出来一块,如不设置好margin-top,则会挡住其他元素,露馅。

以下是实现方法及demo,这里是源码:css3_paper_corner_effect





第一步:做一个方框,利用linear-gradient属性,将矩形块的背景设置成以对角线分开,一边是背景色(紫色),一边是纸条的颜色(黄色)。两个渐变色的起始点设置成同一点,即50%,这样就可以在二分之一处颜色突变,从而产生一条明显的分界线的效果。然后用-150deg,把这条颜色分界线旋转为对角线。这里为了方便看清方块,将边框设置了1px



第二步:利用rotate,将它旋转,并且加上阴影。



第三步:放到大的div中,调整位置。


黄色纸条

第四步:加上一个块,把多余的背景盖住


黄色纸条

OK!隐藏掉这些块的border之后就大功告成啦!


CSS3实现折角Demo


黄色纸条

绿色纸条

红色纸条

以下是相关代码

css部分:

.bg_purple{background-color:#CFCFFF;margin:15px auto;padding:20px;}
.bg_purple h1{text-align:center;}
.bg_purple .tips{width:300px;height:100px;margin:25px auto;text-align:center;line-height:100px;font-size:24px;font-weight:bold;position:relative;}
.bg_purple .conner{display:block;position:relative;}
.bg_purple .conner:after{content:"";-moz-transform:rotate(-30deg);-webkit-transform:rotate(-30deg);background:-moz-linear-gradient(-150deg,#CFCFFF 50%,#FFFFA9 50%) repeat scroll 0 0 transparent;background:-webkit-linear-gradient(-150deg,#CFCFFF 50%,#FFFFA9 50%) repeat scroll 0 0 transparent;height:70px;width:40px;position:absolute;right:14px;top:-15px;box-shadow:-4px 4px 3px -3px #000000;}
.bg_purple .conner:before{content:"";background-color:#CFCFFF;height:40px;width:40px;position:absolute;right:0;top:0;}
.bg_purple .yellow{background-color:#FFFFA9;}
.bg_purple .yellow:after{background:-moz-linear-gradient(-150deg,#CFCFFF 50%,#FFFFA9 50%) repeat scroll 0 0 transparent;background:-webkit-linear-gradient(-150deg,#CFCFFF 50%,#FFFFA9 50%) repeat scroll 0 0 transparent;}
.bg_purple .green{background-color:#CFFFCF;}
.bg_purple .green:after{background:-moz-linear-gradient(-150deg,#CFCFFF 50%,#CFFFCF 50%) repeat scroll 0 0 transparent;background:-webkit-linear-gradient(-150deg,#CFCFFF 50%,#CFFFCF 50%) repeat scroll 0 0 transparent;}
.bg_purple .red{background-color:#FFCFCF;}
.bg_purple .red:after{background:-moz-linear-gradient(-150deg,#CFCFFF 50%,#FFCFCF 50%) repeat scroll 0 0 transparent;background:-webkit-linear-gradient(-150deg,#CFCFFF 50%,#FFCFCF 50%) repeat scroll 0 0 transparent;}

html部分十分简洁:

<div class="bg_purple" >
    <h1>CSS3实现折角Demo</h1>
    <div class="tips conner yellow" >黄色纸条</div>
    <div class="tips conner green" >绿色纸条</div>
    <div class="tips conner red" >红色纸条</div>
</div>

【学习笔记】 精通JavaScript (一)

精通JavaScript是我早就想学习的书。由于年代有些久远,市面上到处都找不到卖的,我也不习惯看电子书。无奈,只得从淘宝20块钱买盗版书。第一章很基础,看完了第二章,讲了很多面向对象编程的知识,感觉很有深度,有很多不知道的技巧,现在整理一下第二章里的重点。

引用

JavaScript是一个基于引用的系统,类似指针概念。但是引用的指向只是具体的对象,而不是另一个引用。不像Perl语言中允许许多层引用,JavaScript里的结果是沿着引用链一直上溯到原来那个对象。

//将 obj 置为空对象
var obj = new Object();
// objRef 是 obj 的引用
var objRef = obj;
//修改原对象的一个属性
//(注意,这里仅是修改,不会产生新的对象)
obj.oneProperty = true;
//我们现在看到,这个改变在两个变量中都反映了出来
//(因为阿门引用的是同一个变量)
alert(obj.oneProperty === objRef.oneProperty);

var items = new Array("one", "two", "three");
var itemsRef = items;
//Array对象实质上是把每个元素作为属性保存
//所以push()方法添加新元素的时候,没有产生新的变量
items.push("four");
//这两个数组长度应该是一致的
//因为他们指向同一个数组对象
alert(items.length === itemsRef.length);

var items = new Array("one", "two", "three");
var itemsRef = items;
//将 items 置为一个新的对象
items = new Array("four", "five");
//这时 items 和 itemsRef 指向不同对象了
// itemsRef 还是指向原来的new Array("one", "two", "three");
alert(items != itemsRef);

这里要特别注意,看似是自修改对象,其结果却产生了一个新的非引用对象。在执行字符串连接操作时,结果总会是一个新的字符串对象,而非源字符串的修改版本!

var items = "test";
var itemsRef = items;
//做一个字符串连接操作
//注意:这会创建一个新对象,而非修改原对象
items += "ing";
//这时 items 和 itemsRef 的值不相等了。因为有新的字符串对象被创建。
alert(items != itemsRef);

对象

共有,私有,特权方法

JavaScript也可以像其他面向对象语言创建各种权限的变量和方法。其中,特权方法,指的是能够访问并处理自身私有变量的方法。现整理如下。

//创建一个Classroom函数,类似c++里类的定义
function Classroom(students, teacher){
    //公共变量
    this.students = students;
    this.teacher = teacher;
    //私有变量
    var namelist = this.students.join(",") + " : " + this.teacher.join(",");

    //公共方法
    this.disp = function (){
        alert("这是公共方法!");
    }
    this.callprivatefunction = function(){
        private_function();//创建一个公共方法调用私有方法
    }
    //私有方法
    function private_function(){
        alert("这是私有方法!");
    }
    //特权方法
    this.disp_list = function (){
        alert("特权方法:" + namelist);//特权方法可以公共访问,处理私有变量
    }

}

//实例化Classroom类
var class1 = new Classroom(["zgr", "lsm"], ["laoshi1", "laoshi2"] );
//公共变量可以直接访问
alert(class1.students);
alert(class1.teacher);
//私有变量直接访问不到,显示undefined
alert(class1.namelist);
//公共方法可以调用
class1.disp();
/*调用私有方法会报错
class1.init();
*/
//这个公共方法是私有方法调用的接口
class1.callprivatefunction();
//特权方法可以调用,并且访问到私有变量
class1.disp_list();

使用eval动态创建js函数

JavaScript有许多小窍门来使编程更加容易。

其中之一就是eval()函数,这个函数可以把一个字符串当作一个JavaScript表达式一样去执行它。前段时间项目中就用到了这个技术,巧妙的解决了问题。

项目中用到这样的技术:在本页用js拉取一个接口、告诉接口回调函数名、接口的回包里直接调用这个函数。这样一来,回调函数的调用,就全交给接口写的回包里做了。

页面js如下

main()
{
    $.getScript('http://qiangblog.com/someInterface?callback=bk_someInterface');
}
function bk_someInterface( _return_obj )
{
    //这里是回调函数,处理 _return_obj 数据
    ...
    ...
}

someInterface的回包如下

var return_obj={
“retcode”:”200”,
“msg”:”Query userid success!”,
“username”:”xiaoqiang”
};if(typeof bk_someInterface==’function’) bk_someInterface(return_obj);

这样做比较方便,回调函数名由调用的函数给接口,接口的回包照写函数名就行了。可是这里也会遇到一些问题,如果这个接口在同一页面需要同时多次调用,那么回调函数则会异步执行。在这种情况下,回调函数就不知道当前的回包数据对应的哪次请求了。以前的解决办法是多给接口一个参数,回包的时候照写就行了。这样,需要修改一下接口程序。

以前的解决办法:

main()
{
    var extraParameter = Math.random();
    $.getScript('http://qiangblog.com/someInterface?callback=bk_someInterface&extra=' extraParameter);
}
function bk_someInterface( _return_obj )
{
    //这里是回调函数,处理 _return_obj 数据
    ...
    ...
}

var return_obj={
    "retcode":"200",
    "msg":"Query userid success!",
    "extra":"0.1155661893165",
    "username":"xiaoqiang"
};if(typeof bk_someInterface=='function') bk_someInterface(return_obj);

如上,这样写需要改动接口程序,以前接口都是自己用自己写,加个参数之类的改动很方便,但不是所有的项目接口都是自己这边写,前段时间做项目就遇到这个情况,研究了下解决办法。以用JS里的eval动态创建函数来解决。

思路是这样的,既然回包里固定了调用回调函数的格式,即 callbackfunctionname(return_obj),那么改动就必须在其他地方完成。最终我需要回包里的return_obj对象和一些附加参数。那么就可以另外定义一个函数,作为新的回调函数,在这个函数里定义附加参数,然后把附加函数和 return_obj 一起传给原先的回调函数。这样就可以解决了。动态定义函数用eval完成即可。

解决办法如下:

main()
{
    //自定义的随机附加参数
    var extraParameter = Math.random();
    //拿一个时间戳
    var timestamp = new Date().getTime();
    //定义动态函数名,函数名包含这个时间戳,保证不重名
    var bkfunctionname = "bk_someInterface_" + timestamp;
    //创建这个动态函数,函数里先定义了附加参数,然后调用原来的回调函数。
    eval( bkfunctionname + ' = function(retobj){var extra = "'+extraParameter+'";bk_someInterface(retobj, extra);} ');
    //所以在拉取接口的时候可以不用写 extraParameter,extraParameter 已经在新的回调中赋值了。
    $.getScript('http://qiangblog.com/someInterface?callback='+bkfunctionname);
}

function bk_someInterface( _return_obj, _extra )
{
    //这里是回调函数,处理 _return_obj 数据,同时也可以拿到自己定义的 _extra 了
    ...
    ...
}

用.htaccess将主域名从网站根目录指向子目录

这个博客原本是要访问 http://www.qiangblog.com/blog/ 来访问的。

我在根目录下放了一个 index.html ,里面的 js 控制自动跳转到 /blog。这样一来,访问http://www.qiangblog.com/ 会自动跳到 http://www.qiangblog.com/blog/ 去。

但是今天突然想把自己的网站整一下,因为这个自动跳转很不利于SEO,而且 qiangblog 这个域名有很强的博客意向。如果总有个 /blog 挂在后面感觉不好看呐。所以我想弄成直接访问 http://www.qiangblog.com/ 就到博客里。并且相应的子目录依旧能用 http://www.qiangblog.com/subdir 访问。

最简单的方法是把 wordpress 装在虚拟主机的根目录下。但是这样目录会变得杂乱,其他项目会跟博客弄到一起,对于整洁强迫症的我来说万万不可。后来看到 wordpress 里有自带一种方法实现这个功能。但是还是要改变目录结构,依旧不能达到我的要求。

今天看到一个 .htaccess 的设置方法。可以实现我想要的效果。编辑根目录下的 .htaccess ,设置好相应参数就完全可以实现我想要的功能了。利于SEO是最爽的事情了~

代码如下:

# .htaccess main domain to subfolder redirect
# Copy and paste the following code into the .htaccess file
# in the public_html folder of your hosting account
# make the changes to the file according to the instructions.

# Do not change this line.

RewriteEngine on

# Change yourdomain.com to be your main domain.

RewriteCond %{HTTP_HOST} ^(www.)?yourmaindomain.com$

# Change ’subfolder’ to be the folder you will use for your main domain.

RewriteCond %{REQUEST_URI} !^/subfolder/

# Don’t change this line.

RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d

# Change ’subfolder’ to be the folder you will use for your main domain.

RewriteRule ^(.*)$ /subfolder/$1

# Change yourdomain.com to be your main domain again.
# Change ’subfolder’ to be the folder you will use for your main domain
# followed by / then the main file for your site, index.php, index.html, etc.

RewriteCond %{HTTP_HOST} ^(www.)?yourmaindomain.com$
RewriteRule ^(/)?$ subfolder/index.php [L]