Python模块学习 - fabric

简介

fabric是一个Python的库,同时它也是一个命令行工具。使用fabric提供的命令行工具,能够很方便地执行应用部署和系统管理等操做。html

fabric依赖于paramiko进行ssh交互,fabric的设计思路是经过几个API接口来完成全部的部署,所以fabric对系统管理操做进行了简单的封装,好比执行命令,上传文件,并行操做和异常处理等。python

#安装
# fabric3支持python3
pip3 install fabric3

因为fabric比较特殊它仍是一个命令行工具,能够经过help进行命令的了解mysql

pyvip@Vip:~/utils$ fab --help
Usage: fab [options] <command>[:arg1,arg2=val2,host=foo,hosts='h1;h2',...] ...

Options:
  -h, --help            show this help message and exit
  -d NAME, --display=NAME
                        print detailed info about command NAME
  -F FORMAT, --list-format=FORMAT
                        formats --list, choices: short, normal, nested
  -I, --initial-password-prompt
                        Force password prompt up-front
  --initial-sudo-password-prompt
                        Force sudo password prompt up-front
  -l, --list            print list of possible commands and exit
  --set=KEY=VALUE,...   comma separated KEY=VALUE pairs to set Fab env vars
……

错误的提示linux

# fab -help
Traceback (most recent call last):
  File "/usr/local/python3/bin/fab", line 11, in <module>
    load_entry_point('Fabric==1.14.0', 'console_scripts', 'fab')()
  File "/usr/local/python3/lib/python3.6/site-packages/pkg_resources/__init__.py", line 480, in load_entry_point
    return get_distribution(dist).load_entry_point(group, name)
  File "/usr/local/python3/lib/python3.6/site-packages/pkg_resources/__init__.py", line 2693, in load_entry_point
    return ep.load()
  File "/usr/local/python3/lib/python3.6/site-packages/pkg_resources/__init__.py", line 2324, in load
    return self.resolve()
  File "/usr/local/python3/lib/python3.6/site-packages/pkg_resources/__init__.py", line 2330, in resolve
    module = __import__(self.module_name, fromlist=['__name__'], level=0)
  File "/usr/local/python3/lib/python3.6/site-packages/fabric/main.py", line 13, in <module>
    from operator import isMappingType
ImportError: cannot import name 'isMappingType'

# 说明你使用的python版本可fabric版本不一样,python3 安装时使用的是fabric3

  

入门使用

fabric的典型使用方式就是,建立一个Python文件,该文件包含一到多个函数,而后使用fab命令调用这些函数。这些函数在fabric中成为task,下面是一个例子web

from fabric.api import *
from fabric.contrib.console import confirm
from fabric.utils import abort
from fabric.colors import *

env.hosts = ['192.168.5.128']
env.port = 22
env.user = 'root'
env.password = 'mysql123'


def hostname():
    run('hostname')


def ls(path='.'):
    run('ls {0}'.format(path))


def tail(path='/etc/pas', line=10):
    run('tail -n {0} {1}'.format(line, path))


def hello():
    with settings(hide('everything'),warn_only=True):   # 关闭显示
        result = run('anetstat -lntup|grep -w 25')
        print(result)                                   # 命令执行的结果
        print(result.return_code)                       # 返回码,0表示正确执行,1表示错误
        print(result.failed)

  

PS:fab命令执行时,默认引用一个名为fabfile.py的文件,咱们也能够经过-f来进行指定(文件名不能为abc.py,会冲突).redis

这里使用了三个fabric的封装:sql

  1. run:用于执行远程命令的封装
  2. sudo:以sudo权限执行远程命令
  3. env:保存用户配置的字典(保存了相关的配置,好比登陆用户名env.user,密码env.password,端口env.port等,若是没有指定用户名那么默认使用当前用户,端口使用22)
 一、获取任务列表
pyvip@Vip:~/utils$ fab -f fab_utils.py --list
Available commands:

    hello
    hostname
    ls
    tail

pyvip@Vip:~/utils$ fab -f fab_utils.py --list
Available commands:

    hello
    hostname
    ls
    tail
# 二、执行hostname函数
pyvip@Vip:~/utils$ fab -f fab_utils.py hostname
[192.168.5.128] Executing task 'hostname'
[192.168.5.128] run: hostname
[192.168.5.128] out: china
[192.168.5.128] out: 


Done.
Disconnecting from 192.168.5.128... done.
# 三、多个参数的状况
pyvip@Vip:~/utils$ fab -f fab_utils.py ls:/
[192.168.5.128] Executing task 'ls'
[192.168.5.128] run: ls /
[192.168.5.128] out: bin  boot  data  dev  etc  home  lib  lib64  lost+found  media	misc  mnt  net	opt  proc  root  sbin  selinux	srv  sys  tmp  usr  var
[192.168.5.128] out: 


Done.
Disconnecting from 192.168.5.128... done.

须要注意的是:shell

  • 一次能够多个task,按照顺序执行: fab -f fab_util.py hostname ls
  • 给task传递参数使用task:参数,多个参数按照位置进行传递(和Python相同,对于关键字的参数能够,在命令行中指定:fab ls:path=/home)

fabric的命令行参数

fab命令做为fabric程序的入口提供了,丰富的参数调用.json

# -l:查看task列表

# -f:指定fab的入口文件,默认是fabfile.py
# -g:指定网管设备,好比堡垒机环境下,填写堡垒机的IP
# -H:在命令行指定目标服务器,用逗号分隔多个服务器
# -P:以并行方式运行任务,默认为串行
# -R:以角色区分不一样的服务
# -t:链接超时的时间,以秒为单位
# -w:命令执行失败时的警告,默认是终止任务
# -- Fabric参数,其余包含fabric脚本的中的参数的快捷操做,好比--user,--port,或者直接跟要执行的Linux命令

  以下例子,不写一行代码获取全部主机的ip地址api

pyvip@Vip:~/utils$ fab -H 192.168.5.128 --port 22 --user='root' --password='mysql123' -- 'ip a '
[192.168.5.128] Executing task '<remainder>'
[192.168.5.128] run: ip a 
[192.168.5.128] out: 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 16436 qdisc noqueue state UNKNOWN 
[192.168.5.128] out:     link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
[192.168.5.128] out:     inet 127.0.0.1/8 scope host lo
[192.168.5.128] out:     inet6 ::1/128 scope host 
[192.168.5.128] out:        valid_lft forever preferred_lft forever
[192.168.5.128] out: 2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000
[192.168.5.128] out:     link/ether 00:0c:29:96:0a:a0 brd ff:ff:ff:ff:ff:ff
[192.168.5.128] out:     inet 192.168.5.128/24 brd 192.168.5.255 scope global eth0
[192.168.5.128] out:     inet6 fe80::20c:29ff:fe96:aa0/64 scope link 
[192.168.5.128] out:        valid_lft forever preferred_lft forever
[192.168.5.128] out: 3: pan0: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN 
[192.168.5.128] out:     link/ether 7a:4d:51:6c:c2:cd brd ff:ff:ff:ff:ff:ff

 

经常使用的对象和方法介绍 

介绍fabric中的env对象,以及其余的好比执行命令模块,上传文件等。

fabric中的env

 env是一个全局惟一的字典,保存了Fabric全部的配置,在Fabric的实现中,他是一个_AttributeDict()对象,之因此封装成_AttributeDict()对象,是覆盖了__getattr__和__setattr__,使咱们可使用“对象.属性=值”的方式,操做字典。

 咱们能够经过源码的方式,查看env的配置参数,或者使用以下方式查看:

import json
from fabric.api import env

print(json.dumps(env, indent=3))


def hell(name='world'):
    print('hello %s' % name)


-----------------------------------------------
结果


pyvip@Vip:~/utils$ fab -f fab_utils.py -l
{
   "show": null,
   "": true,
   "sudo_user": null,
   "default_port": "22",
   "key_filename": null,
   "path": "",
   "hosts": [
      "192.168.5.128"
   ],
   "host_string": null,
   "ok_ret_codes": [
      0
   ],
   "always_use_pty": true,
   "fabfile": "fab_utils.py",
   "echo_stdin": true,
   "again_prompt": "Sorry, try again.",
   "command": null,
   "forward_agent": false,
   "command_prefixes": [],
   "cwd": "",
   "connection_attempts": 1,
   "linewise": false,
   "gateway": null,
   "use_exceptions_for": {
      "network": false
……

 

经常使用的env配置以下:

  • env.hosts:定义目标服务器列表
  • env.exclude_hosts:排除特定的服务器
  • env.user SSH:到远程服务器的用户名
  • env.port:远程服务器的端口号
  • env.key_filename:私钥文件的位置
  • env.password SSH:到远程服务器的密码

针对不一样主机不一样密码的状况,可使用以下的方式:

env.hosts = [
    'root@192.168.10.201:22',
    'root@192.168.10.202:22',
    'root@192.168.10.203:22'
]
env.passwords = {
    'root@192.168.10.201:22':'123456201',
    'root@192.168.10.202:22':'123456202',
    'root@192.168.10.203:22':'123456203'

  

fabric提供的命令

run():在远程服务器上执行Linux命令,还有一个重要的参数pty,若是咱们执行命令之后须要有一个常驻的服务进程,那么就须要设置pty=False,避免由于Fabric退出致使进程的退出

run('service mysqld start',pty=False)

 PS:执行完毕会返回输出的信息,咱们能够定义变量接受,同时这个返回信息有一个方法return_code,当返回的是正确执行的结果时code为0,不然不为0

def hello():

    with settings(hide('everything'),warn_only=True):   # 关闭显示
        result = run('anetstat -lntup|grep -w 25')
        print(result)                                   # 命令执行的结果
        print(result.return_code)                       # 返回码,0表示正确执行,1表示错误

结果

[192.168.5.128] Executing task 'hello'
/bin/bash: anetstat: command not found
1
True

Done.
Disconnecting from 192.168.5.128... done.

sudo():与run相似,使用管理员权限在远程服务器上执行shell命令,还有一个重要的参数pty,若是咱们执行命令之后须要有一个常驻的服务进程,那么就须要设置pty=False,避免由于Fabric退出致使进程的退出。

local():用以执行本地命令,返回要执行的命令,local是对Python的Subprocess模块的封装,更负载的功能能够直接使用Subprocess模块,包含capture参数,默认为False,表示subprocess输出的信息进行显示,若是不想显示,那么指定capture=True便可

 

ef test():
    result = local('make test',capture=True)
    print(result)
    print(result.failed)
    print(result.succeeded)
 
# 返回执行的命令
# 若是执行失败那么 result.failed 为True
# 若是执行成功那么 result.succeeded 为True

get():从远程服务器上获取文件,经过remote_path参数声明从何处下载,经过local_path表示下载到何处。remote_path支持通配符。

get(remote_path='/etc/passwd',local_path='/tmp/passwd')

put():将本地的文件上传到远程服务器,参数与get类似,此外,还能够经过mode参数执行远程文件的权限配置。

get(remote_path='/etc/passwd',local_path='/tmp/passwd')

  

reboot():重启远程服务器,能够经过wait参数设置等待几秒钟重启

	
reboot(wait=30)

propmt():用以在Fabric执行任务的过程当中与管理员进行交互,相似于python的input

key = prompt('Please specify process nice level:',key='nice',validate=int)
# 会返回采集到的key

  

fabric的上下文管理器

env中存储的是全局配置,有时候咱们并不但愿修改全局配置参数,只但愿临时修改部分配置,例如:修改当前工做目录,修改日志输出级别等。

在fabric中咱们能够经过上下文管理器临时修改参数配置,而不会影响全局配置。当程序进入上下文管理器的做用域时,临时修改就会起做用;当程序离开上下文管理器时,临时修改就会消失。

cd():切换远程目录

def change(dir='/tmp'):
    with cd(dir):
        run('pwd')     # /tmp
    run('pwd')         # /root

lcd():切换本地目录

path():配置远程服务器PATH环境变量,只对当前会话有效,不会影响远程服务器的其余操做,path的修改支持多种模式

  • append:默认行为,将给定的路径添加到PATH后面。
  • prepend:将给定的路径添加到PATH的前面。
  • replace:替换当前环境的PATH变量。
def addpath():
    with path('/tmp','prepend'):
        run("echo $PATH")
    run("echo $PATH")

prefix():前缀,它接受一个命令做为参数,表示在其内部执行的代码块,都要先执行prefix的命令参数。

def testprefix():
    with cd('/tmp'):
        with prefix('echo 123'):
            run('echo 456')
            run('echo 789')
 
# 转换为Linux命令为:
cd /tmp && echo '123' && echo '456'
cd /tmp && echo '123' && echo '789' 

shell_env():设置shell脚本的环境变量 

def setenv():
    with shell_env(HTTP_PROXY='1.1.1.1'):
        run('echo $HTTP_PROXY')
    run('echo $HTTP_PROXY')
 
# 等同于shell中的export
export HTTP_PROXY='1.1.1.1'

settings():通用配置,用于临时覆盖env变量

def who():
    with settings(user='dev'):    # 临时修改用户名为dev
        run('who')
    run('who')

 

remote_tunnel():经过SSH的端口转发创建的连接

with remote_tunnel(3306):
    run('mysql -uroot -p password')

  

hide():用于隐藏指定类型的输出信息,hide定义的可选类型有7种

  • status:状态信息,如服务器断开连接,用户使用ctrl+C等,若是Fabric顺利执行,不会有状态信息
  • aborts:终止信息,通常将fabric看成库使用的时候须要关闭
  • warnings:警告信息,如grep的字符串不在文件中
  • running:fabric运行过程当中的数据
  • stdout:执行shell命令的标准输出
  • stderr:执行shell命令的错误输出
  • user:用户输出,相似于Python中的print函数

为了方便使用,fabric对以上其中类型作了进一步的封装

  • output:包含stdout,stderr
  • everything:包含stdout,stderr,warnings,running,user
  • commands:包含stdout,running

show():与hide相反,表示显示指定类型的输出

def hello():
    with settings(show('everything'),warn_only=True):   # 显示全部
        result = run('netstat -lntup|grep')
        print('1='+result)                                   # 命令执行的结果
        print('2='+str(result.return_code))                  # 返回码,0表示正确执行,1表示错误
        print('3='+str(result.failed))

结果

pyvip@Vip:~/utils$ fab -f fab_utils.py hello
[192.168.5.128] Executing task 'hello'
[192.168.5.128] run: netstat -lntup|grep
[192.168.5.128] out: 用法: grep [选项]... PATTERN [FILE]...
[192.168.5.128] out: 试用‘grep --help’来得到更多信息。
[192.168.5.128] out: 


Warning: run() received nonzero return code 2 while executing 'netstat -lntup|grep'!

NoneType


1=用法: grep [选项]... PATTERN [FILE]...
试用‘grep --help’来得到更多信息。
2=2
3=True

Done.

 

quiet():隐藏所有输出,仅在执行错误的时候发出告警信息,功能等同于 with settings(hide('everything'),warn_only=True) .

# 好比建立目录的时候,若是目录存在,默认状况下Fabric会报错退出,咱们是容许这种错误的,因此针对这种错误,咱们进行以下设置,使fabric只打出告警信息而不会中断执行。
with settings(warn_only=True)

  

装饰器

Fabric提供的命令通常都是执行某一个具体的操做,提供的上下文管理器通常都是用于临时修改配置参数,而fabric提供的装饰器,既不是执行具体的操做,也不是修改参数,而是控制如何执行这些操做,在那些服务器上执行这些操做,fabric的装饰器与人物执行紧密相关。下面从几个方面来进行说明

  • hosts:定制执行task的服务器列表
  • roles:定义执行task的role列表
  • parallel:并行执行task
  • serial:串行执行task
  • task:定义一个task
  • runs_once:该task只执行一次

fabric的task

 

task就是fabric须要在远程服务器上执行的函数,在fabric中有3中方法定义一个task

  1. 默认状况下,fabfile中每个函数都是一个task。
  2. 继承自fabric的task类,这种方式比较难用,不推荐。
  3. 使用fabric的task装饰器,这是使用fabric最快速的方式,也是推荐的用法。
from fabric.api import *
 
env.user='root'
env.password='mysql123'
 
@task
def hello():
    run('echo hello')
 
def world():
    run('echo world')

  PS:默认状况下,fabfile中的全部函数对象都是一个task,可是若是咱们使用了task装饰器,显示的定义了一个task,那么,其余没有经过task装饰器装饰的函数将不会被认为是一个task。

fabric的host

为了方便咱们的使用,fabric提供了很是灵活的方式指定对哪些远程服务器执行操做,根据咱们前面的知识,咱们知道有两种方式:经过env.hosts来执行,或者在fab执行命令的时候使用-H参数,除此以外,还有如下须要注意的地方

  1. 指定host时,能够同时指定用户名和端口号: username@hostname:port
  2. 经过命令行指定要多哪些hosts执行人物:fab mytask:hosts="host1;host2"
  3. 经过hosts装饰器指定要对哪些hosts执行当前task
  4. 经过env.reject_unkown_hosts控制未知host的行为,默认True,相似于SSH的StrictHostKeyChecking的选项设置为no,不进行公钥确认。
from fabric.api import *
 
env.hosts = [
    'root@192.168.10.201:22',
    'root@192.168.10.202:22',
    'root@192.168.10.203:22'
]
env.passwords = {
    'root@192.168.10.201:22':'123456201',
    'root@192.168.10.202:22':'123456202',
     'root@192.168.10.203:22':'123456203'
}
 
@hosts('root@192.168.10.201:22')
@task
def hello():
    run('ifconfig br0')
 
 
# 命令行的方式:
fab hello:hosts="root@192.168.10.201;root@192.168.10.202"

  

fabric的role

role是对服务器进行分类的手段,经过role能够定义服务器的角色,以便对不一样的服务器执行不一样的操做,Role逻辑上将服务器进行了分类,分类之后,咱们能够对某一类服务器指定一个role名便可。进行task任务时,对role进行控制。

# role在env.roledefs中进行定义
env.roledefs = {
    'web':['root@192.168.10.201','192.168.10.202']    # role名称为:web
    'db':['root@192.168.10.203',]                     # role名称为:db
}
  当咱们定义好role之后,咱们就能够经过roles装饰器来指定在哪些role上运行task。


from fabric.api import *
 
env.roledefs = {
    'web':['root@192.168.10.201:22','root@192.168.10.202:22',],
    'db':['root@192.168.10.203:22',]
}
env.passwords = {
    'root@192.168.10.201:22':'123456201',
    'root@192.168.10.202:22':'123456202',
    'root@192.168.10.203:22':'123456203'
}
 
@roles('db')       # 只对role为db的主机进行操做
@task
def hello():
    run('ifconfig br0')

 注意:hosts装饰器能够和roles装饰器一块儿使用(全集),看起来容易形成混乱,不建议混搭。

 

fabric的执行模型

 

fabric执行任务的步骤以下:

  1. 建立任务列表,这些任务就是fab命令行参数指定的任务,fab会保持这些任务的顺序
  2. 对于每一个任务,构造须要执行该任务的服务器列表,服务器列表能够经过命令行参数指定,或者env.hosts指定,或者经过hosts和roles装饰器指定
  3. 遍历任务列表,对于每一台服务器分别执行任务,能够将任务列表和服务器列表看做是两个for循环,任务列表是外层循环,服务器列表是内存循环,fabric默认是串行执行的能够经过装饰器或者命令行参数肯定任务执行的方式
  4. 对于没有指定服务器的任务默认为本地任务,仅执行一次

PS:关于并行模式:

  1. 经过命令行参数-P(--parallel)通知Fabric并行执行task
  2. 经过env.parallel设置设否须要并行执行
  3. 经过parallel装饰器通知Fabric并行执行task,它接受一个pool_size做为参数(默认为0),表示能够有几个任务并行执行

其余装饰器

前面介绍了task,hosts,roles和parallel装饰器,此外还有两个装饰器比较经常使用

  • runs_once:只执行一次,防止task被屡次调用。例如,对目录打包进行上传,上传动做对不一样的服务器可能会执行屡次,可是打包的动做只须要执行一次便可。
  • serial:强制当前task穿行执行。使用该参数时优先级最高,即使是制定了并发执行的参数

经常使用的功能函数

fabric中还有其余的一些好用的函数

封装task

 fabric提供了一个execute函数,用来对task进行封装。它最大的好处就是能够将一个大的任务拆解为不少小任务,每一个小任务互相独立,互不干扰

from fabric.api import *
 
env.roledefs = {
    'web':['root@192.168.10.201:22','root@192.168.10.202:22',],
    'db':['root@192.168.10.203:22',]
}
env.passwords = {
    'root@192.168.10.201:22':'123456201',
    'root@192.168.10.202:22':'123456202',
    'root@192.168.10.203:22':'123456203'
}
 
@roles('db')
def hello():
    run('echo hello')
 
@roles('web')
def world():
    run('echo world')
 
@task
def helloworld():
    execute(hello)    
    execute(world)

 # 函数helloworld做为入口,分别调用两个task,对不一样的主机进行操做

 

utils函数

 

包含一些辅助行的功能函数,这些函数位于fabric.utils下,经常使用的函数以下:

  1. abort:终止函数执行,打印错误信息到stderr,而且以退出码1退出。
  2. warn:输出警告信息,可是不会终止函数的执行
  3. puts:打印输出,相似于Python中的print函数
def helloworld():
    execute(hello)
    abort('----->abort')     # 执行到这里时,直接退出
    warn('----->warn')       # 会发出提示信息,不会退出
    puts('----->puts')       # 会打印括号中的信息
    execute(world)

  

带颜色的输出 

 fabric为了让输出日志更具备可读性,对命令行中断的颜色输出进行了封装,使用print打印带有不一样颜色的文本,这些颜色包含在fabric.colors中。像warn,puts打印输出的,也能够直接渲染颜色

  • blue(text,blod=False)  蓝色
  • cyan(text,blod=False)  淡蓝色
  • green(text,blod=False)  绿色
  • magenta(text,blod=False)  紫色
  • red(text,blod=False)  红色
  • white(text,blod=False)  白色
  • yellow(text,blod=False)   黄色
def ls(path='.'):
    run('ls {0}'.format(path))

def hello():

    execute(hell)  # task任务hell
    warn(yellow('----->warn'))  # 会发出提示信息,不会退出
    puts(green('----->puts'))  # 会打印括号中的信息
    execute(ls) # task任务ls
    print(green('the text is green')) # 单纯的渲染文字: 

def hell(name='world'):
    print('hello %s' % name)

 

 

  

确认信息

有时候咱们在某一步执行错误,会给用户提示,是否继续执行时,confirm就很是有用了,它包含在 fabric.contrib.console中

def testconfirm():

    result = confirm('Continue Anyway?')
    print(result)
 
 
# 会提示输入y/n
# y 时 result为True
# n 时 result为False

  

使用Fabric源码安装redis

下载一个redis的包和fabfile.py放在同级目录便可,不一样目录须要修改包的位置,这里使用的是redis-4.0.9版本。

#!/usr/bin/env python3
from fabric.api import *
from fabric.contrib.console import confirm
from fabric.utils import abort
from fabric.colors import *
 
env.hosts = ['192.168.10.202',]
env.user = 'root'
env.password = '123456202'
 
@runs_once
@task
def test():
    with settings(warn_only=True):
        local('tar xf redis-4.0.9.tar.gz')
        with lcd('redis-4.0.9'):
            result = local('make test',capture=True)
            if result.failed and not confirm('Test is Faild Continue Anyway?'):
                abort('Aborting at user request.')
 
    with lcd('redis-4.0.9'):
        local("make clean")
    local('tar zcvf redis-4.0.10.tar.gz redis-4.0.9')
 
@task
def deploy():
    put('redis-4.0.10.tar.gz','/tmp/')
    with cd('/tmp'):
        run('tar xf redis-4.0.10.tar.gz')
        with cd('redis-4.0.9'):
            sudo('make install')
 
@task
def start_redis():
    with settings(warn_only=True):
        result = run('netstat -lntup | grep -w redis-server')
        if result.return_code == 0:
            print(green('redis is started!'))
        else:
            run('set -m ; /usr/local/bin/redis-server &')   # 用pty=False, fabric进程退不出来,不知道为啥,因此这里用set -m
            print(green('redis start Successful'))
 
@task
def clean_local_file():
    local('rm -rf redis-4.0.10.tar.gz')
 
@task
def clean_file():
    with cd('/tmp'):
        sudo('rm -rf redis-4.0.9')
        sudo('rm -rf redis-4.0.10.tar.gz')
 
@task
def install():
    execute(test)
    execute(deploy)
    execute(clean_file)
    execute(clean_local_file)
 execute(start_redis)

  

PS:关于set -m 的做用以下:

"set -m" turns on job control, you can run processes in a separate process group.
理解:在一个独立的进程组里面运行咱们的进程。

  参考资料

http://www.cnblogs.com/dachenzi/p/8695330.html

相关文章
相关标签/搜索