一键部署脚本

一键部署脚本shell 脚本:wget,tar,zip,cd,ls,rm,cp,mkdirecho,sed,ps,netstatgrep,awk,,wc,head,tail,exit1. 参数的输入明确入参2. 用法检查检测用户的入参数,提示用户怎么用,比如说 ./install.sh -P /data/root/test脚本说明:if [ $# -lt 2 ]; then echo “Usage:” echo " ./install.sh -P /data/root/test" exit 1fi3. 读取配置文件,获取参数读取配置文件,解析配置文件的参数,检查配置是否合理假设使用的配置是test.ini$ cat test.ini[mysql]ip=127.0.0.1port=3306读取配置文件和参数value=$(crudini –get $file $section $param)mysql_ip=$($value “mysql” “ip”)通过上面可以获取mysql_ip检查配置function checkIp(){ if [[ $ip =~ ^[0-9]+.[0-9]+.[0-9]+.[0-9]+$ ]]; then exit 0 else echo “fail” exit 1 fi}function checkPort(){ local port="$1" local -i port_num=$(to_int “${port}” 2>/dev/null) if (( $port_num < 1 || $port_num > 65535 )) ; then echo “*** ${port} is not a valid port” 1>&2 exit 1 fi}4. 拼接参数和安装模块如果有不同模块安装,用户需要进行并且参数,并安装其他的模块。5. 编写启动脚本、停止脚本启动服务脚本停止服务脚本监控脚本6. 编写监控脚本通过crontab,进行服务的拉起7. 检查服务脚本编写curl 脚本,查看服务是否正常8. 其他统一处理控制台输出 ...

March 29, 2019 · 1 min · jiezi

mongodb查询表里某字段,进行字符串截取与更新

update() 方法用于更新已存在的文档。语法格式如下: db.collection.update( <query>, <update>, { upsert: <boolean>, multi: <boolean>, writeConcern: <document> } )参数说明:query : update的查询条件,类似sql update查询内where后面的。update : update的对象和一些更新的操作符(如$,$inc…)等,也可以理解为sql update查询内set后面的upsert : 可选,这个参数的意思是,如果不存在update的记录,是否插入objNew,true为插入,默认是false,不插入。multi : 可选,mongodb 默认是false,只更新找到的第一条记录,如果这个参数为true,就把按条件查出来多条记录全部更新。writeConcern :可选,抛出异常的级别。例:图片替换地址,先模糊查询,再替换db.pfs_merchants.find({’logo_url’: /10.2.121.170/}).forEach(function(user) { user.logo_url = user.logo_url.replace(“10.2.121.170”,“10.128.3.80”); print(user.logo_url); db.pfs_merchants.update({"_id":user._id},{$set:{“logo_url”:user.logo_url}}); })

March 29, 2019 · 1 min · jiezi

Web开发中,HTTP概念讲解!

什么是HTTP?HTTP (超文本传输协议) 是用来在 Web 上传输文件的基础 协议 ,最典型的是在浏览器和服务器之间传递以至于上网人员可以浏览他们。作为URI的一部分,“http://”被称为模式,通常位于地址的开头,例如“https://developer.mozilla.org”,就是指示浏览器利用HTTP协议请求文档。https在这个例子中指的是HTTP协议的安全版本,被称为SSL,或者TLS。HTTP 是基于文本 (所有的通信都是以纯文本的形式进行) 以及无状态的 (当前通信不会发现以前的通信状态)。这个特点对在www上访问网页的人是很理想的。而且,HTTP也可以让网站更加的灵活多变,利用在AJAX上等。一、基于HTTP的组件系统在一个浏览器和处理请求的服务器之间,由于Web的层次设计,那些在网络层和传输层的细节都被隐藏起来了。HTTP位于最上层的应用层。虽然底层对于分析网络问题非常重要,但是大多都跟对HTTP的描述不相干。user-agent: 就是任何能够为用户发起行为的工具。Web服务端:Web Server来服务并提供客户端所请求的文档代理(Proxies):在浏览器和服务器之间,有许多计算机和其他设备转发了HTTP消息。由于Web栈层次结构的原因,它们大多都出现在传输层、网络层和物理层上,对于HTTP应用层而言就是透明的,虽然它们可能会对应用层性能有重要影响。还有一部分是表现在应用层上的,被称为代理1、缓存(可以是公开的也可以是私有的,像浏览器的缓存)2、过滤(像反病毒扫描,家长控制…)3、负载均衡(让多个服务器服务不同的请求)4、认证(对不同资源进行权限管理)5、日志记录(允许存储历史信息)二、HTTP 的基本性质HTTP是无状态的,使用Cookies可以创建有状态的会话。 把Cookies添加到头部中,创建一个会话让每次请求都能共享相同的上下文信息,达成相同的状态,将两个请求相关联,如购物车实现两个商品添加! HTTP 和连接 两个最常用的传输层协议:TCP是可靠的,而UDP不是。因此,HTTP依赖于面向连接的TCP进行消息传递,但连接并不是必须的。为了更好的适合HTTP,设计一种更好传输协议的进程一直在进行。Google就研发了一种以UDP为基础,能提供更可靠更高效的传输协议三、HTTP 能控制什么以下是可以被HTTP控制的常见特性:缓存:文档如何缓存能通过HTTP来控制。开放同源限制:为了防止网络窥听和其它隐私泄漏,浏览器强制对Web网站做了分割限制。只有来自于相同来源的网页才能够获取网站的全部信息。这样的限制有时反而成了负担,HTTP可以通过修改头部来开放这样的限制,因此Web文档可以是由不同域下的信息拼接成的(某些情况下,这样做还有安全因素考虑)认证:一些页面能够被保护起来,仅让特定的用户进行访问。基本的认证功能可以直接通过HTTP提供,使用Authenticate相似的头部即可,或用HTTP Cookies来设置指定的会话。代理和隧道:通常情况下,服务器和/或客户端是处于内网的,对外网隐藏真实 IP 地址。因此 HTTP 请求就要通过代理越过这个网络屏障。但并非所有的代理都是 HTTP 代理。例如,SOCKS协议的代理就运作在更底层,一些像 FTP 这样的协议也能够被它们处理。 会话:使用HTTP Cookies允许你用一个服务端的状态发起请求,这就创建了会话。四、HTTP 流当客户端想要和服务端进行信息交互时(服务端是指最终服务器,或者是一个中间代理),过程表现为下面几步:打开一个TCP连接发送一个HTTP报文读取服务端返回的报文信息读取服务端返回的报文信息五、HTTP 报文有两种HTTP报文的类型,请求与响应,每种都有其特定的格式。1、请求请求由以下元素组成:一个HTTP的method,经常是由一个动词像GET, POST 或者一个名词像OPTIONS,HEAD来定义客户端的动作行为。通常客户端的操作都是获取资源(GET方法)或者发送HTML form表单值(POST方法),虽然在一些情况下也会有其他操作。要获取的资源的路径,通常是上下文中就很明显的元素资源的URL,它没有protocol (http://),domain(developer.mozilla.org),或是TCP的port(HTTP一般在80端口)。HTTP协议版本号。为服务端表达其他信息的可选头部headers。对于一些像POST这样的方法,报文的body就包含了发送的资源,这与响应报文的body类似。2、响应响应报文包含了下面的元素:HTTP协议版本号。一个状态码(status code),来告知对应请求执行成功或失败,以及失败的原因。一个状态信息,这个信息是非权威的状态码描述信息,可以由服务端自行设定。HTTP headers,与请求头部类似。可选项,比起请求报文,响应报文中更常见地包含获取的资源body。

March 28, 2019 · 1 min · jiezi

python模块之subprocess类与常量

常量subprocess.DEVNULL:可传递给stdin, stdout, stderr参数的特殊值,意味着将使用特殊文件os.devnull重定向输入输出subprocess.PIPE:可传递给stdin, stdout, stderr参数的特殊值,意味着使用管道重定向输入输出subprocess.STDOUT:可传递给stderr参数的特殊值,表示重定向标准错误到标准输出Popen在一个新的进程中执行子程序。构造参数(args, bufsize=-1, executable=None, stdin=None, stdout=None, stderr=None, preexec_fn=None, close_fds=True, shell=False, cwd=None, env=None, universal_newlines=None, startupinfo=None, creationflags=0, restore_signals=True, start_new_session=False, pass_fds=(), *, encoding=None, errors=None, text=None)args:字符串或序列。如果是序列,则args中的第一个元素是要执行的程序;如果是字符串,解释执行与平台有关,在POSIX系统args将被解释为要执行的程序的名称或路径(前提是不传递任何参数给程序)。shell:指定是否使用shell作为要执行的程序。如果设置为True,更推荐和字符串类型的args参数使用。在POSIX系统,shell=True默认使用/bin/sh作为shell。如果args为字符串,该字符串表示要通过shell执行的命令;如果args为序列,第一个元素指定要执行的程序,其他元素视为参数。在Windows系统,shell=True默认使用COMSPEC环境变量指定的shell,一般是C:\WINDOWS\system32\cmd.exe。唯一需要指定shell=True的场景是要执行的指令是shell内置的,如dir, copy。bufsize:创建stdin/stdout/stderr管道文件对象时作为对应的参数传递给open()函数。0:不始用缓冲1:使用行缓冲其他正整数:缓冲大小负整数(默认):使用系统默认值executable:使用shell=True的场景很少。shell=True时,在POSIX系统上此参数表示指定一个新的shell程序替换默认shell/bin/sh。stdin/stdout/stderr:分别指定程序执行的标准输入,标准输出,标准错误。可选值包括PIPE,DEVNULL,已存在的文件描述符(正整数),已存在的文件对象,None。子进程文件句柄继承自父进程。除此之外,stderr还可以是STDOUT,表示标准错误输出重定向到标准输出。preexec_fn:限于POSIX系统,设置一个可调用对象,先于子进程中的程序执行。close_fds:如果为False,文件描述符遵循Inheritance of File Descriptors中描述的inheritable标识。如果为True,在POSIX系统下,在子进程执行前关闭除0,1,2外的文件描述符。pass_fds:限于POSIX,可选的文件描述符序列,用于在父子进程间保持开放。只要提供了此参数,close_fds强制设为True。cwd:在子进程执行前改变工作目录为cwd,可以是字符串或path-like对象。restore_signals:限于POSIX,略start_new_session:限于POSIX,略env:dict对象,为新进程定义环境变量,替换继承自父进程的变量。在Windows下,要运行side-by-side assembly必须包含可用的环境变量SystemRoot。如果指定了env,就必须提供程序执行依赖的所有环境变量encoding/errors/text/universal_newlines:stdin/stdout/stderr默认以二进制模式打开。但如果指定了encoding/errors或者text为True,将使用指定的encoding和errors以文本模式打开stdin/stdout/stderr。universal_newlines参数等同于text,用于后向兼容。startupinfo:仅限于Windows,略creationflags:仅限于Windows,略方法poll():检查子进程是否终止。返回None表示未终止,否则设置returncode属性并返回。wait(timeout=None):如果子进程在timeout后没有终止,抛出TimeoutExpired异常。否则设置returncode属性并返回。communicate(input=None, timeout=None):进程交互:发送数据到stdin,读取stdout或stderr的数据知道读取到结束符。返回(stdout_data, stderr_data)形式的元组。input为None或要发送到子进程的数据,根据stream打开模式的不同,可以是string或byte类型。如果要和进程的stdin交互,创建Popen对象时需要指定stdin=PIPE。类似的,返回的tuple如果希望是非None,需要设置stdout=PIPE和/或stderr=PIPE。如果子进程在timeout后没有终止,抛出TimeoutExpired异常,但子进程并未kill掉,一个良好的应用应该kill掉子进程并结束交互:proc = subprocess.Popen(…)try: outs, errs = proc.communicate(timeout=15)except TimeoutExpired: proc.kill() outs, errs = proc.communicate()send_signal(signal):发送信号到子进程terminate():终止子进程。POSIX系统上,发送SIGTERM信号到子进程,Windows系统上会调用TerminateProcess()终止进程kill():强制终止子进程。POSIX系统上,发送SIGKILL信号到子进程。Windows系统上kill()是terminate()的别名属性args:传入Popen构造器的第一个参数,list或string类型stdin:如果传递给Popen的stdin参数是PIPE,该属性表示string或byte类型的可写stream对象。如果传递给Popen的stdin参数不是PIPE,此属性值为Nonestdout:与Popen.stdin相近,但stream对象是可读的stderr:与Popen.stdout相近pid:子进程进程号。如果设置了shell=True,pid表示派生shell的进程号returncode:子进程返回码,None表示进程未终止。负数-N表示进程被信号N终止(仅限POSIX)。CompletedProcessrun()函数运行的返回值,表示进程执行完成。属性args:传入run()函数的第一个参数,list或string类型returncode:子进程退出码。如果为负数,表示进程因为某个信号退出stdout:捕获的子进程的标准输出,默认为byte类型,如果run()函数调用时指定了encoding或errors,或设置了text=True则为string类型。如果未捕获标准输出返回Nonestderr:捕获的子进程的标准错误,默认为byte类型,如果run()函数调用时指定了encoding或errors,或设置了text=True则为string类型。如果未捕获标准错误返回None方法check_returncode():如果returncode非0,抛出CalledProcessError异常异常subprocess.SubprocessErrorsubprocess模块的异常基类subprocess.TimeoutExpired子进程执行超时。属性cmd:指令timeout:秒为单位的时间output:run()或check_output()函数捕获到的子进程的输出,否则为Nonestdout:output属性别名stderr:run()函数捕获到的子进程的错误输出,否则为Nonesubprocess.CalledProcessErrorcheck_call()或check_output()函数返回非0状态码时抛出。属性returncode:子进程退出码。如果为负数,表示进程因为某个信号退出cmd:同TimeoutExpiredoutput:同TimeoutExpiredstdout:同TimeoutExpiredstderr:同TimeoutExpired

March 27, 2019 · 1 min · jiezi

python模块之subprocess模块级方法

subprocess.run()运行并等待args参数指定的指令完成,返回CompletedProcess实例。参数:(*popenargs, input=None, capture_output=False, timeout=None, check=False, **kwargs)。除input, capture_output, timeout, check,其他参数与Popen构造器参数一致。capture_output:如果设置为True,表示重定向stdout和stderr到管道,且不能再传递stderr或stdout参数,否则抛出异常。input:input参数将作为子进程的标准输入传递给Popen.communicate()方法,必须是string(需要指定encoding或errors参数,或者设置text为True)或byte类型。非None的input参数不能和stdin参数一起使用,否则将抛出异常,构造Popen实例的stdin参数将指定为subprocess.PIPE。timeout:传递给Popen.communicate()方法。check:如果设置为True,进程执行返回非0状态码将抛出CalledProcessError异常。# 源码def run(*popenargs, input=None, capture_output=False, timeout=None, check=False, **kwargs): if input is not None: if ‘stdin’ in kwargs: raise ValueError(‘stdin and input arguments may not both be used.’) kwargs[‘stdin’] = PIPE if capture_output: if (‘stdout’ in kwargs) or (‘stderr’ in kwargs): raise ValueError(‘stdout and stderr arguments may not be used ’ ‘with capture_output.’) kwargs[‘stdout’] = PIPE kwargs[‘stderr’] = PIPE with Popen(*popenargs, **kwargs) as process: try: stdout, stderr = process.communicate(input, timeout=timeout) except TimeoutExpired: process.kill() stdout, stderr = process.communicate() raise TimeoutExpired(process.args, timeout, output=stdout, stderr=stderr) except: # Including KeyboardInterrupt, communicate handled that. process.kill() # We don’t call process.wait() as .exit does that for us. raise retcode = process.poll() if check and retcode: raise CalledProcessError(retcode, process.args, output=stdout, stderr=stderr) return CompletedProcess(process.args, retcode, stdout, stderr)python3.5版本前,call(), check_all(), checkoutput()三种方法构成了subprocess模块的高级API。subprocess.call()运行并等待args参数指定的指令完成,返回执行状态码(Popen实例的returncode属性)。参数:(*popenargs, timeout=None, **kwargs)。与Popen构造器参数基本相同,除timeout外的所有参数都将传递给Popen接口。调用call()函数不要使用stdout=PIPE或stderr=PIPE,因为如果子进程生成了足量的输出到管道填满OS管道缓冲区,子进程将因不能从管道读取数据而导致阻塞。# 源码def call(*popenargs, timeout=None, **kwargs): with Popen(*popenargs, **kwargs) as p: try: return p.wait(timeout=timeout) except: p.kill() p.wait() raisesubprocess.check_call()运行并等待args参数指定的指令完成,返回0状态码或抛出CalledProcessError异常,该异常的cmd和returncode属性可以查看执行异常的指令和状态码。参数:(*popenargs, **kwargs)。全部参数传递给call()函数。注意事项同call()# 源码def check_call(*popenargs, **kwargs): retcode = call(*popenargs, **kwargs) if retcode: cmd = kwargs.get(“args”) if cmd is None: cmd = popenargs[0] raise CalledProcessError(retcode, cmd) return 0subprocess.check_output()运行并等待args参数指定的指令完成,返回标准输出(CompletedProcess实例的stdout属性),类型默认是byte字节,字节编码可能取决于执行的指令,设置universal_newlines=True可以返回string类型的值。如果执行状态码非0,将抛出CalledProcessError异常。参数:(*popenargs, timeout=None, **kwargs)。全部参数传递给run()函数,但不支持显示地传递input=None继承父进程的标准输入文件句柄。要在返回值中捕获标准错误,设置stderr=subprocess.STDOUT;也可以将标准错误重定向到管道stderr=subprocess.PIPE,通过CalledProcessError异常的stderr属性访问。# 源码def check_output(*popenargs, timeout=None, **kwargs): if ‘stdout’ in kwargs: raise ValueError(‘stdout argument not allowed, it will be overridden.’) if ‘input’ in kwargs and kwargs[‘input’] is None: # Explicitly passing input=None was previously equivalent to passing an # empty string. That is maintained here for backwards compatibility. kwargs[‘input’] = ’’ if kwargs.get(‘universal_newlines’, False) else b’’ return run(*popenargs, stdout=PIPE, timeout=timeout, check=True, **kwargs).stdoutsubprocess模块还提供了python2.x版本中commands模块的相关函数。subprocess.getstatusoutput(cmd)实际上是调用check_output()函数,在shell中执行string类型的cmd指令,返回(exitcode, output)形式的元组,output(包含stderr和stdout)是使用locale encoding解码的字符串,并删除了结尾的换行符。# 源码try: data = check_output(cmd, shell=True, universal_newlines=True, stderr=STDOUT) exitcode = 0except CalledProcessError as ex: data = ex.output exitcode = ex.returncodeif data[-1:] == ‘\n’: data = data[:-1]return exitcode, datasubprocess.getoutput(cmd)与getstatusoutput()类似,但结果只返回output。 ...

March 26, 2019 · 2 min · jiezi

上云端运维打破孤岛式管理服务器

这是2019年linux运维市场的一个变革,也是新一轮技术突破.2019年之前很多linux运维人员还在使用传统的linux面板运维服务器,在服务器敲击安装代码,上服务器配置才能启动管理链接地址,服务器被黑,被恶意登陆也是事后才知道,这里就凸显了一个问题,现在国内所有的服务器都是单线单机运维,安全和管理隐患很大. 现在很多企业和个人把网站或服务搬上互联网,其中的载体云服务器,就需要多方面的管理和运维,尤其是批量化操作,集群式管理多台服务器时,这时候”云端运维”就比较有市场需要. 各个云服务器厂家已经意识到这一步,比如腾讯云有针对企业级大用户开发的PAAS平台,通过云端运维来管理批量化的管理云服务器. 云端融合是”云端运维”的第一步,现在市场上已经有厂家针对这样的需求有产品推出,有别于传统单机ip地址化管理云服务器,这家厂家采用云端SAAS化管理模式,通过平台吧云服务器添加进控制台,进行批量或单独的管理运维,这家厂商未来不可限量.厂商的产品名称为旗鱼云梯,网址:www.marlinos.com,毕竟自动化运维先做到的就是让IT运维人员配置和操作服务器做的简单,缩短开发中维护方面的难度和时间. 只有让运维人员做到使用简单,云端操作,工具可以集群管理和批量使用,那么这个平台将是以后的趋势,是运维市场一个变革,是脱离传统运维模式的一种革命.

March 26, 2019 · 1 min · jiezi

HTTP 协议讲解!

HTTP定义HTTP(HyperText Transfer Protocol,超文本传输协议)最早就是计算机与计算机之间沟通的一种标准协议,这种协议限制了通讯内容的格式以及各项内容的含义。随着时代的发展,技术的变迁,这种协议现在广泛的应用在各种领域,也不仅仅局限于计算机与计算机之间,手机、电视等各种智能设备很多时候都在使用这种协议通讯,所以一般现在称 HTTP 为端与端之间的通讯协议。Web 属于 B/S 架构的应用软件,在 B/S 架构中,浏览器与服务器沟通的协议就是 HTTP 协议,作为一个合格的Web 开发者,了解 HTTP 协议中约定的内容是一门必修课。**应用软件架构一般分为两类:**B/S 架构:Browser(浏览器) ←→ Server(服务器),这种软件都是通过浏览器访问一个网站使用,服务器提供数据存储等服务。C/S 架构:Client(客户端) ←→ Server(服务器),这种软件通过安装一个软件到电脑,然后使用,服务器提供数据存储等服务。1、约定内容请求 / 响应报文格式 请求方法 —— GET / POST 响应状态 —— 200 / 404 / 302 / 304 预设的请求 / 响应头2、约定形式客户端通过随机端口与服务端某个固定端口(一般为80)建立连接 三次握手客户端通过这个连接发送请求到服务端(这里的请求是名词)服务端监听端口得到的客户端发送过来的请求服务端通过连接响应给客户端状态和内容请求头客户端想要告诉服务端的一些额外信息,以下为常见的请求头:响应头服务端想要告诉客户端的一些额外信息,常见的有以下:3、请求方式GET:拿,获取POST:发,给4、状态码状态代码由三位数字组成,第一个数字定义了响应的类别,且有五种可能取值。1xx:指示信息 —— 表示请求已接收,继续处理。2xx:成功 —— 表示请求已被成功接收、理解、接受。3xx:重定向 —— 要完成请求必须进行更进一步的操作。4xx:客户端错误 —— 请求有语法错误或请求无法实现。5xx:服务器端错误 —— 服务器未能实现合法的请求。常见状态代码、状态描述的说明如下。200 OK:客户端请求成功。400 Bad Request:客户端请求有语法错误,不能被服务器所理解。401 Unauthorized:请求未经授权,这个状态代码必须和 WWW-Authenticate 报头域一起使用。403 Forbidden:服务器收到请求,但是拒绝提供服务。404 Not Found:请求资源不存在,举个例子:输入了错误的URL。500 Internal Server Error:服务器发生不可预期的错误。503 Server Unavailable:服务器当前不能处理客户端的请求,一段时间后可能恢复正常。

March 18, 2019 · 1 min · jiezi

博客开篇

之前一直想开博客,但是拖来拖去就到19年,想想也是惭愧,想来工作也有些念头了。用博客来输出这些年的技术积累,个人观点,生活状态。本来想用github+hexo 搭建一个,不过目前公司和家里各有电脑,用起来也不太方便,遂就在这里开始吧。人生短短数十载,除了睡觉,吃饭其实,所剩时间很少了,一朝幡然醒悟,觉今是而昨非,下决心早起早睡,充分利用时间更新自己,虽然愚笨,但是坚持下来也能精进一二,也没荒废了时光博客记录一些个人技术成长,欢迎讨论。

March 18, 2019 · 1 min · jiezi

Linux命令行文本工具

浏览文件cat 查看文件内容more 以翻页形式查看文件内容(只能向下翻页)less 以翻页形式查看文件内容(可以上下翻页)head 查看文件的头几行(默认10行)tail 查看文件的尾几行(默认10行)示例:1、查看前10行$ head -n 10 test.log2、跟踪查看最后100行$ tail -f -n 100 test.logwc命令 wc 用于统计文件的行数、单词数、字符数等。不带参数时默认输出一行,字段格式为:行数 单词数 字符数 文件名常用参数:-l 只统计行数-w 只统计单词数-c 只统计字节数-m 只统计字符数-L 最长的一行包含了多少个字符grepgrep (global search regular expression(RE) and print out the line,全面搜索正则表达式并把行打印出来)是一种强大的文本搜索工具,它能使用正则表达式搜索文本,并把匹配的行打印出来。常用来在结果中搜索特定的内容。一般格式: grep [选项] 基本正则表达式 [文件]选项 -c 只输出匹配行的计数 -i 不区分大小写(单字符) -h 不显示文件名(多文件时) -l 只输出文件名(多文件时) -n 显示匹配行及行号 -s 不显示错误信息 -v 显示不包含匹配文本的所有行 -r 递归在子目录里文件查找 –color=auto 自动高亮找到的关键词示例1) 将/etc/passwd,有出现 root 的行取出来:$ grep ‘root’ /etc/passwdroot❌0:0:root:/root:/bin/bashoperator❌11:0:operator:/root:/sbin/nologin# 或者$ cat /etc/passwd | grep ‘root'2)将/etc/passwd,有出现 root 的行取出来,同时显示这些行在/etc/passwd的行号:$ grep -n root /etc/passwd1:root❌0:0:root:/root:/bin/bash30:operator❌11:0:operator:/root:/sbin/nologin3)将/etc/passwd,将没有出现 root 的行取出来$ grep -v root /etc/passwdroot❌0:0:root:/root:/bin/bashoperator❌11:0:operator:/root:/sbin/nologin4)将/etc/passwd,将没有出现 root 和nologin的行取出来$ grep -v root /etc/passwd | grep -v nologinroot❌0:0:root:/root:/bin/bashoperator❌11:0:operator:/root:/sbin/nologin5) 查找nginx是否运行:$ ps aux | grep nginxwww 1576 0.0 2.7 71652 28232 ? S Aug14 0:21 nginx: worker process根据文件内容递归查找目录6)在当前目录里文件查找字符串’math’$ grep ‘math’ *grep: my: Is a directorygrep: my1: Is a directorys.txt:lisi 1989 male math 99s.txt:wangxuebing 1978 male math 89s.txt:lichang 1989 male math 997)在当前目录及其子目录下搜索’math’行的文件$ grep -r ‘math’ * 8)当前目录及其子目录下搜索’math’行的文件,但是不显示匹配的行,只显示匹配的文件$ grep -l -r ‘math’ * s.txt显示行号:$ grep -nr ‘swoole’ –color=auto /work/www/mixphp/正则表达式支持正则语法,单引号里面写正则。正则示例:’t[ae]st’ #查找test或者tast’[^g]oo’ #字符串不含有g。注意中括号里是不包含,不是以其开头’[^a-z]oo’ #字符串前不包含a-z小写字母’[0-9]’ #包含数字0-9’^the’ #匹配字母t开始的字符’the$’ #匹配字母e结尾的字符示例:$ grep ‘^xu’ s.txt xuliang 1977 male economic 89xuxin 1986 female english 99更多的正则知识请查看正则表达式相关知识。扩展grep(grep -E 或者 egrep)使用扩展grep的主要好处是增加了额外的正则表达式元字符集。示例:查找包含1990和1989的行:$ grep -E ‘1990|1989’ s.txt lisi 1989 male math 99wanglijiang 1990 female chinese 78lichang 1989 male math 99wanglijiang 1990 female chinese 78lisibao 1989 male math 99xiaobao 1990 female chinese 78awkawk简介awk是一个强大的文本分析工具,相对于grep的查找,sed的编辑,awk在其对数据分析并生成报告时,显得尤为强大。简单来说awk就是把文件(或其他方式的输入流, 如重定向输入)逐行的读入(看作一个记录集), 把每一行看作一条记录,以空格(或t,或用户自己指定的分隔符)为默认分隔符将每行切片(类似字段),切开的部分再进行各种分析处理。awk有3个不同版本: awk、nawk和gawk,未作特别说明,一般指gawk,gawk 是 AWK 的 GNU 版本。Awk基本语法: awk ‘pattern1 {command1;command 2…; command 3} pattern2 { command …}‘pattern表示用来过滤记录的模式,可是是正则表达式,关系运算表达式,也可以什么也没有(表示选中所有记录)。每个pattern选中的行记录会被花括号括起来的命令command操作一遍, command之间用;分割。 花括号里面可以什么也没有, 则默认为print输出整行记录。 Comamnd可以是输出, 可以是算术运算,逻辑运算,循环控制等等。示例s.txtzhangsan 1977 male computer 83lisi 1989 male math 99wanglijiang 1990 female chinese 78xuliang 1977 male economic 89xuxin 1986 female english 99wangxuebing 1978 male math 89lichang 1989 male math 99wanglijiang 1990 female chinese 78zhangsansan 1977 male computer 83 langxuebing 1978 male math 89lisibao 1989 male math 99xiaobao 1990 female chinese 78一行中的5个字段分别表示姓名, 出生年, 性别,科目,分数, 是一个很传统很典型的报表文件。现在演示awk是如何查找的:1)直接输出1990年出生的同学:$ awk ‘/1990/’ s.txtwanglijiang 1990 female chinese 78wanglijiang 1990 female chinese 78xiaobao 1990 female chinese 78 或者:$ awk ‘/1990/{print $0}’ s.txtawk默认把输入的内容以空格拆分出每列。$0表示匹配所有列,print $0将输出所有列,每列分隔符是空格。2)对chinese的课程的行输出"语文":$ awk ‘/chinese/{print “语文”}’ s.txt语文语文语文3)记录的头部和结尾加上一段说明:$ awk ‘BEGIN{print “Result of the quiz:\n”}{print $0}END{print “——”}’ s.txtResult of the quiz:zhangsan 1977 male computer 83lisi 1989 male math 99wanglijiang 1990 female chinese 78xuliang 1977 male economic 89xuxin 1986 female english 99wangxuebing 1978 male math 89lichang 1989 male math 99wanglijiang 1990 female chinese 78zhangsansan 1977 male computer 83langxuebing 1978 male math 89lisibao 1989 male math 99xiaobao 1990 female chinese 78——AWK工作流程:逐行扫描文件,从第一行到最后一行,寻找匹配特定模式的行,并在这些行上进行用户想要到的操作。BEGIN只会在最开始执行;END只会在扫描所有行数之后执行。BEGIN和END之间的花括号的内容每扫描一行都会执行。4)查找女生的成绩且只输出姓名、学科、成绩:$ awk ‘$3==“female”{print $1,$4,$5}’ s.txtwanglijiang chinese 78xuxin english 99wanglijiang chinese 78xiaobao chinese 78$1表示第1列,$n类推。这里条件是表达式,而不是正则。print里,表示空格分隔符。5)找出1990年出生的学生姓名,并要求匹配正则:$ awk ‘$2~/1990/{print $1}’ s.txtwanglijiangwanglijiangxiaobao这里~表示匹配正则表达式。!表示不匹配正则表达式。如果需要多选,则改成:$ awk ‘$2/(1990|1991)/{print $1}’ s.txt6) 找出大于1985年出生的学生姓名,年龄,使用表达式:$ awk ‘$2>“1985”{print $1,$2}’ s.txtlisi 1989wanglijiang 1990xuxin 1986lichang 1989wanglijiang 1990lisibao 1989xiaobao 1990awk内置变量awk有许多内置变量用来设置环境信息,这些变量可以被改变,下面给出了最常用的一些变量。ARGC 命令行参数个数ARGV 命令行参数排列ENVIRON 支持队列中系统环境变量的使用FILENAME awk浏览的文件名FNR 浏览文件的记录数FS 设置输入列分隔符,等价于 -F选项。默认是空格OFS 输出列分隔符。默认是空格NF 列的字段总数,$NF指当前列最后一个的值NR 已读的记录数(当前行数)ORS 行输出分隔符,默认为\nRS 行输入分隔符,默认分隔符为\nRT 指定的那个分隔符$0 指整条记录$1, $2, … $n 分别是第1,2,…n列的字段值示例:6)第四个字段科目为chinese的记录编号, 学生姓名, 科目:$ awk ‘$4==“chinese”{print NR, $1, $4, $5}’ s.txt3 wanglijiang chinese 788 wanglijiang chinese 7812 xiaobao chinese 787)统计数学成绩大于90的个数:$ awk ‘BEGIN{goodMath=0;}($4==“math” && $5>90){goodMath++}END{print goodMath}’ s.txt38)更换输入换行符echo “11 22|12 23” | awk ‘BEGIN{RS="|"}{print $0}‘等价于:echo “11 22|12 23” | awk -v RS=’|’ ‘{print $0}‘输出:11 2212 23注:文本内容(例如"11 22n12 23")里的\n不是换行符,实际是\n。shell里字符串的\n要生效,需要使用echo -e。示例:echo -e “11 22\n12 23” | awk ‘{print $0}’ 。9)更换列输入、输出分隔符:$ cat /etc/passwd |awk -F ‘:’ -v OFS=’\t’ ‘{print $1}’ rootdaemonbinsys-F指定输入域分隔符为:。-F等价于-v FS。-v OFS指定输出域分隔符为\t。注:-F和-v OFS在处理MySQL数据导出导入时有非常大的作用:我们可以使用-F指定每列是以\t或者,(csv格式)分隔的;输出的时候我们可以用-v OFS指定每列分隔符,默认的空格经常不足以方便使用。如果使用了-v OFS,使用print $0是改变不了输出分隔符的,需要手动指定列,例如print $1,$2。10)批量操作# docker里关闭所有正在运行容器docker ps | awk ‘{print $1}’ | xargs docker stop# docker里删除所有容器应用docker ps -a | awk ‘{print $1}’ | xargs docker rm11)文件切割awk ‘{filename = “sub.” int((NR-1)/5000) “.csv”; print >> filename}’ history.csv每5W行切割为一个文件。12)分组合并例如test.txt文本内容是:yjc 1 20170118yjc 1 20170118lisi 1 20170223需要整理成(姓名、日期相同的计数累加):yjc 2 20170118lisi 1 20170223脚本:cat test.txt | awk ‘{a[$1$3][“c”]+=$2;a[$1$3][“u”]=$1;a[$1$3][“d”]=$3;}END{for(i in a)print a[i][“u”],a[i][“c”],a[i][“d”]}‘awk函数awk还支持内置函数。这里只列举部分。int(x) 返回 x 的截断至整数的值rand() 返回任意数字 n,其中 0 <= n < 1。sqrt(x) 返回 x 平方根。sub(Ere, Repl, [In]) 字符串替换gsub(Ere, Repl, [In]) 正则替换index(str, str2) str2在str中出现的位置,从1开始编号。不存在返回0substr(str, M, [N]) 返回具有N参数指定的字符数量子串。如果未指定 N 参数,则子串的长度将是M参数指定的位置到str参数的末尾的长度。length [(str)] 返回 str 参数指定的字符串的长度(字符形式)。如果未给出 str 参数,则返回整个记录的长度($0 记录变量)。match(str, Ere) 返回Ere匹配的字符串在str中出现的位置,从1开始编号。不匹配返回 -1tolower(str) 字符串转小写toupper(str) 字符串转大写split(str, A, [Ere] ) 将str参数指定的参数分割为数组元素 A[1], A[2], . . ., A[n],并返回n变量的值。分隔符由正则表达式Ere匹配得出。mktime(YYYY MM DD HH MM SS[DST]) 根据日期生成时间戳。失败返回-1 strftime([format [, timestamp]]) 格式化时间输出,将时间戳转为时间字符串systime() 得到时间戳13) int函数$ echo “10.22元” | awk ‘{$1=int($1);print $1}‘10如果只是想转换为数字,可以使用乘法运算:$ echo “10.22元” | awk ‘{$1=$11;print $1}‘10.2214) 数学函数$ echo 9 | awk ‘{$1=sqrt($1);print $1}‘315) 字符串函数$ awk ‘BEGIN{info=“test2010test”;gsub(“2010”," “,info);print info}’test test$ awk ‘BEGIN{info=“test2010test”;gsub(/[0-9]+/,” “,info);print info}’test test$ awk ‘BEGIN{info=“test2010test”;print index(info, 2010);}‘5$ awk ‘BEGIN{info=“test2010test”;print substr(info, 5);}‘2010test$ awk ‘BEGIN{info=“test2010test”;print length(info);}‘12$ awk ‘BEGIN{info=“test2010test”;print toupper(info);}‘TEST2010TEST$ awk ‘BEGIN{info=“test2010test”;print match(info, /[0-9]+/);}‘5$ echo “10:20” | awk ‘{split($1,arr,”:");print arr[1];print arr[1];print arr[1]*60+arr[2];}‘1010620$ awk ‘BEGIN{info=“hello shell”;split(info,arr," “);print length(arr);for(k in arr){print k,arr[k];}}‘21 hello2 shellawk for …in 循环,是一个无序的循环。 并不是从数组下标1…n ,因此使用时候需要注意。split生成的数组下标从1开始。16) 时间戳函数$ awk ‘BEGIN{print systime();}‘1543668202$ awk ‘BEGIN{print strftime("%Y-%m-%d %H:%M:%S”,1543668202);}‘2018-12-01 20:43:22$ awk ‘BEGIN{print mktime(“2018 12 01 20 43 22”);}‘1543668202$ awk ‘BEGIN{$1=“2018-12-20”;gsub(/[-:]/," “,$1);print mktime($1.” 20 43 22");}‘1545309802printf格式化格式和C语言的一样。支持%d %s %u %f %c %o %x等。格式符说明%d十进制有符号整数%u十进制无符号整数%f浮点数%s字符串%c单个字符%p指针的值%e指数形式的浮点数%x%X 无符号以十六进制表示的整数%o无符号以八进制表示的整数%g自动选择合适的表示法$ awk ‘BEGIN{x=12.12; printf("%.2f,%.2u,%d,%s,%o\n",x,x,x,x,x);}‘12.12,12,12,12.12,14其它详见:https://www.cnblogs.com/cheng…17) if…else示例: 将下列文件格式转换为新的格式:原格式:route.csv//测试’/api/user/info’ => ‘User::getUserInfo’,’/api/user/info_batch’ => ‘User::getUserInfoBatch’, //批量获取新格式:route2.csv//测试’/api/user/info’ => [“route” => ‘User::getUserInfo’, “tag” => ‘user’],’/api/user/info_batch’ => [“route” => ‘User::getUserInfoBatch’, “tag” => ‘user’], //批量获取脚本:cat route.csv | awk -F ‘=>’ -v OFS=’\t’ ‘{print $1,$2}’ | awk -F ‘,’ -v OFS=’\t’ ‘{print $1,$2}’ | awk -F ‘\t’ ‘{if($2>"") print $1, " => [ "route" => “$2”, "tag" => “user” ], “$3; else print $1 }’ >> route2.csvsed和grep、awk不同,sed更侧重对搜索文本的处理,如修改、删除、替换等等。sed工作原理:sed会一次处理一行内容。处理时,把当前处理的行存储在临时缓冲区中,成为"模式空间”,接着用sed命令处理缓冲区中的内容,处理完成后,把缓冲区的内容送往屏幕。接着处理下一行,这样不断重复,直到文件末尾。文件内容并没有改变,除非你使用重定向存储输出。语法sed [options] ‘command’ file(s)sed [options] -f scriptfile file(s)参数说明:-n, –quiet, –silent 安静模式,也就是不会输出默认打印信息-e <script>, –expression=<script> 以选项中指定的script来处理输入的文本文件,可以多条-i 直接编辑文件而不是显示在屏幕上-f <script文件>, –file=<script文件> 以选项中指定的script文件来处理输入的文本文件。-h, –help 显示帮助。-V, –version 显示版本信息。动作说明:a 表示在指定行下边插入指定行的内容。i 命令i和a使用上基本上一样,只不过是在指定行上边插入指定行的内容。d 表示删除指定的行内容c c是表示把指定的行内容替换为自己需要的行内容。注意是整行替换y 字符替换,可以替换多个字符,只能替换字符不能替换字符串,且不支持正则表达式s 字符串替换,是平时sed使用的最多的子命令。支持正则表达式r 类似于a,也是将内容追加到指定行的后边,只不过r是将指定文件内容读取并追加到指定行下边。 sed命令必须跟一个动作。新建文件t.txt:zhangsan 1977 male computer 83lisi 1989 male math 99wanglijiang 1990 female chinese 781)新增一行:第3行后新增:$ sed -e ‘3a newline’zhangsan 1977 male computer 83lisi 1989 male math 99wanglijiang 1990 female chinese 78newline2)插入一行:第3行前插入:$ sed -e ‘3i newline’zhangsan 1977 male computer 83lisi 1989 male math 99newlinewanglijiang 1990 female chinese 783)删除一行:删除第3行:$ sed -e ‘3d’zhangsan 1977 male computer 83lisi 1989 male math 994)替换一行:$ sed -e ‘3c newline’zhangsan 1977 male computer 83lisi 1989 male math 99newline5)行内部分内容的替换:格式:sed ’s/要被取代的字串/新的字串/g’示例:$ sed ‘3s/1990/2000/g’ t.log zhangsan 1977 male computer 83lisi 1989 male math 99wanglijiang 2000 female chinese 78一些替换规则范例:s/\t/","/g; \t换成",“s/^/”/; 开头加上"s/$/"/;s/\n//g 末尾加上"s/"//g; 引号去掉 s/,/ /g 逗号换成空格s/,/\n/g 逗号换成空行s/[NULL]//g; NULL换成空白s/[,,]/\n/g 中英文逗号换成空行6)多行操作:$ sed ‘2,3d’ t.log #删除2,3行$ sed ‘2,$d’ t.log #从第2行开始删除到末尾$ sed ‘2,3a test’ t.log #分别在第2行,第3行增加字符"test"7)r命令sed ‘2r t.log’ message将a.txt文件内容读取并插入到t1.log文件第2行的下边。注意:1、上述的操作均只在输出缓冲里操作,文件并没有变化;需要直接修改文件,前面加-i参数;2、-e参数可以没有。xargs之所以能用到这个命令,关键是由于很多命令不支持|管道来传递参数,而日常工作中有有这个必要,所以就有了xargs命令,例如:find /sbin -perm +700 | ls -l #这个命令是错误的find /sbin -perm +700 | xargs ls -l #这样才是正确的sort排序,默认按照字符升序排序。-r, –reverse 逆向(倒序)排序-n, –numeric-sort 基于数字排序-d, –dictionary-order 字典序-h, –human-numeric-sort 按照人类可识别的顺序,例如(1G>1M>1K)-f, –ignore-case 忽略大小写-u, –unique 去重复(剔除重复行)-t, –field-separator=SEP 指定分隔符(一般配合-k参数使用,单纯分割毫无意义)-k, –key=POS1[,POS2] 当指定分割符时,按照第n个字段进行排序(序号n从1开始) 以下面的内容为例:$ cat 1.txt45645612311345678456按字符降序:sort -r 1.txt 56784564564563412311按数值降序:# sort -rn 1.txt 56784564564561233411去除重复行:$ sort -nu 1.txt 11341234565678-h这个一般可以用来排序文件大小:$ du -h2.0G ./test24.0K ./test3316M ./test2.3G .$ du -h |sort -hr2.3G .2.0G ./test2316M ./test4.0K ./test3-t,-k适用于多列的情况:$ cat 2.txtbaidu,100,5000google,110,5000sohu,100,4500guge,50,3000分别表示公司、员工数、最低工资。我们先按照员工数升序,如果员工数相同,按照最低工资升序:$ sort -n -t ‘,’ -k 2 -k 3 2.txt guge,50,3000sohu,100,4500baidu,100,5000google,110,5000按照员工工资降序排序,如果员工人数相同的,则按照公司人数升序排序:# sort -t ‘,’ -k 3nr -k 2n 2.txt baidu,100,5000google,110,5000sohu,100,4500guge,50,3000uniq用于去重,需要先执行sort。-c: 显示文件中行重复的次数-d: 只显示重复的行(相邻的行)-D: 把所有重复的行都显示出来(相邻的行)示例:# 排序后去重$ sort 1.txt |uniq11123344565678# 显示重复次数$ sort 1.txt | uniq -c 1 11 1 123 1 34 3 456 1 5678# 仅显示重复的内容$ sort 1.txt | uniq -d456# 显示所有重复的行内容$ sort 1.txt | uniq -D456456456注:uniq仅针对换行符是\n,对于windows下编写的文件如果换行符是\r\n则无法排序。rsyncrsync(remote sync) 是用于同步某一位置文件和目录到另一位置的有效方法。备份的位置可以在本地服务器或远程服务器。rsync [OPTION]… SRC [SRC]… DEST示例: 将Jenkins编译生成的文件同步到远程服务器,排除git目录:rsync -azP –exclude .git /var/lib/jenkins/workspace/myapi/output/ root@172.17.17.10:/alidata/myapi/参数: –z:允许压缩 –v:冗长 –r:递归 –a: 递归模式,同步软链接,同步权限, 同步时间戳,同步属主和属组与scp的区别:1、对于scp来说,除了在机器之间和一个机器目录之间进行数据同步之外,还可以在两台不同机器之间进行数据同步。比如你在A机器,可以对B、C两台机器上的数据进行同步,但是rsync就不可以;也就是说当rsync进行跨机器同步数据的时候只可以在本机与另外一台机器之间进行数据的同步。2、rsync是为了在两个机器之间进行数据的同步,既然有了scp为何还要有这个协议呢?该协议主要目的是在两台机器之间进行数据同步的时候,尽量少的传递数据。rsync可以聪明的在两台机器之间进行数据的同步,并通过合适的差分编码减少数据的传输。rsync的作用就是当要同步数据的对端已经存在部分要同步数据的情况下,通过使用rsync可以只传递对端没有的数据。假设一个文件100G,在文件末尾只加了一个句号。这时候要同步数据,如果使用scp那么要拷贝传输100G数据过去,而rsync只传输修改后的数据,整个任务就结束了。参考1、linux awk命令详解 - ggjucheng - 博客园 http://www.cnblogs.com/ggjuch… 2、sed入门详解教程 - 肖邦linux - 博客园 https://www.cnblogs.com/liwei… 3、Linux 入门记录:十七、Linux 命令行文本/文件处理工具 - mingc - 博客园 https://www.cnblogs.com/mingc… 4、linux sort 命令详解 - 孙愚 - 博客园 https://www.cnblogs.com/51lin… 5、OFS-输出字段分隔符 | awk https://lvs071103.gitbooks.io… 6、Awk关系运算符和布尔运算符 - 自由的代价永远是警惕 - CSDN博客 https://blog.csdn.net/liu4546… 7、linux awk 内置函数详细介绍(实例) - 程默 - 博客园https://www.cnblogs.com/cheng…8、rsync 使用示例 | 《Linux就该这么学》https://www.linuxprobe.com/rs…9、Linux之rsync数据同步服务 - 潇潇、寒 - 博客园http://www.cnblogs.com/caicai…10、【Linux】Linux下同步数据scp与rsync - zwan0518的专栏 - CSDN博客https://blog.csdn.net/zwan051… ...

March 17, 2019 · 6 min · jiezi

《Shell编程从入门到精通》张昊-chap1-2

缘起20190314开始复习及学习吧;张昊编著;内容目录 i(9/314)全书11章chap1 第1个Shell程序 1(13/334)1.1、第一道菜[echo.sh]#!/bin/shcd /tmpecho “hello world!"(1)每一行代码是啥意思;(2)如何运行程序?1.2、如何运行程序1.2.1 选婿:位于第一行的#!匹配解释器。解释器路径可以用whereis bash查。自删除脚本#!/bin/rm# 运行这个脚本时,什么也不会发生,只是删自已WHATEVER=65echo “This line will never print!“exit $WHATEVER #脚本不会在这退出1.2.2 找茬:程序执行的差异source echo.sh时,改变了目录。1.2.3 shell的命令种类(1)内建命令(2)shell函数(3)外部命令要知道source执行的差异,不会创建子进程,直接在父进程中执行。1.3、Linux Shell的变量1.3.1、变量测试全局变量和局部变量的适用范围#!/bin/shnum=123func1(){ num=321 echo $num}Func2(){ local num=456 #局部变量 echo $num}echo $numfunc1echo $numfunc2echo $num1.3.2、用echo输出变量echo 掌握各种转义字符1.3.3、环境变量的相关操作export命令bash的启动文件/登出文件/etc/profile/etc/bashrc$HOME/.bash_profile$HOME/.bashrc$HOME/.bash_logoutunset命令env命令1.3.4、shell中的一些常用环境变量1.4、Linux Shell是解释型语言1.4.1、编译型语言和解释型语言1.4.2、Linux Shell编程的优势1.5、小结chap2 shell编程基础 15(27/334)2.1、向脚本传递参数2.1.1、Shell脚本的参数Shell编程中的函数testfunc(){ echo “$# parameters”; echo “$@”;}shell编程中参数引用0,1,2…*@#$!?-在2.1.2、参数的用途[ps.sh]#! /bin/shps -eLf | grep $1mv和mkdir命令2.2、I/O重定向2.2.1、标准输入、标准输出与标准错误cat命令后面的主要参数2.2.2、管道与重定向>、<、>>、|head命令2.2.3、文件描述符2.2.4、特殊文件的妙用/dev/null/dev/zero/dev/ttyread命令,我基本没在命令中用过,最多编程中有用2.3、基本文本检索grep相关命令2.4、Unix/Linux系统的设计与shell编程2.4.1、一切皆文件Linux文件的后缀名Linux文件类型ls命令2.4.2、UNIX编程的基本原则2.5、小结chap3 编程的基本元素 39(51/334)3.1、再识变量收获履历20190314过了一遍chap1和2,还是有一些知识点不知道的。shell中的参数引用就不熟。

March 14, 2019 · 1 min · jiezi

pm2 命令使用

1、启动时指定日志存放的文件位置pm2 start app.js -o ./logs/out.log -e ./logs/error.log2、使用配置文件的方式{ “script” : “app.js”, “error_file” : “./logs/err.log”, “out_file” : “./logs/out.log”, “merge_logs” : true, “log_date_format” : “YYYY-MM-DD HH:mm Z”}然后启动配置文件pm2 start config.json注意: 如果你已经启动过了项目,那么再次启动项目时指定的日志路径是不生效的,需要先在pm2实例列表中把该项目清除掉,按照id单个清除如:pm2 delete [id] 或者全部清除掉 pm2 delete allpm2 常用命令开启关闭pm2 start server.js //启动server.js进程pm2 start server.js -i 4 //启动4个server.js进程pm2 restart server.js //重启server.js进程pm2 restart all // 重启所有应用pm2 stop all // 停止所有进程pm2 stop server.js //停止server.js进程pm2 stop 0 //停止编号为0的进程查看pm2 list //查看当前正在运行的进程pm2 show [app-name] //显示当前应用程序的所有信息监控pm2 monit //监控当前所有的进程pm2 monit 0 //监控批评行编号为0的进程pm2 monit server.js //监控名称为server.js的进程日志pm2 logs //显示所有日志pm2 logs 0 //显示执行编号为0的日志pm2 logs server.js //显示名称为server.js的进程pm2 flush //清洗所有的数据[注:我没有试出来效果]其他 pm2 scale api 10 # 把名字叫api的应用扩展到10个实例 pm2 reset [app-name] # 重置重启数量 pm2 startup # 创建开机自启动命令 pm2 save # 保存当前应用列表 pm2 resurrect # 重新加载保存的应用列表 ...

March 14, 2019 · 1 min · jiezi

理解shell脚本中的2>&1

问题描述: 最近在写crontab的时候,看到一条0 10 sh /abc/f.sh > /abc/log 2>&1大致长成这样的一条定时任务。不知道最后面的2>&1是起什么作用的,然后就去学习了一下。问题分析: 1.首先我们看前面的0 10 sh /abc/f.sh > /abc/log,这个比较好理解,意思是每天的10点整去执行f.sh脚本,并且将标准输出重定向到log文件中 2.我们再来看后面的2>&1, 然后我们还得知道一个概念,文件描述符,可以参考下,wiki对文件描述符的简绍和文件描述符的原理链接。简单的来说就是有一个整数,它的枚举值是0(标准输入),1(标准输出),2(标准错误),然后0是从键盘输入,1和2都是输出到屏幕上。 3.我们了解了整数的含义之后再来看就比较好理解了,对于重定向符号">",我们可以这么理解: 文件描述符 > 文件.比如 ls > a.txt,就是说把ls的输出存入a.txt, >等于 1 >, 所以写成ls 1> a.txt也是一样的效果.如果>后面是&1就是用来表示这是文件描述符. 4.0 10 sh /abc/f.sh > /abc/log 2>&1所以这个命令就是说把执行结果的标准输出放入到log文件,又因为2>&1(标准错误也重定向到标准输入,之前标准输入已经重定向到了log),因此这个命令的正确执行和报错都会放入到log文件中。实例:我们可以看下面的两个例子:1.首先由f,log两个文件,ll f1 > log 2 >&1,意思是标准输出和错误都输出到log中,所以cat log中有错误信息。2.再看第二个,首先由f,log两个文件,ll f2 > log >&2,意思是标准输出重定向到log中,标准输出又重定向到标准错误中,因为标准错误是输出到屏幕的,所以不管ll f2是存在还是不存在都会在屏幕上显示出来。

March 14, 2019 · 1 min · jiezi

iOS新手用swift写一个macos打包工具 一键打包到指定位置

使用dmg安装macos app打包出的app运行如下图,使用磁盘压缩成dmg,直接打开package.dmg即可配置完毕后点击start运行打包脚本,生成ipa到指定目录该项目用swift开发,项目和dmg保存在https://github.com/gwh111/tes…流程解析概述整个流程就是,通过recoverAndSet()函数恢复之前保存数据,start()检查路径后会替换内部package.sh的动态路径,然后起一个线程创建Process(),通过Pipe()监控脚本执行输出,捕获异常1.recoverAndSet()通过UserDefaults简单地记住上次打包的路径,下次写了新代码后即可点击start立即打包恢复时把值传给控件func recoverAndSet() { let objs:[Any]=[projectPath,projectName,exportOptionsPath,ipaPath] let names:[NSString]=[“projectPath”,“projectName”,“exportOptionsPath”,“ipaPath”] for i in 0…3{ print(i) let key=names[i] let obj=objs[i] as! NSTextField let v=UserDefaults.standard.value(forKey: key as String) if (v == nil){ continue } obj.stringValue=(v as? String)! } let ps=UserDefaults.standard.value(forKey: “projectName” as String) if (ps==nil){ }else{ projectName.stringValue=(ps as? String)!; } let dr=UserDefaults.standard.value(forKey: “debugRelease”) if (dr==nil){ }else{ debugRelease.selectedSegment=dr as! Int; } debugRelease.action = #selector(segmentControlChanged(segmentControl:)) }2.selectPath()通过NSOpenPanel()创建打开文档面板对象,选择文件目录,而不是手动输入通常项目路径名和项目名称是一致的,这里使用了path.components(separatedBy:"/")将路径分割自动取工程名@IBAction func selectPath(_ sender: NSButton) { let tag=sender.tag print(tag) // 1. 创建打开文档面板对象 let openPanel = NSOpenPanel() // 2. 设置确认按钮文字 openPanel.prompt = “Select” // 3. 设置禁止选择文件 openPanel.canChooseFiles = true if tag==0||tag==2 { openPanel.canChooseFiles = false } // 4. 设置可以选择目录 openPanel.canChooseDirectories = true if tag==1 { openPanel.canChooseDirectories = false openPanel.allowedFileTypes=[“plist”] } // 5. 弹出面板框 openPanel.beginSheetModal(for: self.view.window!) { (result) in // 6. 选择确认按钮 if result == NSApplication.ModalResponse.OK { // 7. 获取选择的路径 let path=openPanel.urls[0].absoluteString.removingPercentEncoding! if tag==0 { self.projectPath.stringValue=path let array=path.components(separatedBy:"/") if array.count>1{ let name=array[array.count-2] print(array) print(name as Any) self.projectName.stringValue=name } }else if tag==1 { self.exportOptionsPath.stringValue=path }else{ self.ipaPath.stringValue=path } let names:[NSString]=[“projectPath”,“exportOptionsPath”,“ipaPath”] UserDefaults.standard.setValue(openPanel.url?.path, forKey: names[tag] as String) UserDefaults.standard.setValue(self.projectName.stringValue, forKey: “projectName”) UserDefaults.standard.synchronize() // self.savePath.stringValue = (openPanel.directoryURL?.path)!// // 8. 保存用户选择路径(为了可以在其他地方有权限访问这个路径,需要对用户选择的路径进行保存)// UserDefaults.standard.setValue(openPanel.url?.path, forKey: kSelectedFilePath)// UserDefaults.standard.synchronize() } // 9. 恢复按钮状态// sender.state = NSOffState } }3.start()通过str.replacingOccurrences(of: “file://”, with: “")将路径和sh里的路径替换通过DispatchQueue.global(qos: .default).async获取Concurrent Dispatch Queue并开启Process()在处理完的terminationHandler里回到主线程更新UI@IBAction func start(_ sender: Any) { guard projectPath.stringValue != "” else { self.logTextField.stringValue=“工程目录不能为空”; return } guard projectName.stringValue != "" else { self.logTextField.stringValue=“工程名不能为空”; return } guard exportOptionsPath.stringValue != "" else { self.logTextField.stringValue=“exportOptions不能为空 xcode生成ipa文件夹中包含”; return } guard ipaPath.stringValue != "" else { self.logTextField.stringValue=“输出ipa目录不能为空”; return } var str1=“abc” let str2=“abc” if str1==str2{ print(“same”) } //save let objs:[Any]=[projectPath,exportOptionsPath,ipaPath] let names:[NSString]=[“projectPath”,“exportOptionsPath”,“ipaPath”] for i in 0…2{ let obj=objs[i] as! NSTextField UserDefaults.standard.setValue(obj.stringValue, forKey: names[i] as String) } UserDefaults.standard.setValue(self.projectName.stringValue, forKey: “projectName”) UserDefaults.standard.setValue(self.debugRelease.selectedSegment, forKey: “debugRelease”) UserDefaults.standard.synchronize() // self.showInfoTextView.string=“abc”; if isLoadingRepo { self.logTextField.stringValue=“正在执行上一个任务”; return }// 如果正在执行,则返回 isLoadingRepo = true // 设置正在执行标记 let projectStr=self.projectPath.stringValue let nameStr=self.projectName.stringValue let plistStr=self.exportOptionsPath.stringValue let ipaStr=self.ipaPath.stringValue let returnData = Bundle.main.path(forResource: “package”, ofType: “sh”) let data = NSData.init(contentsOfFile: returnData!) var str = NSString(data:data! as Data, encoding: String.Encoding.utf8.rawValue)! as String if debugRelease.selectedSegment==0 { str = str.replacingOccurrences(of: “DEBUG_RELEASE”, with: “debug”) }else{ str = str.replacingOccurrences(of: “DEBUG_RELEASE”, with: “release”) } str = str.replacingOccurrences(of: “NAME_PROJECT”, with: nameStr) str = str.replacingOccurrences(of: “PATH_PROJECT”, with: projectStr) str = str.replacingOccurrences(of: “PATH_PLIST”, with: plistStr) str = str.replacingOccurrences(of: “PATH_IPA”, with: ipaStr) str = str.replacingOccurrences(of: “file://”, with: “”) print(“返回的数据:(str)”); self.logTextField.stringValue=“执行中。。。”; DispatchQueue.global(qos: .default).async { // str=“aaaabc”// str = str.replacingOccurrences(of: “ab”, with: “dd”) // print(self.projectPath.stringValue)// print(self.exportOptionsPath.stringValue)// print(self.ipaPath.stringValue) let task = Process() // 创建NSTask对象 // 设置task task.launchPath = “/bin/bash” // 执行路径(这里是需要执行命令的绝对路径) // 设置执行的具体命令 task.arguments = ["-c",str] task.terminationHandler = { proce in // 执行结束的闭包(回调) self.isLoadingRepo = false // 恢复执行标记 //5. 在主线程处理UI DispatchQueue.main.async(execute: { self.logTextField.stringValue=“执行完毕”; }) } self.captureStandardOutputAndRouteToTextView(task) task.launch() // 开启执行 task.waitUntilExit() // 阻塞直到执行完毕 } }4.captureStandardOutputAndRouteToTextView()对执行脚本的日志监控为了看到脚本报错或执行成功提示,使用Pipe()监控 NSPipe一般是两个线程之间进行通信使用的在osx 系统中 ,沙盒有个规则:在App运行期间通过NSOpenPanel用户手动打开的任意位置的文件,把这个这个路径保存下来,后面都是可以直接用这个路径继续访问文件,但当App退出后再次运行,这个路径默认是不可以访问的fileprivate func captureStandardOutputAndRouteToTextView(_ task:Process) { //1. 设置标准输出管道 outputPipe = Pipe() task.standardOutput = outputPipe //2. 在后台线程等待数据和通知 outputPipe.fileHandleForReading.waitForDataInBackgroundAndNotify() //3. 接受到通知消息 observe=NotificationCenter.default.addObserver(forName: NSNotification.Name.NSFileHandleDataAvailable, object: outputPipe.fileHandleForReading , queue: nil) { notification in //4. 获取管道数据 转为字符串 let output = self.outputPipe.fileHandleForReading.availableData let outputString = String(data: output, encoding: String.Encoding.utf8) ?? "" if outputString != “”{ //5. 在主线程处理UI DispatchQueue.main.async { if self.isLoadingRepo == false { let previousOutput = self.showInfoTextView.string let nextOutput = previousOutput + “\n” + outputString self.showInfoTextView.string = nextOutput // 滚动到可视位置 let range = NSRange(location:nextOutput.utf8CString.count,length:0) self.showInfoTextView.scrollRangeToVisible(range) if self.observe==nil { return } NotificationCenter.default.removeObserver(self.observe!) return }else{ let previousOutput = self.showInfoTextView.string var nextOutput = previousOutput + “\n” + outputString as String if nextOutput.count>5000 { nextOutput=String(nextOutput.suffix(1000)); } // 滚动到可视位置 let range = NSRange(location:nextOutput.utf8CString.count,length:0) self.showInfoTextView.scrollRangeToVisible(range) self.showInfoTextView.string = nextOutput } } } if self.isLoadingRepo == false { return } //6. 继续等待新数据和通知 self.outputPipe.fileHandleForReading.waitForDataInBackgroundAndNotify() } }-exportOptions.Plist 常用文件内容格式compileBitcodeFor non-App Store exports, should Xcode re-compile the app from bitcode? Defaults to YESembedOnDemandResourcesAssetPacksInBundleFor non-App Store exports, if the app uses On Demand Resources and this is YES, asset packs are embedded in the app bundle so that the app can be tested without a server to host asset packs. Defaults to YES unless onDemandResourcesAssetPacksBaseURL is specifiedmethodDescribes how Xcode should export the archive. Available options: app-store, ad-hoc, package, enterprise, development, and developer-id. The list of options varies based on the type of archive. Defaults to developmentteamIDThe Developer Portal team to use for this export. Defaults to the team used to build the archivethinningFor non-App Store exports, should Xcode thin the package for one or more device variants? Available options: <none> (Xcode produces a non-thinned universal app), <thin-for-all-variants> (Xcode produces a universal app and all available thinned variants), or a model identifier for a specific device (e.g. “iPhone7,1”). Defaults to <none>uploadBitcodeFor App Store exports, should the package include bitcode? Defaults to YESuploadSymbolsFor App Store exports, should the package include symbols? Defaults to YES ...

March 11, 2019 · 4 min · jiezi

亦大亦小如你--MySQL

写在前面MySQL 是个神奇的关系型数据库,真心感觉牛逼,因为做的项目比较杂,之前也碰到过 Oracle 数据库,给我的印象是 Oracle 很臃肿繁琐,配置多,如果是小项目用它的话感觉就像是杀鸡用牛刀,大材小用。但是也不是说Mysql不能用于大项目,MySQL 开元免费,是现在关系型数据库的主流产品,网上相应的文档和问题解决方案也会很多,意思就是比如菜鸟如我碰到了Mysql出的问题,网上基本上很全。系统环境 Debain 7Mysql 5.61.MySQL 简单操作命令//1.登录mysql,括号中的为可选项,$(包括$)后面为实际数据,-D是指定数据库登录mysql (-h$host) (-P$port) -u$user -p$pwd (-D$dbname) //地址 端口 账号 密码 数据库名//2.删除、创建数据库drop database dbname; //删除数据库create database dbname charset utf8 //创建数据库//3.删除、创建数据库表drop table tablename;//删除表create table tablename(id int, name varchar(80)); //创建表//4.表操作show triggers\g / show triggers; //查看触发器show variables like ‘character_set_database’; //查看库编码desc tablename; //查看表结构select current_date(); //查看表创建时间//5.导入sql文件use dbname;source /dbname.sql; //执行 sql 文件//6.当前的连接情况select current_user(); //查看当前登录账号show processlist; //查看当前进程show full processlist;//查看当前全部进程select user,host,Super_priv from mysql.user; //查看所有可连接用户、地址和权限信息(Super_priv 用户有super权限才可以导入数据)grant all privileges on . to root@’%’ identified by ‘root’ with grant option;flush privileges; //给root用户远程登录的所有权限2.自动导入sql文件2.1 shell操作#创建 createDb.sh,内容如下:#!/bin/bash#通过 shell 自动初始化数据库和表结构host=$1 #地址port=$2 #端口user=$3 #账号pwd=$4 #密码dbname=$5 #数据库名path=$6 #sql 文件路径mysql -h$host -P$port -u$user -p$pwd <<EOFdrop database if exists $dbname;create database $dbname charset utf8;use $dbname;source $pathCOMMIT;EOF#查看 shell 的执行过程命令sh -x ./shell //查看 shell 执行过程2.2 expect操作#!/usr/bin/expect -fset timeout 10set host [lindex $argv 0]set port [lindex $argv 1]set user [lindex $argv 2]set pwd [lindex $argv 3]set dbname [lindex $argv 4]set path [lindex $argv 5]set cset [lindex $argv 6] #字符编码spawn mysql -h$host -P$port -u$user -pexpect “Enter password: “send “$pwd\r"expect “mysql> “send “drop database if exists $dbname;create database $dbname charset $cset;\r"expect “mysql> “send “use $dbname;\r"expect “mysql> “send “source $path;\r"expect “mysql> “send “exit\r"interact回头研究再更新操作,有哪里写的不对的也请不吝赐教 ...

March 9, 2019 · 1 min · jiezi

linux系统下用crontab定时清除日志文件

在应用服务器上,我们常常需要定时清理文件,尤其是日志,这时候就需要用到Linux自带的用于例行性工作调度的at和crontab两个命令了。其中at是仅执行一次的命令,这次先不谈,而crontab是循环执行的,符合定时清理文件的需求。cron这个系统服务是默认启动的,当用户用crontab这个命令新建工作调度后,该项工作就会被记录到/var/spool/cron/里面去了,而且是以账号作为判别,比如kindy使用crontab后,他的工作被记录到/var/spool/cron/kindy里面去。这个文件不能直接用vi编辑,而需要借助命令crontab.crontab [-u username] [-l|-e|-r]-u 只有root可以执行这个任务,不常用-e 编辑crontab工作内容,常用!-l 查阅crontab工作内容,常用!默认情况下,任何用户只要不被列入/etc/cron.deny中,那么他就可以执行crontab -e去编辑自己的定时任务了如果要定时清除的日志是已知的固定路径下的文件,比如//logs,我们规定每天23:59定时去清除最后修改时间在7天以前的文件,那么首先编辑任务:crontab -e进入后会看到每项工作的格式是怎么定义的,然后按需求直接写任务:23 59 * * * root find //logs -name ‘catalina.out*.log’ -and -mtime +7 -type f |xargs rm再退出,以后就会定时执行啦~

March 9, 2019 · 1 min · jiezi

shell通过通道批量插入大量数据到redis

前言:当我们需要短时间内快速插入大量数据到redis中,就需要一个行之有效的方式。 实践发现redis的(Redis Mass Insertion – Redis)是个比较高效的方法。借助于pipline插入首先通过简单的脚本生成例如如下的:SET Key0 Value0SET Key1 Value1…SET KeyN ValueN的目标文本文件。转码redis-cli中只支持dos格式的换行符 rn ,如果在Linux下、Mac下或者Windows下创建的文件,最好都转个码。没有转码的文件,执行会失败。 转换的方法有好多种:a. 利用todos进行转化。如未进行安装可参考这里。todos set_ins_redis.txtb. 利用vim格式化$ vim set_ins_redis.txt:set fileformat=dos:wq最后执行如下命令:cat set_ins_redis.txt | redis-cli -p 8244 –pipe执行成功后会出现如下信息:All data transferred. Waiting for the last reply…Last reply received from server.errors: 0, replies: 10654294Done

March 6, 2019 · 1 min · jiezi

认识 Here Document

基础HereDoc 全名叫做 Here Document,中文可以称之为 嵌入文档。对它的叫法实际上很多,here文档,hereis,here-string 等等都是它。嵌入文档是 Shell I/O 重定向功能的一种替代。我们已经知道 Shell 的 I/O 重定向是一种文件句柄的传输。例如:COMMAND 1>/tmp/1.lst 2>&1将命令的标准输出为一个文件,同时也将错误输出到同一个文件中。而下例:cat /etc/passwd | grep ‘^admin:‘则通过管道将前一命令的输出当做后一命令的标准输入。基本语法Here Document 就是标准输入的一种替代品。它使得脚本开发人员可以不必使用临时文件来构建输入信息,而是直接就地生产出一个文件并用作命令的标准输入。一般来说其格式是这样的:COMMAND <<IDENT…IDENT在这里,<< 是引导标记,IDENT 是一个限定符,由开发人员自行选定,两个 IDENT 限定符之间的内容将被当做是一个文件并用作 COMMAND 的标准输入。例如echo大段文本时,我们可以使用 cat file 的语法:cat <<EOFSOME TEXTHERE!EOF此例中,我们使用 EOF 短语作为限定符。Here Document 是可以嵌套的,只要双层分别使用不同的 IDENT 限定符且保证正确的嵌套关系即可:ssh user@host <<EOTls -la –colorcat <<EOFfrom a remote hostEOF[ -f /tmp/1.tmp ] && rm -f /tmp/1.tmpEOT看起来有点怪?其实还好啦。实际上,限定符可以取得非常长,只要是字母开头且只包含字母和数字(通常,下划线和短横线也是有效的,不过根据 bash 的版本不同、宿主实现的不同,可能会有一定的出入)即可。abs 中有一个例子,节选如下:wall <<zzz23EndOfMessagezzz23fdjsldjfdsjlfdsjfdlszzz23EndOfMessagezzz23这是正确有效的,不过这个其实更怪一些。Here String在 bash, ksh 和 zsh 中,还可以使用 Here String:$ tr a-z A-Z <<<“Yes it is a string"YES IT IS A STRING此时也可以使用变量:$ tr a-z A-Z <<<"$var"不常见的用法同时重定向标准输出有没有可能将HEREDOC存储为一个文件?显然是可以:cat << EOF > /tmp/yourfilehereThese contents will be written to the file. This line is indented.EOF你可以注意到这种写法不同于经常性的写法:cat >/tmp/1<<EOFsEOF但两者都是对的。root但当需要 root 权限时,’>’ 并不能很好地工作,此时需要 sudo tee 上场:cat <<EOF | sudo tee /opt/1.logsEOF子shell标准输出的重定向,还可以通过子 shell 的方式来构造:(echo ‘# BEGIN OF FILE | FROM’ cat <<- EOF LogFile /var/log/clamd.log LogTime yes DatabaseDirectory /var/lib/clamav LocalSocket /tmp/clamd.socket TCPAddr 127.0.0.1 SelfCheck 1020 ScanPDF yes EOF echo ‘# END OF FILE’) > /etc/clamd.conf这个例子只是一个示意,因为实际上该例子用不着那么麻烦,单个 cat HEREDOC 足够达到目的了,也不需要开子 shell 那么重。cat <<EOF 的少见的变形let() { res=$(cat)}let <<‘EOF’…EOF元芳,你怎么看?还可以写作这样:let() { eval “$1”’=$(cat)’}let res<<‘EOF’…EOF当然,其实它和单行指令是等效的:{ res=$(cat); } <<‘EOF’…EOF{} 是语句块,而不是子shell,因而更省力。根据具体情况来使用它,有时候你希望子 shell 的变量无污染的效果,或者别的期待,那你就使用 ()。在参数展开语法中使用 HEREDOCvariable=$(cat <<SETVARThis variableruns over multiple lines.SETVAR)echo “$variable"示例展示了在 $() 语法中可以随意地嵌入 HEREDOC。如果你只是需要为变量用 HEREDOC 赋值,read var 通常是更好的主意:read i <<!Hi!echo $i # Hi对函数使用 HEREDOCGetPersonalData () { read firstname read lastname read address read city read state read zipcode} # This certainly appears to be an interactive function, but . . .# Supply input to the above function.GetPersonalData <<RECORD001BozoBozeman2726 Nondescript Dr.BozemanMT21226RECORD001echoecho “$firstname $lastname"echo “$address"echo “$city, $state $zipcode"echo可以看到,只要函数能够接收标准输入,那就可以将 HEREDOC 套用上去。匿名的 HEREDOC#!/bin/bash# filename: aa.sh: <<TESTVARIABLES${UX?}, ${HOSTNAME?} | ${USER?} | ${MAIL?} # Print error message if one of the variables not set.TESTVARIABLESexit $?这个示例中,如果变量没有被设置,则会产生一条错误消息,而该 HEREDOC 的用处实际上是用来展开要确认的变量,HEREDOC产生的结果作为 : 的标准输入,实际上被忽略了,最后只有 HEREDOC 展开的状态码被返回,用以确认是不是有某个变量尚未被设置:$ ./aa; echo $?./aa: line 3: UX: parameter null or not set1由于 UX 变量缺失,因此调用的结果是一行错误输出,以及调用的退出码为 1,也就是 false 的意思。: 是 true 命令的同义词。就好像 . 是 source 命令的同义词一样。进一步除了用来一次性检测一大批变量有否被赋值的效果之外,匿名的 HEREDOC 也常常被用作大段的注释。cat >/dev/null<<COMMENT…COMMENT: <<COMMENT…COMMENT这些写法都可以,看你的个人喜好。Bash 程序员的一般风格是能省键盘就省键盘。但有时候他们也喜欢能炫就炫::<<-! ____ _ ____ _ / | ___ ___ __| | / | ___ ___ __| || | _ / _ \ / _ \ / | | | _ / _ \ / _ \ / _ || || | () | () | (| | | || | () | () | (| | _|_/ _/ _,| _|_/ __/ _,| ____ _ _/ __|| | _ _ | | _ | __| | | |/ _| | | | ___) | |_| |_| | (_| | |_| ||____/ \__|\__,_|\__,_|\__, | |___/!while read当我们需要读一个csv文件时,我们会用到 while read 结构。将 csv 文件改为 HEREDOC:while read pass port user ip files directs; do sshpass -p$pass scp -o 'StrictHostKeyChecking no' -P $port $files $user@$ip:$directsdone &lt;&lt;____HERE PASS PORT USER IP FILES DIRECTS . . . . . . . . . . . . . . . . . . PASS PORT USER IP FILES DIRECTS____HERE由于不同格式的 CSV 的处理并非本文的主题,因此这里不再展开讨论具体情况了。补充:循环的重定向对于 while … done 来说,标准输入的重定向应该写在 done 之后。同样的,for … do … done 也是如此,until … done 也是如此。whilewhile [ "$name" != Smith ] # Why is variable $name in quotes?do read name # Reads from $Filename, rather than stdin. echo $name let "count += 1"done &lt;"$Filename" # Redirects stdin to file $Filename. untiluntil [ "$name" = Smith ] # Change != to =.do read name # Reads from $Filename, rather than stdin. echo $namedone &lt;"$Filename" # Redirects stdin to file $Filename. forfor name inseq $line_count` # Recall that “seq” prints sequence of numbers.# while [ “$name” != Smith ] – more complicated than a “while” loop –do read name # Reads from $Filename, rather than stdin. echo $name if [ “$name” = Smith ] # Need all this extra baggage here. then break fi done <"$Filename” # Redirects stdin to file $Filename. 新的缩进和对齐语法删除 TAB 缩进字符<<-IDENT 是新的语法,市面上的 Bash 均已支持这种写法。它的特殊之处就在于 HEREDOC 正文内容中的所有前缀 TAB 字符都会被删除。这种语法往往被用在脚本的 if 分支,case 分支或者其他的代码有缩进的场所,这样 HEREDOC 的结束标记不必非要在新的一行的开始之处不可。一方面视觉效果上 HEREDOC 跟随了所在代码块的缩进层次,可读性被提升,另一方面对于许多懒惰的编辑器来说,不会发生面对 HEREDOC 时语法分析出错、代码折叠的区块判断不正确的情况。function a () { if ((DEBUG)); then cat <<-EOF French American - Uses UTF-8 Helvetica - Uses RTL EOF fi}如上的脚本段落中,结束标记EOF可以不必处于行首第一个字母,只要EOF以及其上的HEREDOC正文都以TAB字符进行缩进就可以了。注意如果TAB字符缩进在这里没有被严格遵守的话,Bash解释器可能会报出错误。像在正文中的 - Uses UTF-8 除开行首的 TAB字符缩进之外,还包含两个空格字符,这不会受到 <<- 的影响而被删除。禁止变量展开一般情况下,HEREDOC 中的 ${VAR},$(pwd),$((1+1)) 等语句会被展开,当你想要编写 ssh 指令时,可能你希望的是不要展开 $ 标记。这可以用 <<“EOF” 来实现。只需要在 IDENT 标记上加上引号包围就可以达到效果,结束标记则无需引号。cat <<“EOF"Command is: $ lookup fantasyEOF# 如果不想展开,则你需要对 $ 字符进行转义cat <<EOF $ lookup fantasyEOF这个例子中,请注意单个的 $ 字符其实是不会展开也不会报错的,所以我们只是为了编写一个示例而已。引号包围呢,单引号、双引号都可以,都会同样地生效。甚至,你可以使用转义语法,也就是说:cat <<\EOFCommand is: $ lookup fantasyEOF也能禁止参数展开。同时应用上两者上面两个新的语法特性,是可以被同时组合和运用的: cat <<-“EOF” Command is: $ lookup fantasy EOF虽然你可能根本不需要遇到这样的情形。参考Advanced Bash-Scripting Guide - Chapter 19. Here Documents维基:Here文档 ...

March 6, 2019 · 4 min · jiezi

rsync算法原理及使用

如果服务器之间需要保持某些文件的一致,我们可以使用scp来复制,如果需要长期保持一致,可以配合crontab脚本来使用。但是此时我们有更优的方式,就是rsync+crontab来实现定时增量传输保持文件一致。rsync功能很强大,网上的资料也都很全,这里做一些简单的汇总。rsync原理这一小节内容大幅度转载了 RSYNC 的核心算法 的内容,因为原文章写的太好,就不再狗尾续貂了,感兴趣的可以直接查看原文。他的翻译原文是:The rsync algorithm。rsync是linux下同步文件的一个高效算法,用于同步更新两处计算机的文件和目录,并适当利用查找文件中的不同块以减少数据传输。rsync的主要特点就是增量传输,只对变更的部分进行传送。增量同步算法假如我们现在需要同步两个文件保持一致,并且只想传送不同的部分,那么我们就需要对两边的文件做diff,但是这两个问题在两台不同的机器上,无法做diff。如果我们做diff,就要把一个文件传到另一台机器上做diff,但这样一来,我们就传了整个文件,这与我们只想传输不同部的初衷相背。于是我们就要想一个办法,让这两边的文件见不到面,但还能知道它们间有什么不同。这就是rsync的算法。rsync同步算法我们将同步源文件名称为fileSrc,同步目的文件叫fileDst。1. 分块Checksum算法首先,我们会把fileDst的文件平均切分成若干个小块,比如每块512个字节(最后一块会小于这个数),然后对每块计算两个checksum:一个叫rolling checksum,是弱checksum,32位的checksum另一个是强checksum,128位的,以前用md4,现在用md5 hash算法。为什么要这样?因为若干年前的硬件上跑md4的算法太慢了,所以,我们需要一个快算法来鉴别文件块的不同,但是弱的adler32算法碰撞概率太高了,所以我们还要引入强的checksum算法以保证两文件块是相同的。也就是说,弱的checksum是用来区别不同,而强的是用来确认相同。2. 传输算法同步目标端会把fileDst的一个checksum列表传给同步源,这个列表里包括了三个东西,rolling checksum(32bits),md5 checksume(128bits),文件块编号。同步源机器拿到了这个列表后,会对fileSrc做同样的checksum,然后和fileDst的checksum做对比,这样就知道哪些文件块改变了。但是,聪明的你一定会有以下两个疑问:如果我fileSrc这边在文件中间加了一个字符,这样后面的文件块都会位移一个字符,这样就完全和fileDst这边的不一样了,但理论上来说,我应该只需要传一个字符就好了。这个怎么解决?如果这个checksum列表特别长,而我的两边的相同的文件块可能并不是一样的顺序,那就需要查找,线性的查找起来应该特别慢吧。这个怎么解决?很好,让我们来看一下同步源端的算法。3. checksum查找算法同步源端拿到fileDst的checksum数组后,会把这个数据存到一个hash table(特殊的数据结构体,可以快速检索)中,用rolling checksum做hash,以便获得O(1)时间复杂度的查找性能。这个hash table是16bits的,所以,hash table的尺寸是2的16次方,对rolling checksum的hash会被散列到0 到 2^16 – 1中的某个整数值。4. 比对算法取fileSrc的第一个文件块(我们假设的是512个长度),也就是从fileSrc的第1个字节到第512个字节,取出来后做rolling checksum计算。计算好的值到hash表中查。如果查到了,说明发现在fileDst中有潜在相同的文件块,于是就再比较md5的checksum,因为rolling checksume太弱了,可能发生碰撞。于是还要算md5的128bits的checksum,这样一来,我们就有 2^-(32+128) = 2^-160的概率发生碰撞,这太小了可以忽略。如果rolling checksum和md5 checksum都相同,这说明在fileDst中有相同的块,我们需要记下这一块在fileDst下的文件编号。如果fileSrc的rolling checksum 没有在hash table中找到,那就不用算md5 checksum了。表示这一块中有不同的信息。总之,只要rolling checksum 或 md5 checksum 其中有一个在fileDst的checksum hash表中找不到匹配项,那么就会触发算法对fileSrc的rolling动作。于是,算法会住后step 1个字节,取fileSrc中字节2-513的文件块要做checksum,go to (1.) – 现在你明白什么叫rolling checksum了吧。这样,我们就可以找出fileSrc相邻两次匹配中的那些文本字符,这些就是我们要往同步目标端传的文件内容了。5. 传输最终在同步源这端,我们的rsync算法可能会得到这个样子的一个数据数组,图中,红色块表示在目标端已匹配上,不用传输(注:我专门在其中显示了两块chunk #5,代表数据中有复制的地方,不用传输),而白色的地方就是需要传输的内容(注意:这些白色的块是不定长的),这样,同步源这端把这个数组(白色的就是实际内容,红色的就放一个标号)压缩传到目的端,在目的端的rsync会根据这个表重新生成文件,这样,同步完成。最后想说一下,对于某些压缩文件使用rsync传输可能会传得更多,因为被压缩后的文件可能会非常的不同。对此,对于gzip和bzip2这样的命令,记得开启 “rsyncalbe” 模式。rsync的使用同样的,这一小节内容也是大幅度转载了 第2章 rsync(一):基本命令和用法 的内容,因为原文章很全面,感兴趣的可以直接查看原文。rsync是实现增量备份的工具。配合任务计划,rsync能实现定时或间隔同步,配合inotify或sersync,可以实现触发式的实时同步。它的目的是实现本地主机和远程主机上的文件同步(包括本地推到远程,远程拉到本地两种同步方式),也可以实现本地不同路径下文件的同步,但不能实现远程路径1到远程路径2之间的同步(scp可以实现)。rsync同步过程中由两部分组成:决定哪些文件需要同步的检查模式以及文件同步时的同步模式。检查模式是指按照指定规则来检查哪些文件需要被同步,例如哪些文件是明确被排除不传输的。默认情况下,rsync使用"quick check"算法快速检查源文件和目标文件的大小、mtime(修改时间)是否一致,如果不一致则需要传输。当然,也可以通过在rsync命令行中指定某些选项来改变quick check的检查模式,比如"–size-only"选项表示"quick check"将仅检查文件大小不同的文件作为待传输文件。rsync支持非常多的选项,其中检查模式的自定义性是非常有弹性的。同步模式是指在文件确定要被同步后,在同步过程发生之前要做哪些额外工作。例如上文所说的是否要先删除源主机上没有但目标主机上有的文件,是否要先备份已存在的目标文件,是否要追踪链接文件等额外操作。rsync也提供非常多的选项使得同步模式变得更具弹性。相对来说,为rsync手动指定同步模式的选项更常见一些,只有在有特殊需求时才指定检查模式,因为大多数检查模式选项都可能会影响rsync的性能。rsync四种工作方式rsync的基础语法为:rsync [OPTION…] SRC… [DEST]支持的参数高达一百多个,最常用的选项组合是"avz",即压缩和显示部分信息,并以归档模式传输。详细的可以参考 博客园-man rsync翻译(rsync命令中文手册),下面是部分参数说明:-v:显示rsync过程中详细信息。可以使用"-vvvv"获取更详细信息。-P:显示文件传输的进度信息。(实际上"-P"="–partial –progress",其中的"–progress"才是显示进度信息的)。-n –dry-run :仅测试传输,而不实际传输。常和"-vvvv"配合使用来查看rsync是如何工作的。-a –archive :归档模式,表示递归传输并保持文件属性。等同于"-rtopgDl"。-r –recursive:递归到目录中去。-t –times:保持mtime属性。强烈建议任何时候都加上"-t",否则目标文件mtime会设置为系统时间,导致下次更新 :检查出mtime不同从而导致增量传输无效。-o –owner:保持owner属性(属主)。-g –group:保持group属性(属组)。-p –perms:保持perms属性(权限,不包括特殊权限)。-D :是"–device –specials"选项的组合,即也拷贝设备文件和特殊文件。-l –links:如果文件是软链接文件,则拷贝软链接本身而非软链接所指向的对象。-z :传输时进行压缩提高效率。-R –relative:使用相对路径。意味着将命令行中指定的全路径而非路径最尾部的文件名发送给服务端,包括它们的属性。用法见下文示例。–size-only :默认算法是检查文件大小和mtime不同的文件,使用此选项将只检查文件大小。-u –update :仅在源mtime比目标已存在文件的mtime新时才拷贝。注意,该选项是接收端判断的,不会影响删除行为。-d –dirs :以不递归的方式拷贝目录本身。默认递归时,如果源为"dir1/file1",则不会拷贝dir1目录,使用该选项将拷贝dir1但不拷贝file1。–max-size :限制rsync传输的最大文件大小。可以使用单位后缀,还可以是一个小数值(例如:"–max-size=1.5m")–min-size :限制rsync传输的最小文件大小。这可以用于禁止传输小文件或那些垃圾文件。–exclude :指定排除规则来排除不需要传输的文件。–delete :以SRC为主,对DEST进行同步。多则删之,少则补之。注意"–delete"是在接收端执行的,所以它是在 :exclude/include规则生效之后才执行的。-b –backup :对目标上已存在的文件做一个备份,备份的文件名后默认使用"“做后缀。–backup-dir:指定备份文件的保存路径。不指定时默认和待备份文件保存在同一目录下。-e :指定所要使用的远程shell程序,默认为ssh。–port :连接daemon时使用的端口号,默认为873端口。–password-file:daemon模式时的密码文件,可以从中读取密码实现非交互式。注意,这不是远程shell认证的密码,而是rsync模块认证的密码。-W –whole-file:rsync将不再使用增量传输,而是全量传输。在网络带宽高于磁盘带宽时,该选项比增量传输更高效。–existing :要求只更新目标端已存在的文件,目标端还不存在的文件不传输。注意,使用相对路径时如果上层目录不存在也不会传输。–ignore-existing:要求只更新目标端不存在的文件。和”–existing"结合使用有特殊功能,见下文示例。–remove-source-files:要求删除源端已经成功传输的文件。1. 本地文件系统上实现同步rsync [OPTION…] SRC… [DEST]2. 本地主机使用远程shell和远程主机通信 Pull: rsync [OPTION…] [USER@]HOST:SRC… [DEST] Push: rsync [OPTION…] SRC… [USER@]HOST:DEST3. 本地主机通过网络套接字连接远程主机上的rsync daemon Pull: rsync [OPTION…] [USER@]HOST::SRC… [DEST] rsync [OPTION…] rsync://[USER@]HOST[:PORT]/SRC… [DEST] Push: rsync [OPTION…] SRC… [USER@]HOST::DEST rsync [OPTION…] SRC… rsync://[USER@]HOST[:PORT]/DEST前两者的本质是通过管道通信,即使是远程shell。而方式(3)则是让远程主机上运行rsync服务,使其监听在一个端口上,等待客户端的连接。路径的格式可以是本地路径,也可以是使用user@host:path或user@host::path的远程路径,如果主机和path路径之间使用单个冒号隔开,表示使用的是远程shell通信方式,而使用双冒号隔开的则表示的是连接rsync daemon。另外,连接rsync daemon时,还提供了URL格式的路径表述方式rsync://user@host/path。4. 远程shell临时启动一个rsync daemonrsync [options] –rsh=ssh auth_user@host::modulersync [options] –rsh=“ssh -l ssh_user” auth_user@host::modulersync [options] -e “ssh -l ssh_user” auth_user@host::modulersync [options] -e “ssh -l ssh_user” rsync://auth_user@host/module这不同于方式(3),它不要求远程主机上事先启动rsync服务,而是临时派生出rsync daemon,它是单用途的一次性daemon,仅用于临时读取daemon的配置文件,当此次rsync同步完成,远程shell启动的rsync daemon进程也会自动消逝。此通信方式的命令行语法格式同"Access via rsync daemon",但要求options部分必须明确指定"–rsh"选项或其短选项"-e"。一些用法示例# 将/etc/fstab拷贝到/tmp目录下rsync /etc/fstab /tmp# 将/etc/cron.d目录拷贝到/tmp下rsync -r /etc/cron.d /tmp# 将/etc/cron.d目录拷贝到/tmp下,但要求在/tmp下也生成etc子目rsync -R -r /etc/cron.d /tmp# 拷贝源路径较长,但只保留一部分目录结构,使用一个点代表相对路径的起始位置rsync -R -r /var/./log/anaconda /tmp# 对远程目录下已存在文件做备份,备份后缀为"",使用"–suffix"指定后缀rsync -R -r –backup /var/./log/anaconda /tmp# 指定备份文件保存路径,默认将不会加备份后缀,使用"–suffix"显式指定后缀rsync -R -r –backup –backup-dir=/tmp/log_back /var/./log/anaconda /tmp# .指定ssh连接参数,如端口、连接的用户、ssh选项等rsync -e “ssh -p 22 -o StrictHostKeyChecking=no” /etc/fstab 172.16.10.5:/tmp# 使用"–existing"选项使得只更新目标端已存在的文件rsync -r -v –existing /tmp/a/ /tmp/b # “–ignore-existing"更新目标端不存在的文件rsync -r -v –ignore-existing /tmp/a/ /tmp/b# “–remove-source-files"删除源端文件rsync -r -v –remove-source-files /tmp/a/anaconda /tmp/a/audit /tmp# 使用”–exclude"选项指定排除规则,排除那些不需要传输的文件。rsync -r -v –exclude=“anaconda/*.log” /var/log/anaconda /var/log/audit /tmp如果仅有一个SRC或DEST参数,则将以类似于"ls -l"的方式列出源文件列表(只有一个路径参数,总会认为是源文件),而不是复制文件。源路径如果是一个目录的话,带上尾随斜线和不带尾随斜线是不一样的,不带尾随斜线表示的是整个目录包括目录本身,带上尾随斜线表示的是目录中的文件,不包括目录本身。# 在/tmp目录下创建etc目录[root@xuexi ~]# rsync -a /etc /tmp# 不会在/tmp目录下创建etc目录,源路径/etc/中的所有文件都直接放在/tmp目录下[root@xuexi ~]# rsync -a /etc/ /tmp参考资料酷壳-RSYNC 的核心算法:https://coolshell.cn/articles…The rsync algorithm:https://rsync.samba.org/tech_…博客园-rsync(一):基本命令和用法:http://www.cnblogs.com/f-ck-n…博客园-man rsync翻译(rsync命令中文手册): http://www.cnblogs.com/f-ck-n… ...

March 5, 2019 · 2 min · jiezi

linux下新文件权限设置之umask的理解

起源是一道题1:如果你的umask设置为022,缺省的你创建的文件权限为?这让我回忆起被问过的另外一道题2: 777表示什么权限?用户组说明-rwxrw-r‐-1 root root 1213 Feb 2 09:39 abc第一个字符代表文件(-)、目录(d),链接(l)其余字符每3个一组(rwx),读(r)、写(w)、执行(x)第一组rwx:文件所有者的权限是读、写和执行第二组rw-:与文件所有者同一组的用户的权限是读、写但不能执行第三组r–:不与文件所有者同组的其他用户的权限是读不能写和执行也可用数字表示为:r=4,w=2,x=1 ,因为rwx代表三位二进制的话,正好计算出这几个数字。数字权限说明那么回到前面题2,777是三位八进制数,对应111111111,则代表三个组都可读可写可执行,我们可以这么用:chmod 755 abc //chmod 改变文件abc的权限为文件所有者可读可写可执行,同组和其他组用户是可读可执行umask说明umask是权限掩码,代表默认不要的权限,它是基于文件最大默认值666,文件夹777的基础上取计算该用户新建对象的默认权限的。比如,题1,那么创建文件的默认权限就是666-022=644,也就是-rw-r–r–

March 1, 2019 · 1 min · jiezi

Linux快速复制或删除大量小文件

前言公司需要输送给网安部一批数据集,共计1550w张图片,大约3,5T。 处理过程中同时参考网上的一些方法的实践总结。1:快速大量小文件复制a. 本机不同磁盘之间复制:复制目录$ tar cvf – /home/src_dir | tar xvf – -C /opt复制文件$ tar cf – access.log |tar xf – -C /optTips:快速tar打包的一些小技巧以及常用的tar使用tar 快速打包(仅打包不压缩传输比较快)step1:从文件中生成文件列表$ find . -name ‘.jpg’ -print > jpg.txt*** 匹配多个后缀格式$ find . -regex ‘..png|..jpeg|.*.jpg’ -print >jpg.txt若生成的文件列表比较大,可以借助split拆分成小文件进行并行打包. 若文件数较小,可忽略此步骤。#将 文件 jpg.txt 分成若干个小文件,每个文件500000行(-l 500000),文件前缀为xiu_ ,系数不是字母而是 数字(-d),后缀系数为四位数(-a 4)$ split -l 500000 ../ jpg.txt -d -a 4 xiu_step2: tar怎么从文件中读取文件列表呢?查了很久,用 -T-T, –files-from F get names to extract or create from file Ftips: 注意这里不要加 -v参数啦,对于大量文件,控制台输出是很浪费时间的…$ tar -czf jpg.tar.gz -T yourfile **** 不压缩的话,可以直接打包, 比较快。$ tar -cf jpg.tar.gz -T yourfile tar 常用的一些命令解包 $ tar xvf FileName.tar ## 或者 不输出文件,比较快 $ tar xf FileName.tar 不解包查看打包内容$ tar tvf FileName.tar 不解包统计打包内容文件数以及文件夹### 统计文件数$ tar tvf FileName.tar |grep “^-"|wc -l### 统计文件加$ tar tvf FileName.tar |grep “^d”|wc -lb. 跨网络不同主机之间复制 tar+nc:思路:在网络环境中传输时,打包再结合nc命令,通过管道和tcp端口进行传输。 比如 A往B主机传输数据i. 在机器B上,用nc来监听一个端口,任意都行,只要不被占用;并且将收到的数据用tar展开。-l代表监听模式。 $ nc -l 34183 |tar -C /data1datasets/norm/ -zxf -ii. 接着,在A上通过nc和 tar发送data_01目录。使用一致的34183的端口。 $ tar -zcvf - data_01 |nc 192.168.0.1 341832:快速删除大量小文件 或者 大文件2.1: 快速删除大量小文件rsync提供了一些跟删除相关的参数 rsync –help | grep delete –del an alias for –delete-during –delete delete files that don’t exist on the sending side –delete-before receiver deletes before transfer (default) –delete-during receiver deletes during transfer, not before –delete-after receiver deletes after transfer, not before –delete-excluded also delete excluded files on the receiving side –ignore-errors delete even if there are I/O errors –max-delete=NUM don’t delete more than NUM files 其中–delete-before 接收者在传输之前进行删除操作 可以用来清空目录或文件,如下: 1. 建立一个空目录 mkdir -p /del_blank 2. 确立需要清空的目标目录 /del_data 3. 使用rsync同步删除(注意目录后面的“/”),整体效率会快一个数量级的样子。 rsync –delete-before -a -H -v –progress –stats /del_blank /del_data 选项说明: –delete-before 接收者在传输之前进行删除操作 –progress 在传输时显示传输过程 -a 归档模式,表示以递归方式传输文件,并保持所有文件属性 -H 保持硬连接的文件 -v 详细输出模式 -stats 给出某些文件的传输状态 一般我们不需要显示进度,使用以下命令即可 rsync –delete-before -a -H /del_blank /del_data 这样我们要删除的 del_data目录就会被清空了2.2: 快速删除大文件如何删除特别大的文件(数量级),比如nohup.out这样的实时更新的文件,动辄都是几十个G上百G的,也可 以用rsync来清空大文件,而且效率比较高 。1、创建空文件 touch /data/blank.txt 2、用rsync清空文件 rsync -a –delete-before –progress –stats /data/blank.txt ./nohup.out building file list … 1 file to consider blank.txt 0 100% 0.00kB/s 0:00:00 (xfer#1, to-check=0/1) Number of files: 1 Number of files transferred: 1 Total file size: 0 bytes Total transferred file size: 0 bytes Literal data: 0 bytes Matched data: 0 bytes File list size: 27 File list generation time: 0.006 seconds File list transfer time: 0.000 seconds Total bytes sent: 73 Total bytes received: 31 sent 73 bytes received 31 bytes 208.00 bytes/sec total size is 0 speedup is 0.00 ** tips: 当SRC和DEST文件性质不一致时将会报错 当SRC和DEST性质都为文件【f】时,意思是清空文件内容而不是删除文件 当SRC和DEST性质都为目录【d】时,意思是删除该目录下的所有文件,使其变为空目录 最重要的是,它的处理速度相当快,处理几个G的文件也就是秒级的事 最核心的内容是:rsync实际上用的就是替换原理参考链接:https://blog.csdn.net/xiaoyi2…https://www.cnblogs.com/tryin...http://www.voidcn.com/article… ...

February 27, 2019 · 2 min · jiezi

webstorm 不能热更新

1.打开webstorm设置选项 —- file –> settings2.搜索或找到 System Setting3.取消勾选如下选项

February 25, 2019 · 1 min · jiezi

Mac/Linux 安装中文版 man 帮助命令

一份攻城狮笔记有哪些鲜为人知,但是很有意思的网站?每天搜集 Github 上优秀的项目一些有趣的民间故事超好用的谷歌浏览器、Sublime Text、Phpstorm、油猴插件合集linux 环境安装macOS 环境安装编译工具安装因为需要编译安装,所以你电脑上需要有编译工具,运行下面两个命令安装$ brew install automake$ brew install opencc安装# 进入下载目录$ cd ~/Downloads/# 下载最新版本的源码包$ wget https://github.com/man-pages-zh/manpages-zh/archive/v1.6.3.3.tar.gz# 解压源码包(atool命令,推荐安装这个工具,统一所有压缩文档的命令)$ atool -x v1.6.3.3.tar.gz# 或者使用这个命令解压$ tar zxvf v1.6.3.3.tar.gz# 进入源码包文件夹$ cd manpages-zh-1.6.3.2/# 编译安装 1$ autoreconf –install –force# 编译安装 2$ ./configure# 编译安装 3$ sudo make# 编译安装 4$ sudo make install# 配置别名(如果是 zsh 对应处理即可)$ echo “alias cman=‘man -M /usr/local/share/man/zh_CN’” >> ~/.bash_profile# 使别名生效$ source ~/.bash_profile# 我们就安装上了中文版本的 man 工具了,但是运行命令会发现乱码。cman ls安装 groff 新版本解决中文乱码的问题# 进入下载目录$ cd ~/Downloads/# 下载1.22版本的源码包$ wget http://git.savannah.gnu.org/cgit/groff.git/snapshot/groff-1.22.tar.gz# 解压$ atool -x groff-1.22.tar.gz# 进入目录$ cd groff-1.22# 编译安装$ ./configure$ sudo make$ sudo make install# 打开配置文件$ sudo vim /etc/man.conf# 进入编辑器之后,在文件末尾添加NROFF preconv -e UTF8 | /usr/local/bin/nroff -Tutf8 -mandoc -c# 保存退出# 运行命令,完美解决乱码问题cman ls ...

February 21, 2019 · 1 min · jiezi

Terraform入门 - 4. destroy 基础设施

我们已经看到如何构建和变更基础设施。在创建多种资源并展示资源依赖关系前,我们先复习下如何完全销毁Terraform管理的基础设施。在生产环境销毁基础设施是罕见的时间。但是如果你使用Terraform启动多个环境,比如:开发,测试,QA环境等,那么销毁是个非常有用的操作。销毁与terraform apply命令类似,资源可以用terraform destroy命令销毁掉,但是它的行为就好像所有的资源都从配置中删除了一样。$ terraform destroy# …- aws_instance.example-前缀说明该实例将被销毁。像apply一样,Terraform输出其执行计划并在执行变更前等待确认。输入yes以执行该计划并销毁基础设施:# …aws_instance.example: Destroying…Apply complete! Resources: 0 added, 0 changed, 1 destroyed.# …像apply一样,Terraform决定了必须被销毁的资源的顺序。当前示例中只有一个实例,所以不需要顺序。在更多有更多资源的复杂场景中,Terraform将按合适的顺序销毁它们以遵循以来关系,我们将在本指南最后看到这一点。下一步<!– more –><!–//硬啃官方文档产物,若有不妥之处,欢迎指正,请以官方文档为准!//–>

February 16, 2019 · 1 min · jiezi

Terraform入门 - 3. 变更基础设施

在上个页面,你使用Terraform创建了你第一个基础设施:一个EC2实例。在本页,我们将变更那个资源,并了解Terraform如何处理变更。基础设施在持续发展,Terraform的创建就是为了管理和实施这些变更。当你修改配置文件时,Terraform构建一个变更必要的执行计划来达到你的期望状态。使用Terraform变更基础设施,你不仅可以版本控制你的配置也可以版本控制你的状态,所以你可以看到你的基础设施时如何随着时间发展变化。配置让我们修改实例的ami。在你的配置文件中编辑 aws_instance.example资源,将它改成如下所示:resource “aws_instance” “example” { ami = “ami-b374d5a5” instance_type = “t2.micro”}注意:EC2经典用户请使用 ami-656be372 AMI 和 t1.micro类型。我们将AMI从Ubuntu 16.04 LTS改成了Ubuntu 16.10。Terraform配置意味着做如此变更。你也可以删除该资源,Terraform知道将要销毁老资源。应用变更改完配置以后,再次执行 terraform apply来查看Terraform将如何应用该变更到当前资源。$ terraform apply# …-/+ aws_instance.example ami: “ami-2757f631” => “ami-b374d5a5” (forces new resource) availability_zone: “us-east-1a” => “<computed>” ebs_block_device.#: “0” => “<computed>” ephemeral_block_device.#: “0” => “<computed>” instance_state: “running” => “<computed>” instance_type: “t2.micro” => “t2.micro” private_dns: “ip-172-31-17-94.ec2.internal” => “<computed>” private_ip: “172.31.17.94” => “<computed>” public_dns: “ec2-54-82-183-4.compute-1.amazonaws.com” => “<computed>” public_ip: “54.82.183.4” => “<computed>” subnet_id: “subnet-1497024d” => “<computed>” vpc_security_group_ids.#: “1” => “<computed>“下一步<!– more –><!–//硬啃官方文档产物,若有不妥之处,欢迎指正,请以官方文档为准!//–> ...

February 16, 2019 · 1 min · jiezi

Terraform与其他工具对比

Terraform 提供了一个资源和服务商的灵活的抽象层。该模型允许可以表示从物理硬件,虚拟机和容器到邮件和DNS提供商的所有信息。因为灵活性,Terraform 可以被用于解决许多不同的问题。这意味着许多已存在的工具与Terraform功能重叠。我们对比了Terraform和大量此类工具,但是需要注意到Terraform与其他系统并不是互斥的。它可以被用来管理单个应用程序或整个数据中心。使用左边的导航来阅读Terraform与其他特定系统对比。Terraform与Chef,Puppet等配置管理工具在已存在的机器上安装和管理软件。Terraform不是一个配置管理工具,并且它允许现有的工具专注于自己的优势:引导和初始化资源。使用provisioners,在资源被创建以后,Terraform可以使任意配置管理工具可以被用于配置和初始化资源。Terraform专注于数据中心和相关服务的更高层次的抽象,而不牺牲配置管理工具来做他们最擅长事情的能力。它也拥抱这些工具所负责的成功编排,使整个基础设施的部署简单可靠。Terraform与CloudFormation, Heat等像 CloudFormation, Heat这样的工具。允许将基础设施的细节编写为配置文件。配置文件允许基础设施被弹性创建,修改和销毁。Terraform 受其所解决问题的启发。Terraform同样使用配置文件配置基础设施细节,但是它更进一步与云平台无关并且能够结合多provider和多服务编排。例如:Terraform可以同时编排AWS和OpenStack集群,开启第三方provider像Cloudflare和DNSimple集成CDN和DNS服务。这使Terraform可以利用其支持的服务来表现和管理整个基础设施,而不是仅仅已存在于单个provider里的子集。它提供一种统一的语法,而不是需要运维为每个平台和服务使用独立且不可相互交互的工具。Terraform通过使用执行计划的概念将计划语法和执行阶段分开。通过执行terraform plan,更新当前状态并查询配置文件生成新的执行计划。该计划包含所有将要被执行的动作:将被创建,销毁和变更的资源。运维可以检查其状态以确保其符合预期。使用terraform graph,该计划将被可视化并按顺序输其出依赖关系。一旦计划被获取,执行语法会被限制在计划中的动作内。其他工具将计划和执行阶段结合在一起,这意味着运维人员被迫在心里推断变更带来的影响,这在一个大型基础架构中会很快变得难以追溯。Terraform让运维人员有信心应用变更,因为他们事前可以确切知道将会发生什么。Boto,Fogd等像Boto, Fog这样的库,被用于原生访问云提供商和服务所提供的API。一些库强制专注于特定的云,有些则尝试打通所有的云并隐藏于以上的不同。使用客户端工具仅提供对于API的低级访问,需要应用程序开发者创建他们自己的工具来构建和管理他们的基础设施。Terraform不打算提供对于提供商的低级编程访问,而已提供一个高级语法来描述云资源和服务如何被创建,制备和结合。Terraform非常灵活,使用一个基于插件的模型支持providers和provisioners,赋予它支持几乎所有服务API的能力。自定义解决方案Terraform的设计旨在解决这些挑战。它提供了一个简单,统一的语法,允许管理几乎任意资源而不需要学习一个新工具。通过捕获所有资源,他们之间的依赖关系可以被自动解决,所以运维人员不需要记住和推理。消除构建工具的负担使运维人员专注于他们的基础设施而不是工具。此外,Terraform是一款开源工具。除了HashiCorp公司,Terraform社区也在帮助扩展它的功能,修复bug,完善文档和使用案例。Terraform帮助解决每个组织都存在的一个问题并提供一种适用的标准以避免不同组织间重复造轮子。它的开源特性确保它可以长远发展。<!– more –><!–//硬啃官方文档产物,若有不妥之处,欢迎指正,请以官方文档为准!//–>

February 14, 2019 · 1 min · jiezi

Terraform入门 - 1. 安装Terraform

你机器上必须要先安装Terraform。Terraform为所有平台和架构发布的版本为二进制包。本页面不包含如何从源代码编译Terraform,对于确定希望从源代码编译出自己相信的最终二进制文件的用户可以参考文档。安装Terraform安装Terraform,找到与你系统匹配的软件包然后下载。Terraform被打包为一个zip归档文件。下载完zip文件以后,解压这个包。Terraform是一个名为terraform的独立文件。包里其他所有的文件都可以安全删掉,Terraform依然可以正常工作。最后一步确保terraform二进制文件在PATH上可用。在Linux和Mac上设置PATH请查看此页面。Windows设置PATH的命令在此页面。校验安装Terraform安装完以后,确认该安装在新开的终端中可运行,并检验terraform可用。通过执行terraform你将看到类似如下的输出的帮助信息:$ terraformUsage: terraform [–version] [–help] <command> [args]The available commands for execution are listed below.The most common, useful commands are shown first, followed byless common or more advanced commands. If you’re just gettingstarted with Terraform, stick with the common commands. For theother commands, please read the help and docs before usage.Common commands: apply Builds or changes infrastructure console Interactive console for Terraform interpolations# …如果你遇到一个terraform 未找到的错误,(因为)你的PATH环境变量没有设置正确。请回去检查确保你的PATH环境变量包含你terraform的安装目录。下一步到了使用最小化Terraform配置文件构建基础设施的时间了。在你部署它到AWS之前可以检查执行计划。<!– more –><!–//硬啃官方文档产物,若有不妥之处,欢迎指正,请以官方文档为准!//–>

February 14, 2019 · 1 min · jiezi

Terraform入门 - 2. 构建基础设施

Terraform安装完成以后,我们直接开始创建一些基础设施。我们在AWS上创建一些基础设施来开始入门指南,因为它最流行且通常可以被理解,但是Terraform可以管理许多provider,包含在单个配置文件中管理多个provider。在使用案例中有一些例子。如果你没有AWS账号,就创建一个。在入门指南中,我们将只会用AWS免费试用的资源,也就是说它是免费的。如果你已经有一个AWS账号,你可能要充一些钱,但是最多不会超过几美元。警告!如果你使用的不是一个AWS免费试用账号,你可能需要充值来执行这些例子。你最多需要充值几美元,但是我们不对任何可能产生的费用负责。配置Terraform中用来描述基础设施的的文件被叫做Terraform配置文件。现在我们将写下我们第一个配置文件来启动一个AWS的EC2实例。配置文件的文档在这里。配置文件也可以是一给json文件,但是我们建议只在机器生成配置文件时使用json格式。整个配置文件内容如下所示。我们将在随后的每一步逐步讲解。将下面内容保存到一个名为example.tf的文件中。确认在你的目录中没有其他*.tf文件,因为Terraform将加载所有的*.tf文件。provider “aws” { access_key = “ACCESS_KEY_HERE” secret_key = “SECRET_KEY_HERE” region = “us-east-1”}resource “aws_instance” “example” { ami = “ami-2757f631” instance_type = “t2.micro”}注意:上面的配置工作于大部分AWS账户,将访问默认VPC。EC2经典网络用户请为instance_type指定t1.micro,并为ami指定ami-408c7f28。如果你使用一个非us-east-1的region你将需要指定该region的ami因为每个region的ami都是特定的。用你的access key和secret key替换ACCESS_KEY_HERE和SECRET_KEY_HERE,可从此页面获取。我们现在将他们硬编码,但是在入门指南后面的将会将他们提取到变量里。注意:如果你仅仅遗漏了AWS凭证,Terraform将自动从已保存的API凭证中搜索(如:在~/.aws/credentials中)。或者IAM实例配置文件凭据。对于将文件签入源代码管理或者有多个管理员的情况,该选择要干净很多。到这里查看细节。将凭据信息遗留到配置文件以外,使你可以将凭据信息放在源代码管理之外,并且也可以为不同的用户使用不同的IAM凭据而不需要修改配置文件。这是一个完整的可执行的Terraform配置文件。一般结构应该直观并且直接。provider块用于指定provider名称,在我们的实例中叫"aws"。provider负责创建和管理资源。如果一个Terraform配置文件由多个provider组成,可以有多个provider块,这是常见的情况。resource块定一个基础设施中存在的资源。一个资源可能是物理组件,如:EC2实例,或也可以是一个逻辑资源比如Heroku应用。resource块开始前有两个字符串:资源类型和资源名称。在我们的实例中资源类型是"aws_instance",资源名为"example"。资源类型的前缀映射到provider。在我们的实例中,“aws_instance"自动告知你Terraform被"aws"provider管理。resource块内部是该资源的配置。它独立于每个资源provider并且在provider参考完全列出来。对于我们的EC2实例,我们为ubuntu指定一个AMI,然后请求一个"t2.micro"的实例因为我们有免费资格。安装为一个新配置文件或从版本控制工具中检出的已存在的配置执行的第一个命令是terraform init,它将初始化各种本地配置和数据为后面的命令使用。Terraform使用基于插件的结构来支持众多的基础设施和服务提供商。从Terraform"0.10.0"起,每个提供商有他们自己封装和发型的二进制文件,从Terraform分离出来。terraform init将自动为下载配置文件中包含provider下载插件。在该实例中只包含"aws"插件。$ terraform initInitializing the backend…Initializing provider plugins…- downloading plugin for provider “aws”…The following providers do not have any version constraints in configuration,so the latest version was installed.To prevent automatic upgrades to new major versions that may contain breakingchanges, it is recommended to add version = “…” constraints to thecorresponding provider blocks in configuration, with the constraint stringssuggested below.* provider.aws: version = “~> 1.0"Terraform has been successfully initialized!You may now begin working with Terraform. Try running “terraform plan” to seeany changes that are required for your infrastructure. All Terraform commandsshould now work.If you ever set or change modules or backend configuration for Terraform,rerun this command to reinitialize your environment. If you forget, othercommands will detect it and remind you to do so if necessary.aws provider插件与其他薄记文件一起被下载安装到当前目录子目录。输出信息会显示所安装插件的版本,以及建议在配置文件中指定版本以确保terraform init在未来安装一个兼容的版本。对于后面步骤来说这一步不是必须的,因为该配置文件后面将会被废弃。应用变更注意:本指南中列出的命令适用于terraform0.11及以上版本。更早版本需要在应用前使用terraform plan命令查看执行计划。使用terraform version命令确认你当前terraform版本。在当前目录中你创建的example.tf为例,执行terraform apply。你将看到以下类似输出,我们删节了部分输出以节省空间:$ terraform apply# …+ aws_instance.example ami: “ami-2757f631” availability_zone: “<computed>” ebs_block_device.#: “<computed>” ephemeral_block_device.#: “<computed>” instance_state: “<computed>” instance_type: “t2.micro” key_name: “<computed>” placement_group: “<computed>” private_dns: “<computed>” private_ip: “<computed>” public_dns: “<computed>” public_ip: “<computed>” root_block_device.#: “<computed>” security_groups.#: “<computed>” source_dest_check: “true” subnet_id: “<computed>” tenancy: “<computed>” vpc_security_group_ids.#: “<computed>“该输出显示执行计划,描述terraform将根据配置文件执行那些动作来改变基础设施。输出格式与工具输出的diff产生的格式类似,比如git。输出内容在 aws_instance.example 有个 + 意味着Terraform将会创建该资源。在那些之下,显示将会被设置的属性。当值为<computed>时,意味着资源被创建后才能知道。terraform apply执行失败报错时,读取错误信息并修复所报错误。在这一步,它可能是配置文件中的语法错误。如果计划被成功创建,Terraform将在执行前暂停并等待确认。如果计划中有任何不对或危险信息,在这里终止很安全,它不会对你的基础设施做任何改变。这是如果计划看起来可接受,在确认终端输入yes执行。执行该计划会花几分钟时间,直到EC2实例可用:# …aws_instance.example: Creating… ami: "” => “ami-2757f631” instance_type: "” => “t2.micro” […]aws_instance.example: Still creating… (10s elapsed)aws_instance.example: Creation completeApply complete! Resources: 1 added, 0 changed, 0 destroyed.# …在此之后Terraform执行完成,你可以到EC2终端查看创建号的EC2实例。(确保你在查看与配置文件中相同的可用区!)Terraform也会写一些数据到terraform.tfstate。该状态文件极其重要;它追踪创建的资源ID,所以Terraform知道它管理的是什么资源。该文件必须被保存并分发给可能使用terraform的任何人。通常建议在使用Terraform时设置远程状态,来自动分享状态,但是针对像入门指南这样简单的环境这不是必须的。你可以使用terraform show检查当前状态:$ terraform showaws_instance.example: id = i-32cf65a8 ami = ami-2757f631 availability_zone = us-east-1a instance_state = running instance_type = t2.micro private_ip = 172.31.30.244 public_dns = ec2-52-90-212-55.compute-1.amazonaws.com public_ip = 52.90.212.55 subnet_id = subnet-1497024d vpc_security_group_ids.# = 1 vpc_security_group_ids.3348721628 = sg-67652003你可以看到,通过创建资源,我们收集了很多信息。这些值可以被引用以配置其他资源和输出,这些将会在入门指南后面的部分讲到。准备我们在这一节创建的EC2是基于已给出的AMI,但是没有安装额外的软件。如果你在运行一个基于镜像的的架构(或许是使用Packer创建的镜像),那么这就是你所需要的。不论如何,许多基础设施都需要一些不同程度的初始化或者软件准备阶段。做到这些,Terraform支持provisioner这将会在稍后的入门指南中讲到。下一步恭喜你已经使用Terraform构建了你的第一个基础设施。你已经看到了配置语法,一个基本的执行计划实例,并且理解了状态文件。下一步,我们将继续变更和销毁基础设施。<!– more –><!–//硬啃官方文档产物,若有不妥之处,欢迎指正,请以官方文档为准!//–> ...

February 14, 2019 · 2 min · jiezi

Terraform使用案例

理解Terraform用例之前,先理解什么是Terraform非常有用。当前页面列出了Terraform的一些实际使用案例,实际的用例比我们讲到的要多很多。由于其原生扩展性,可以增加provider和provisioner来进一步扩展Terraform操作资源的能力。Heroku应用程序设置Heroku是个流行的托管web应用的PaaS平台。开发者们创建一个应用程序,然后追加附件组件,如:数据库或邮件供应商等。最好的特性之一就是可以动态调整 dynos 和 workers 的数量。但是,但多数普通应用会很快需要许多附加组件和外部服务。Terraform可以用来代码化 HeroKu 应用需要的一些配置,以确保所有所需组件可用,但是它可以更进一步做到:配置DNSSimple设置一个 CNAME,或为应用配置一个 CloudFare CDN。最妙的是,Terraform可以在不借助Web界面的前提下在30秒以内完成所有工作。多层应用多层架构一个非常常见的模式。大多数两层架构是使用数据库层的web服务器集群。增加其他层用于API服务器,缓存服务器,路由网格等。使用该模式是因为每一层可以独立伸缩,并为关注点进行隔离。Terraform是一个构建和管理基础设施的完美工具。每一层都可以被描述为一个资源集,并且每层间的依赖关系都会被自动处理;Terraform会在web服务器启动前确保数据库服务器已经可用并且负载均衡可以感知后端web节点。每一层通过只修改配置文件的计数值,使用Terraform可以很容易实现伸缩。因为创建和配置资源被代码化和自动化了,所以,随负载弹性伸缩变得不再重要。自服务集群 (Self-Service Clusters)在一定的组织规模下,管理一个大型且在增长的基础设施对于集中式运维团队而言变得非常有挑战性。取而代之的是变为创建自服务的基础设施,允许产品团队使用集中运维团队提供的工具管理他们自己的基础设施。使用Terraform,如何构建和伸缩一个服务的知识可以被代码化为一个配置文件。Terraform配置文件可以在组织内分享传播,使客户团队可以像黑盒一样使用配置文件,并且使用Terraform作为工具来管理服务。软件演示现代软件越来越向网络化和分布式发展。尽管又像Vagrant这样的工具构建虚拟环境用于演示,在与生产环境更接近的真实基础设施做演示依然充满挑战。软件开发这可以提供一个terraform配置文件来在类似AWS的云平台上创建,配置,引导一个演示环境。这使最终用户很容易在他们自己的基础设施上演示软件,设置可以调整参数像集群数量以更加严格的测试任意规模的工具。一次性环境同时有production和staging或QA环境是常用的实践。这些环境是生产环境副本的缩小版克隆,用于在正式环境发布新应用前的测试。随着生产环境增长,也越来越复杂,维护一个最新版staging环境的工作也日益繁重。使用Terraform,生产环境可以被代码化,然后共享给staging, QA 或 dev。这些环境可以被用于快速启动新环境进行测试,也很容易被销毁。Terraform可以帮助驯服平行环境的复杂性,使弹性创建和销毁变得可行。软件定义网络软件定义网络(SDN)在数据中心中变得越来越受欢迎,因为它为运维和开发提供了更多控制并使网络更好的支持运行于上层的应用。大多数SDN的实现有一个控制层和一个基础设施层。Terraform可以用于软件定义网络的代码化配置。这些配置以后可以使用Terraform自动配置和代码化代码化控制层接口。这使得配置可以版本化和自动变更。比如:AWS VPC是最常见的软件定义网络之一,且可以使用Terraform配置。资源调度程序一个大型基础架构下,为应用静态分配机器变得越来越具有挑战性。为了解决该问题,涌现了大量像 Borg, Mesos, YARN, 和 Kubernetes的调度器。他们可以用来动态调度Docker容器,Hadoop,Spark和许多其他软件工具。Terraform并不局限于像AWS这样的物理 provider。资源调度器可以被认为是一个provider,Terraform能够向他们请求资源。这使Terraform可以用于多层:配置物理基础设施,运行调度器以及配置调度器网格。多云部署跨云扩展基础设施依提升容错能力通常很有吸引力。只用一个可用区或一家云提供商,容错能力往往受限于该提供商的能力。拥有多云部署可以更优雅的恢复某宕掉的可用区甚至整个云平台。很明显多云部署很有挑战,因为目前已有的针对基础设施管理的工具都是针对特定云平台的。Terraform不限定某个云平台,允许一个配置文件用于管理多个云提供商甚至可以处理跨云依赖。它简化了管理和编排,可帮助运维构建大型多云基础设施。<!– more –><!–//二流运维,三流英语,硬啃官方文档产物,若有不妥之处,欢迎指正,请以官方文档为准!//–>

February 14, 2019 · 1 min · jiezi

在Shell中进行独立的集成测试

翻译:疯狂的技术宅原文:https://zachholman.com/posts/…本文首发微信公众号:jingchengyideng欢迎关注,每天都给你推送新鲜的前端技术文章我在开发 during.com 时创建了一系列的微服务,它们被用来做一些同步、导入和单调繁重数据处理之类的工作。如果你对微服务不熟悉,那么它只是一个花哨的名词而已,意思就是“让我们把这些该死的业务逻辑散落的到处都是!”不管怎样,我的微服务到处都是,嗯,的确是“微”。不过我绝对不是一个逗逼,我已经多次重写了自己的web服务,从Rails中的一个目录开始,然后迁移到Ruby,接着是Crystal,之后是Go,现在又回到了Ruby。这并不是在浪费时间,这只是为了以防万一而尝试新的方法。最后我又把这些服务迁移回了Ruby。这段时间Ruby的表现真是没得说,它能很轻松的进行扩展来应对用户的请求。不过目前这个应用还没有进入beta测试阶段,在你还没有用户的时候,它的确容易扩展。实际上如果在没有用户使用的前提下,几乎任何关于软件开发的一切问题都不算什么,当然除了赚钱(当然了这也并没有成为硅谷任何一家公司的障碍)。好吧我跑题了,我一直都很享受用Shell来测试这些服务的过程。在POSIX shell环境下测试, 或者 UBIQUITOUSIX shell 环境也可以我已经用Shell脚本为这些服务编写了测试,很不错。首先,不需要为基本环境操心。无论是我的AWS实例,还是我的持续集成服务器,还有我自己的开发机上都有Shell环境。所以不需要安装任何东西,也不必运行什么Docker实例(实际上用它肯定也没什么坏处)。不过最重要的一点是,我的测试是独立的,独立于将来可能会使用的任何语言。我可以在不同的语言和框架之间进行切换,而不需要对测试脚本做任何改变。这一点非常重要,因为如果你的v1版本中有一个微妙的bug,而测试却通过了,当你开始重写v2版本的服务时,如果在无意中修正了这个bug,测试将可能失败。这意味着你暴露给其它服务的API不会因此而意外中断,你可以使用其它服务来暂时顶替,为修复bug争取时间,而不是在部署到生产环境后大吃一惊。这些测试的工具也是相当不错的,这些年我一直在用我的好友Blake Mizerany写的一个Shell环境下的小工具roundup。最近我一直在使用Sam Stephenson的 bats,现在它已经形成了一个十分活跃的社区(哈,谁能想到呢,仅仅是一个shell测试工具而已)。我的Shell测试看起来就像这样,用bats:@test “Responds with events within the given timespan” { url_params="?starts_at=2017-05-01T00:00:00-00:00&ends_at=2017-05-31T00:00:00-00:00" run curl “$URL$url_params” –silent -H “Authorization: Bearer:$bearer” assert_output –partial “Test Event 0” assert_output –partial “Test Event 2” refute_output –partial “Test Event 5” refute_output –partial “No location data” refute_output –partial “Not included in the date span”}测试非常简单,也容易理解。基本上就是运行curl然后检查输出结果,完成。整合周围的一切最后一点,这些微服务非常之小,我完全可以不用为它们写任何其它的测试,只需要写集成测试即可。全栈测试(full-stack)真的非常有趣,但是人们对此很谨慎,不知道它会成为下一个好主意还是成为世界上最差劲的想法。对于它的价值,GitHub的主旨是随时愉快地运行在零单元测试的生产环境下。总的来说我正在实践这种悬而未决的理论,不过我会悬崖勒马。如果你感兴趣的话可以阅读关于这个话题更多的文章。但是我要说的是在这种情况下,哇,一股新鲜空气袭来。我们的测试是可移植的,如果我重写了服务,不必为它们重写新的测试。我只需要通过自己的基于 shell 的测试即可。本文首发微信公众号:jingchengyideng欢迎扫描二维码关注公众号,每天都给你推送新鲜的前端技术文章

February 13, 2019 · 1 min · jiezi

Shell脚本(2)

Shell变量数值计算shell中的算术运算符shell中的运算命令算术运算符常用于运算命令,shell中运算命令有以下几种:示例1,(())命令实践双小括号的作用是进行数值运算与数值的比较,常用。[root@moli_linux1 shell_test] echo $((1+1)) #计算1+1后输出2[root@moli_linux1 shell_test] echo $((9-3))6[root@moli_linux1 shell_test] ((i=5))[root@moli_linux1 shell_test] echo $((i+=1)) #获取i值,计算i+1,把i+1重新赋给i,输出i6[root@moli_linux1 shell_test] echo $i6[root@moli_linux1 shell_test] ((a=1+23-4%3))#<==表达式运算后将结果赋值给变量a,先乘除后加减[root@moli_linux1 shell_test] echo $((1+23-4%3))#<==这里直接将运算表达式进行运算并将结果输出,输出要加上$符号8[root@moli_linux1 shell_test] b=$((1+2**3-4%3))#<==这里直接将运算表达式进行运算并将结果输出,输出结果赋值给变量b[root@moli_linux1 shell_test]# echo $b8[root@moli_linux1 shell_test] a=$((100*(100+1)/2))#<==利用公式计算1+2+3….100[root@moli_linux1 shell_test] echo $a5050示例2,用(())命令进行比较[root@moli_linux1 shell_test] echo $((3>6)) #<==3>6是不成立的,因此输出0。表示比较时,1为真,0为假0[root@moli_linux1 shell_test] echo $((3>1))#<==3>1是成立的,因此输出1.1[root@moli_linux1 shell_test] echo $((3==3))#<==3=3是成立的,因此输出11注意:上面提到的数字和变量必须是整型的,不能是浮点型或字符串。下面的bc和awk命令可以用于进行浮点数运算。[root@moli_linux1 ~] echo $((a=1.0+2.5))-bash: a=1.0+2.5: 语法错误: 无效的算术运算符 (错误符号是 “.0+2.5”)[root@moli_linux1 ~]# 示例3,有关++,–的运算[root@moli_linux1 shell_test] a=10[root@moli_linux1 shell_test] echo $((a++))变量a在运算符++之前,输出整个表达式时会输出a的值,再进行表达式的自增10[root@moli_linux1 shell_test] echo $a此时输出a变量的值,是自增后的值,即为1111[root@moli_linux1 shell_test] echo $((a–))11[root@moli_linux1 shell_test] echo $a10[root@moli_linux1 shell_test] echo $((++a))变量a在运算符++之后,输出整个表达式时会先输出整个表达式自增的值,即1111[root@moli_linux1 shell_test] echo $a11[root@moli_linux1 shell_test] echo $((–a))10[root@moli_linux1 shell_test] echo $a10变量在++.–运算符之前,输出表达式的值为变量自身,再进行表达式的自增或自减;变量在++.–运算符之后,会先进行表达式的自增或自减,再输出表达式的值。let运算命令let运算命令的语法格式为:let 赋值表达式let赋值表达式相当于”((赋值表达式))“[root@moli_linux1 shell_test] i=5[root@moli_linux1 shell_test] let i=i+1[root@moli_linux1 shell_test] echo $i6expr运算命令语法:expr 表达式[root@moli_linux1 shell_test] expr 2 + 24[root@moli_linux1 shell_test] expr 2 - 20[root@moli_linux1 shell_test] expr 2 * 2expr: 语法错误[root@moli_linux1 shell_test] expr 2 * 24[root@moli_linux1 shell_test] expr 2 / 21注意:使用expr时:运算符及用于计算的数字左右至少有一个空格,否则会报错使用乘号时,必须使用反斜线屏蔽其特定含义,因为shell可能会误解星号的含义expr在Shell中可配合变量进行计算,但需要用反引号将计算表达式括起来[root@moli_linux1 shell_test] i=5[root@moli_linux1 shell_test] i=expr $i + 6此处反引号括起来的表达式,变量和数字符号两边都要有空格[root@moli_linux1 shell_test] echo $i11Shell脚本中常见的需求判断一个未知的变量是不是整数原理: 利用expr做计算时变量或字符串必须是整数的规则,把一个变量和一个整数(非零)相加。看命令的返回值是否为0。如果为0,就认为与整数相加的变量是整数,如果不是0,就不是整数[root@moli_linux1 shell_test] i=5#此时的i是整数,数字5[root@moli_linux1 shell_test] expr $i + 6 &>/dev/null #把i与一个整数相加,&>/dev/null 表示不保留任何输出[root@moli_linux1 shell_test] echo $?0#返回0说明i是整数[root@moli_linux1 shell_test] i=moli[root@moli_linux1 shell_test] expr $i + 6 &>/dev/null [root@moli_linux1 shell_test] echo $?2#返回1说明i不是是整数判断传参是不是整数[root@moli_linux1 shell_test] cat t3.sh #!/bin/bashexpr $1 + 1 >/dev/null 2>&1[ $? -eq 0 ] &&echo int||echo chars#这是一个条件表达式语法,返回值为0则输出int,否则输出chars[root@moli_linux1 shell_test] sh t3.sh 1int[root@moli_linux1 shell_test] sh t3.sh achars编写shell脚本,查找一段语句中字符数小于6的单词思路,需要知道字符串长度的计算方法,利用for循环。字符串长度可以利用expr命令得出,如几种计算字符串的方法[root@moli_linux1 ~] char=“i am a student”[root@moli_linux1 ~] expr length “$char"14[root@moli_linux1 ~] echo ${#char}14[root@moli_linux1 ~] echo ${char} | wc -L14[root@moli_linux1 ~] echo ${char} | awk ‘{print length($0)}‘14知道怎么计算字符串长度的方法,就可以利用计算出来的长度和整数做比较就可以得出结果了,例如判断下面这条语句。(有关for循环,if判断稍后再讲)i am oldboy linux welcome to our training[root@moli_linux1 shell_test] cat wordLength.sh #!/bin/bashfor n in i am oldboy welcome to our trainingdo if [ expr length $n -le 6 ] then echo $n fidone[root@moli_linux1 shell_test] sh wordLength.sh iamoldboytoour基于Shell变量输出read命令的运算实践Shell变量除了可以直接赋值或脚本传参外,还可以使用read命令从标准输入中获得。语法格式:read [参数] [变量名]常用参数:-p prompt : 设置提示信息-t timeout:设置输入等待的时间,单位默认为秒实现read命令的基本读入功能[root@moli_linux1 ~] read -t 10 -p “Please input a num:” num #作为接受read的变量,不该带$符号#读入一个输入,赋值给num变量,注意,num变量前需要有空格Please input a num:10 #输入10,把数字10赋值给num变量[root@moli_linux1 ~] echo $num10[root@moli_linux1 ~] read -t 10 -p “Please input two num:” a1 a2#读入两个输入,注意要以空格隔开,分别赋值给a1 a2变量,a1变量前后都要有空格Please input two num:10 20[root@moli_linux1 ~] echo $a1 $a210 20[root@moli_linux1 ~] read a3 a430 40[root@moli_linux1 ~] echo $a330[root@moli_linux1 ~] echo $a440read命令小实践[root@moli_linux1 shell_test] cat t2_read_change.sh#!/bin/bash#no.1read -t 10 -p “Pleasa input two int number:” a b#通过read命令读入两个值,赋给变量a和变量b[ ${#a} -le 0 ]&&{#利用条件表达式,根据变量值的长度是否小于1,来确定第一个数是否为空 echo “The first number is null.” exit 1}[ ${#b} -le 0 ]&&{#利用条件表达式,根据变量值的长度是否小于1,来确定第二个数是否为空 echo “The second number is null.” exit 2}#no.2 判断传参是否是整数expr $a + 1 &>/dev/nullRETVAL_A=$? expr $b + 1 &>/dev/nullRETVAL_B=$?#如果A和B中有一个不是整数,那么就打印提示并退出。if [ $RETVAL_A -ne 0 -o $RETVAL_B -ne 0 ];then echo “one of the num is not num,please input again.” exit 3fi#no.3echo “a+b=$(($a+$b))“echo “a-b=$(($a-$b))“echo “ab=$(($a$b))“echo “a/b=$(($a/$b))” ...

January 27, 2019 · 2 min · jiezi

Shell脚本(1)

将自己之前的shell脚本笔记搬到segmentfault,顺带复习一下shell基础。2019-1-26shell脚本介绍Shell介绍Shell是一个命令解释器,作用是解释执行用户输入的命令程序等,用户每输入一条命令,Shell就执行解释一条。这种从键盘输入命令就可以得到回应的方式叫做交互的方式。Shell存在于操作系统的最外层,负责与用户直接对话,把用户的输入解释给操作系统,并处理各种操作系统的输出结果,然后输出到屏幕给用户。Shell脚本当命令或程序语句不在命令行下执行,而是通过一个程序文件来执行,这个文件就叫做Shell脚本。Shell脚本很适合用于处理纯文本类型的数据,而Linux系统中几乎所有的配置文件,日志文件(NFS,Rsync,Httpd,Nginx,LVS 等),以及绝大多数的启动文件都是纯文本类型的文件。因此,学好shell语言,就可以利用它在linux系统中发挥巨大的作业。查看CentOS系统默认的Shell[root@moli_linux1 ~]# echo $SHELL/bin/bash[root@moli_linux1 ~]# grep root /etc/passwdroot❌0:0:root:/root:/bin/bashShell脚本的创建和执行Shell脚本的创建一个规范的Shell脚本在第一行会指出由那个程序(解释器)来执行脚本的内容,这一行内容在Linux bash编程中一般为:#!/bin/bash或者#!/bin/sh 注意:这一行必须位于每个脚本顶端的第一行,如果不是第一行则为脚本注释行。且shell脚本文件的后缀名通常以.sh结尾。Shell脚本的执行1. bash script-name(脚本的路径名字)或者sh script-name:这是当脚本文件没有可执行权限时常用的放法 2. path/script-name 或者./script-name:指在当前路径下执行脚本(脚本需要有可执行权限)。即需要将脚本文件的权限先修改为可执行,chmod +xscript-name 。然后通过脚本的绝对路径和相对路径就可以直接执行脚本。shell脚本中的变量什么是变量变量是暂时存储数据的地方及数据标记,所存储的数据存在于内存空间中,通过正确地调用内存空间中变量的名字就可以取出与变量对应的数据。CentOS中变量的定义直接由一个赋值符号=就可以创建一个变量,而echo命令类似于其他语言的print命令,打印这个变量的值。[root@moli_linux1 ~] name=“laowan” #定义一个变量[root@moli_linux1 ~] echo $name #打印这个变量的值laowan[root@moli_linux1 ~] age=14[root@moli_linux1 ~] echo $age14shell变量的特性默认情况下,在bash shell 中是不会区分变量类型的。变量类型变量分为:环境变量(全局变量)和普通变量(局部变量)环境变量可以在创建它们的shell以及其派生出来的任意子进程shell中使用。环境变量又分为自定义环境变量和bash内置的环境变量。普通变量只能在创建它们的shell函数或shell脚本中使用。提示:有三个命令可以查看系统中变量的值:set、env、declare。set命令输出所有的变量;env只显示全局变量;declare命令输出所有的变量,函数,整数和已经导出的变量。set -o命令显示bash shell的所有参数配置信息。环境变量的定义上面定义一个变量是在一个shell中直接使用赋值符号对这个变量进行创建,但是当打开一个新的子shell,打印这个变量却不能打印出来。这是因为我们创建的变量是一个普通变量,若想在其他子shell中使用这个变量,需要将这个变量设置为全局变量。而定义一个全局变量的方法如下:export 变量名=value或者declare -x 变量名=value第二个方法是将变量名添加到环境变量文件/etc/profile编辑环境配置文件vim /etc/profile在文件最后面添加export name=“laowan"保存,退出。重新加载文件source /etc/profile打印变量的值echo $namelaowan而CentOS中,全局环境变量的配置文件有三个,分别是:/etc.profile/etc/bashrc/etc/profile.d #这是一个目录当用户登录Linux系统时,首先会先加载/etc/profile全局环境变量文件,这是Linux系统默认的Shell主配置文件,其次会执行/etc/profile.d下的脚本文件,要做重启Linux系统后初始化或者显示某些内容,只需要把脚本文件放在/etc/progile.d下即可,最后才会执行bashrc文件。设置登录提示符Linux中可以设置登录之后,或者远程连接服务器打开的shell中显示登录提示符。设置登录提示内容的文件是/etc/motd,编辑这个文件就可以设置每次登录提示的内容。例如:[root@moli_linux1 ~]# cat /etc/motdwelcome to my Linux Server!打开一个新的shellConnecting to 192.168.30.3:22…Connection established.To escape to local shell, press ‘Ctrl+Alt+]’.WARNING! The remote SSH server rejected X11 forwarding request.Last login: Sat Jan 26 19:35:54 2019 from 192.168.30.1welcome to my Linux Server!可以看到登录提示符。定义变量不加单引号和双引号,加单引号,加双引号的区别[root@moli_linux1 ~] a=10[root@moli_linux1 ~] b=10-$a[root@moli_linux1 ~] c=‘100-$a’[root@moli_linux1 ~] d=“100-$a”[root@moli_linux1 ~] echo $a10[root@moli_linux1 ~] echo $b10-10[root@moli_linux1 ~] echo $c100-$a[root@moli_linux1 ~] echo $d100-10第一种定义b变量的方式,当内容为简单连续的数字,字符串,路径名时,可以这样用,不加引号时,值里有变量的会被解析后输出。第二种定义c的变量有加单引号,这样输出变量时单引号里面内容是什么就是什么,即使有变量和命令也是直接输出。第三种定义d变量的方式加了双引号,这种输出变量内容时引号里的变量以及命令会经过解析后再输出内容,这种方式比较适合字符串中附带有变量以及命令且想将其解析后再输出的变量定义。取消变量取消变量使用unset命令,注意要取消的变量之前不用添加$符号。[root@moli_linux1 ~] a=100[root@moli_linux1 ~] echo $a100[root@moli_linux1 ~] unset a[root@moli_linux1 ~] echo $a #可以看到这个变量的为空 [root@moli_linux1 ~]# 将一个命令的结果赋值给一个新的变量生产环境中把命令的结果作为变量的内容进行赋值的方法在脚本开发时很常见。有两个方法可以实现。变量名=$(命令)或者:变量名=命令[root@moli_linux1 ~] ls192.168.229 anaconda-ks.cfg git_data server shell-100 test-find[root@moli_linux1 ~] file=$(ls)[root@moli_linux1 ~] echo $file192.168.229 anaconda-ks.cfg git_data server shell-100 test-find[root@moli_linux1 ~] file2=ls[root@moli_linux1 ] echo $file2192.168.229 anaconda-ks.cfg git_data server shell-100 test-findShell中的特殊变量shell中特殊位置参数变量要从命令行,函数或脚本执行等处传递参数时,就要在shell脚本中使用位置参数变量。$0 :获取当前执行的shell脚本的文件名,如果执行脚本包含了路径,那么就包括脚本路径$1:获得当前执行的shell脚本的第n个参数值,n=1…….9,当n为0时表示脚本的文件名,当n大于9时,则用大括号括起来,例如$(10),接的参数以空格隔开$# : 获取当前执行的shell脚本后面接的参数的总个数$* :获取当前Shell脚本所有传参的参数,不加引号和$@相同,如果给$加上双引号,例如“$”,则表示将所有的参数视为单个字符串,相当于“$1$2$3”#@ :获取当前shell脚本所有传参的参数,不加引号和$*相同;如果给$@加上双引号,例如:“$@”,则表示将所有的参数视为不同独立的字符串,相当于“$1”,"$2"."$3”….这是将多参数传递给其他程序的最佳方式。示例1,特殊位置变量$1[root@moli_linux1 script] cat test.sh #!/bin/bashecho $1 #脚本的功能是打印脚本传递的第一个参数的值[root@moli_linux1 script] sh test.sh laowan #传入一个字符串参数,赋值给$1laowan[root@moli_linux1 script] sh test.sh laowan xiaoming #传入多个参数,但脚本不会接收多个参数,因此只输出第一个参数的值laowan[root@moli_linux1 script] sh test.sh “I am laowan” #用双引号括起来代表一个参数I am laowan示例2,特殊位置变量$1,$2,$3….${10}[root@moli_linux1 script] echo ${1..15} > test2.sh[root@moli_linux1 script] cat test2.sh #!/bin/bashecho $1 $2 $3 $4 $5 $6 $7 $8 $9 $10 $11 $12 $13 $14 $15[root@moli_linux1 script] sh test2.sh {a..z} #传入26个字母az,作为26个参数a b c d e f g h i a0 a1 a2 a3 a4 a5 #位置参数的数字大于9后,输出内容就不对了[root@moli_linux1 script] vim test2.sh [root@moli_linux1 script] cat test2.sh #!/bin/bash#位置参数的数字大于9时,需要用大括号将数字括起来echo $1 $2 $3 $4 $5 $6 $7 $8 $9 ${10} ${11} ${12} ${13} ${14} ${15}[root@moli_linux1 script] sh test2.sh {a..z} a b c d e f g h i j k l m n o #大于9的数字加上大括号后显示正确的内容示例3,特殊变量$0–获取脚本的名称以及路径:[root@moli_linux1 shell_test] cat 05-getName.sh #!/bin/bashecho $0[root@moli_linux1 shell_test] sh 05-getName.sh 05-getName.sh ...

January 26, 2019 · 2 min · jiezi

开发函数计算的正确姿势 —— 安装第三方依赖

前言首先介绍下在本文出现的几个比较重要的概念:函数计算(Function Compute): 函数计算是一个事件驱动的服务,通过函数计算,用户无需管理服务器等运行情况,只需编写代码并上传。函数计算准备计算资源,并以弹性伸缩的方式运行用户代码,而用户只需根据实际代码运行所消耗的资源进行付费。函数计算更多信息参考。Fun: Fun 是一个用于支持 Serverless 应用部署的工具,能帮助您便捷地管理函数计算、API 网关、日志服务等资源。它通过一个资源配置文件(template.yml),协助您进行开发、构建、部署操作。Fun 的更多文档参考。fun install: fun install 是 fun 工具的一个子命令,用于安装 pip 和 apt 依赖,提供了命令行接口和 fun.yml 描述文件两种形式。备注: 本文介绍的技巧需要 Fun 版本大于等于 2.9.3。函数计算安装第三方依赖一大痛点,文章 函数计算安装依赖库方法小结 对可能会遇到的问题和解决方法做了细致总结,fun install 是基于之前的经验和成果将最佳实践的方法固化到工具中,方便用户便捷的安装依赖。初始化使用 fun install init 在当前目录初始化一个 fun.yml 文件。(这一步不是必须的,如果您打算手写 fun.yml 然后通过 fun install 命令批量执行 task,init 是一个好的开始。)在函数计算项目根目录执行 fun install init 命令,选择一个 runtime。$ fun install init? Select runtime (Use arrow keys) python2.7 python3 nodejs6 nodejs8 java8 php7.2然后会在当前目录生成一个 fun.yml 文件,内容如下:runtime: python2.7tasks: []安装 pip 包依赖下面的命令安装 python 的 tensorflow 包$ fun install –runtime python2.7 –package-type pip tensorflowskip pulling image aliyunfc/runtime-python2.7:build-1.2.0…Task => [UNNAMED] => PYTHONUSERBASE=/code/.fun/python pip install –user tensorflow说明–runtime 指定 runtime,如果已经初始化 fun.yml 文件, 由于 fun.yml 里声明了 runtime ,该选项可以省略。–package-type 指定安装依赖的类型,pip 和 apt 是目前的两个可选值。tensorflow 是一个 pip 包名。命令执行在 fc-docker 提供的 container 中,容器内部执行的命令会逐行打印出来,比如上面命令中内部真实执行了 PYTHONUSERBASE=/code/.fun/python pip install –user tensorflow 命令。安装完成以后会在生成一个 .fun 目录, 可执行文件会被放置到 .fun/python/bin 目录下,库文件放置到 .fun/python/lib/python2.7/site-packages 下。.fun└── python ├── bin │ ├── freeze_graph │ ├── markdown_py │ ├── pbr │ ├── saved_model_cli │ ├── tensorboard │ ├── tflite_convert │ ├── toco │ └── toco_from_protos └── lib └── python2.7 └── site-packages ├── tensorboard ├── tensorboard-1.12.2.dist-info ├── tensorflow ├── tensorflow-1.12.0.dist-info ├── termcolor-1.1.0.dist-info …相比之前的 pip install -t . <package-name> 方式,fun install 安装文件的存放位置更有组织,依赖文件和代码文件分离开了,便于清理、拆分后借助 OSS 或 NAS 初始化依赖文件。但是组织过后也带来一个新问题,需要用户自定义环境变量库文件才能被程序找到。为了方便用户使用提供了一个 fun install env 打印出必要的环境变量。$ fun install envLD_LIBRARY_PATH=/code/.fun/root/usr/lib/x86_64-linux-gnu:/code:/code/lib:/usr/local/libPATH=/code/.fun/root/usr/local/bin:/code/.fun/root/usr/local/sbin:/code/.fun/root/usr/bin:/code/.fun/root/usr/sbin:/code/.fun/root/sbin:/code/.fun/root/bin:/code/.fun/python/bin:/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/sbin:/binPYTHONUSERBASE=/code/.fun/python关于如果设定函数计算的环境变量,请参考 https://help.aliyun.com/document_detail/69777.html 。如果您使用 fun local 和 fun deploy 进行调试和部署,您无需关注环境变量问题,已经帮您设定好了。使用 –save 持久化install 命令加上 –save 参数,会将命令持久化成 task 保存到 fun.yml 文件中。$ fun install –runtime python2.7 –package-type pip –save tensorflowskip pulling image aliyunfc/runtime-python2.7:build-1.2.0…Task => [UNNAMED] => PYTHONUSERBASE=/code/.fun/python pip install –user tensorflow上面的命令多加了一行 –save 参数,查看 fun.yml 内容:runtime: python2.7tasks: - pip: tensorflow local: true之后直接执行 fun install 不带参数,就可以依次执行任务。$ fun installskip pulling image aliyunfc/runtime-python2.7:build-1.2.0…Task => [UNNAMED] => PYTHONUSERBASE=/code/.fun/python pip install –user tensorflow使用 -v 显示详细日志$ fun install -vskip pulling image aliyunfc/runtime-python3.6:build-1.2.0…Task => [UNNAMED] => apt-get update (if need)Ign http://mirrors.aliyun.com stretch InReleaseGet:1 http://mirrors.aliyun.com stretch-updates InRelease [91.0 kB]Get:2 http://mirrors.aliyun.com stretch-backports InRelease [91.8 kB]Get:3 http://mirrors.aliyun.com stretch/updates InRelease [94.3 kB]Hit http://mirrors.aliyun.com stretch Release.gpgHit http://mirrors.aliyun.com stretch ReleaseGet:4 http://mirrors.aliyun.com stretch-updates/main Sources [3911 B]….安装 apt 包依赖函数计算使用 apt-get 安装依赖是另一类常见的安装问题,使用 fun install 也可以方便的安装。$ fun install –runtime python3 –package-type apt libzbar0skip pulling image aliyunfc/runtime-python3.6:build-1.2.0…Task => [UNNAMED] => apt-get update (if need) => apt-get install -y -d -o=dir::cache=/code/.fun/tmp libzbar0 => bash -c ‘for f in $(ls /code/.fun/tmp/archives/.deb); do dpkg -x $f /code/.fun/root; done;’ => bash -c ‘rm -rf /code/.fun/tmp/archives’使用方法及其参数和 pip 包依赖类似,只需要将 –package-type 设定成 apt, 包名使用日常 apt-get 可以安装的 deb 包名即可。使用 fun.ymlfun.yml 由一组 task 组成,执行 fun install 命令时会依次执行 task ,达到批量安装的效果。fun.yml 的文件格式如下runtime: python3tasks: - name: install libzbar0 apt: libzbar0 local: true - name install Pillow by pip pip: Pillow local: true - name: just test shell task shell: echo ‘111’ > 1.txtruntime 是必填的字段。目前 task 有三种类型:apt, pip 和 shell。fun.yml 文件放置在 template.yml 文件中函数 codeUri 指向的目录,如果 template.yml 里声明了多个函数,并且放置在不同的 codeUri 目录,需要创建多个 fun.yml 文件。所有 task 的 name 字段是可选的,没有 name 字段的时候执行的时候会输出为Task => [UNNAMED]apt/pip taskapt 和 pip 类型的 task 都是 install task 的子类型,描述格式类似name: install libzbar0apt: libzbar0local: true上面的 task 描述与下面的命令是等价的fun install –package-type apt libzbar0在使用 fun install 安装的过程中,使用 –save 参数可以在当前目录的 fun.yml 文件中生成上面 task 的描述结构。local 字段默认为 true,表示依赖会被装在当前目录的 .fun 子目录下,打包 zip 的时候回一并打包进去。设定为 false,依赖安装到系统目录,这种情况一般用于编译依赖,比如某个执行文件或者库是编译或者构建期需要的,运行期不要,那可以设定 local: false,打包的时候会被忽略,不影响最终 zip 包的文件尺寸。shell taskshell 类型的 task 是为基于源码编码的安装场景设计的。name: install from sourceshell: ./autogen.sh –disable-report-builder –disable-lpsolve –disable-coinmp示例下面是一个 python3 实现简单二维码识别程序部署到函数计算的例子。源码位于 https://github.com/aliyun/fun/tree/master/examples/install/pyzbar_example本例子使用 pip 的 pyzbar 库进行二维码识别,pyzbar 依赖 apt-get 安装的 libzbar0 库。装载图片需要 pip 的 Pillow 库。所以 fun.yml 的文件描述如下runtime: python3tasks: - apt: libzbar0 local: true - pip: Pillow local: true - pip: pyzbar local: true使用 fun install 安装依赖$ fun installskip pulling image aliyunfc/runtime-python3.6:build-1.2.0…Task => [UNNAMED] => apt-get update (if need) => apt-get install -y -d -o=dir::cache=/code/.fun/tmp libzbar0 => bash -c ‘for f in $(ls /code/.fun/tmp/archives/.deb); do dpkg -x $f /code/.fun/root; done;’ => bash -c ‘rm -rf /code/.fun/tmp/archives’Task => [UNNAMED] => PYTHONUSERBASE=/code/.fun/python pip install –user PillowTask => [UNNAMED] => PYTHONUSERBASE=/code/.fun/python pip install –user pyzbartemplate.yml 文件内容如下ROSTemplateFormatVersion: ‘2015-09-01’Transform: ‘Aliyun::Serverless-2018-04-03’Resources: pyzbar-srv: Type: ‘Aliyun::Serverless::Service’ pyzbar-fun: Type: ‘Aliyun::Serverless::Function’ Properties: Handler: index.handler Runtime: python3 Timeout: 60 MemorySize: 128 CodeUri: .index.py 文件内容如下:from pyzbar.pyzbar import decodefrom pyzbar.pyzbar import ZBarSymbolfrom PIL import Imagedef handler(event, context): img = Image.open(’./qrcode.png’) return decode(img, symbols=[ZBarSymbol.QRCODE])[0].data使用 fun local 在本地执行fun local invoke pyzbar-funskip pulling image aliyunfc/runtime-python3.6:1.2.0…ThalassiodraconRequestId: 964980d1-1f1b-4f91-bfd8-eadd26a307b3 Billed Duration: 630 ms Memory Size: 1998 MB Max Memory Used: 32 MBThalassiodracon 即为识别后的输出结果。小结本文介绍了 fun 工具的一个新特性 fun install ,使用 fun install 可以方便的安装 apt 和 pip 软件包,对于多次安装的工程化需求可以考虑将安装步骤持久化为 fun.yml 文件. fun.yml 文件提供了比命令行更多的功能,可以编写 shell 类型的 task,以支持源码安装的场景。可以通过设定 local: false 将依赖安装的系统目录,以解决编译依赖而非运行依赖的情况。本文作者:倚贤阅读原文本文为云栖社区原创内容,未经允许不得转载。 ...

January 25, 2019 · 4 min · jiezi

云平台设计系统,助力业务快速开发

用友进入3.0阶段,敏捷开发、逆向开发、双披萨饼原则快速作战,“用户之友”价值回归,这些都为快速原型、高保真验证、快速开发、体验一致提出了更高的要求。也带来了更大的压力。新的变化体现在:1、阶段性发版变成持续迭代,周期短,进度快2、单周或双周迭代留给各个角色的时间非常少3、被C端应用“惯坏”的用户要求更高的B端体验4、为避免资源浪费,研发流程必须UE先行,非常短时间内完成设计方案及高保真验证5、各方对产品、体验一致化的要求越来越高6、协作各个角色要求快速即时沟通、全员0延误共享设计方案7、设计团队需要摒弃简单重复的低效率工作,要求高效合作、资源共享、协同设计8、人人都是产品经理、需求变动频繁,设计要随需而变如何满足新的需求呢?要满足这些变换的新需求,唯一的解决方案就是需要一个设计系统,一个真正的设计系统,这有别于项目级别guideline或uikit的系统性设计系统,这个系统要求:所见即所得、更清晰、看得见、标准明确、可协同、可复用、可配置、可扩展…… ……云平台设计系统正是为此而生。设计系统,能够使设计团队更好、更快的打造产品并规模化产出。设计系统由基础可复用的元件构成,通过明确的标准原则组合在一起,用以搭建任意应用程序。云平台设计系统·云平台设计系统是支撑平台产品体验一致、高效开发的UE设计系统,是云平台产品的设计语言。它是基于用友一致化产品标准和风格指南,遵循用友产品设计模式的一种”沟通方式“,是平台积累多年的设计方法、模式、体验原则的理论支撑及应用。它能够有效保障平台产品交互、视觉及体验一致,有效保障团队开发的敏捷高效。·对平台UE、FE角色而言,该系统尽最大可能保障云平台产品及服务在用户体验领域的整体性、一致性。为团队提供基本的可参考原则及共享资源,提升工作效率、促进协同与创新。·云平台设计系统秉承用友”用户之友“的核心体验价值观,致力于”稳如磐石、丝般柔滑“的云平台用户体验理念。助力产品服务实现客户高价值、用户高体验的诉求。云平台设计系统的原则云平台设计系统定位服务于中、后台产品,提供:系统的、逻辑的、贴合业务的、可落地的设计理论、模式及规范支撑,设计目标追求简单、清晰、高效、一致。·简单:设计师尽可能将复杂的东西,用浅显的形式呈现出来·清晰:对问题的描述、执行、反馈等,不纠结、不含糊·高效系统设计、资源共享、元素、模式复用,团队协同·一致:目标一致、交互一致、视觉一致云平台设计系统的特点1、云平台设计系统完成了大部分组件的定制。在实现业务中就不需要多考虑按钮圆角要多大,行间距要留多少,可以更加聚焦于业务逻辑本身,节省时间精力。2、设计系统具有原子级的样式自定义能力。我们可以针对不同需求高效地产出对应的设计系统,然后快速复用组件。3、元素构成系统化。原子级的颜色、字体、阴影样式等等这些都可以自定义,而且一次修改,所有组件能够同步修改。4、协同作战样式资源规范设计团队全员共享5、先设计系统,后业务与组件库这是云平台设计系统与伪系统的根本性差异原子化设计方法的应用设计系统的维护与进化云平台设计系统综合了以往的产品、项目、经验及前瞻性趋势,将这些信息打散至“原子”颗粒,再结合产品、项目、业务、用户需求,重新梳理、抽取、沉淀,保留最原始最公共的部分形成设计基础规范及元素,基于底层设计元素组装可扩展、可配置的块元素及大型组件,通过颗粒级的元素+块元素+大型组件来灵活但又不失一致的构建页面,再融入沉淀的设计模式,快速构建应用,快速完成视觉、交互高保真、可交互界面输出。云平台设计系统的元素遵循以下原则:1、结构化、模块化所有元素在展现及模式上均被视为是可重复使用的零件;均基于结构化设计,随意搭建拆解。尽可能减少依赖关系。增强体系化元素。2、高效、标准元素或组件基于共同的标准规范。组装时使用最少的元素、产出使用最少的标注及描述来实现并清晰阐述明确设计。3、抽象、模式化基础元素(控件、组件、模式)从归纳的典型场景中分离成形。4、可配置自定义的基础元素通过可选的参数配置生成,不需要重新设计。5、可扩展元件很容易扩展,对未来的设计有预期。6、准确清晰所有设计不含糊、不纠结、契合场景角色精准传达。云平台设计系统如何实现快速开发?平台设计系统高效一致的保障还来源于UE团队与FE团队的产出均依赖这套系统的指导。基于云平台设计系统的通用sketch库及前端组件库设计系统对快速交付及一致性的支撑一体化的云平台sketch-Libraries,内含通用的平台Symbol.基于sketch-Libraries,普通UE设计师原来需要2天工作量的界面,在充分理解业务的前提下仅仅需要几个小时就可以为开发输出规范的高保真交互页面。所有UE觉得全部基于同一套标准symbol,共同使用,共同维护,实现其设计资源的协同与复用共享。对前端开发而言,无需重复书写基础样式,这里的基础控件视觉同时也是tinper-react基础控件的设计规范。设计师基于云平台sketch-Libraries快速完成设计方案,并发布到墨刀项目文档,通过共享地址,团队所有角色实现设计方案协同共享,同步即时更新。前端开发无需分散精力特别关注UI层的实现,只需要引入tinper-react即可保证一致。因为tinper-beereact 组件库,包含丰富的基础组件和应用组件,这些均基于平台设计系统规范封装,包含丰富的基础组件和应用组件, 支持组件的灵活调用和扩展。基于云平台设计系统的设计及实现方案专属云-开发平台开发者中心云平台设计系统应用案例企业服务中心、用友云运营系统、云商务系统、移动平台、Moli、企业账号、数据门户、开发者中心、开放平台、友互通、iot物联网平台、精智工业云平台、服务治理平台、云ESB、MDM主数据管理、微服务平台……等产品或系统均基于云平台设计系统。最后,我们节省了大量的时间,用来做什么?云平台设计系统帮助设计师避免重复和无用的劳动,避免沦落为密集型劳动力,剔除对设计师没有价值的工作,从而节省时间。这些时间来自,比如:在100个不同的页面上进行同样的调整工作,将系统整体风格变换,创建50次同一个组件或者替换70次同样文字。然后我们用这些新获得时间关注对用户或是客户更重要的部分,能带来更高价值、更高体验的事儿:深刻理解业务、洞察用户、改进流程、融入品牌特性,做用户反馈的分析,创新与相关的解决方案,情感化设计等等。

January 21, 2019 · 1 min · jiezi

树莓派基础-模拟信号和数字信号的区别

树莓派是很多程序员都喜欢玩的,我个人也很喜欢玩,开源接触到很多关于linux的知识,也可以通过读取硬件的数据来学习关于数据库的知识。前言本文详解阐释了模拟信号和数字信号的区别,希望帮助到有需要的朋友。数字信号使用树莓派上的GPIO引脚,很容易向输出组件发送信号并打开或关闭。还可以检测输入组件是否打开或关闭。以这种方式工作的组件称为数字组件(Digital components)。数字输出LED是数字输出元件的一个例子。它可以是打开的,也可以是关闭的,两者之间没有任何价值。我们可以把ON和OFF状态看作是1或0。你可以发送一个1给LED LED就会亮了,当你给LED发送0给 LED就熄灭了。图片描述数字输入一个按钮是数字输入组件。它可以是打开的,也可以是关闭的。当按钮被按下时,它向连接的树莓派GPIO引脚发送1。当按钮被释放时,它向GPIO引脚发送0。没有其他可以发送的值,因为你把按钮按到一半,幻想可以输出0.5。图片描述请看下面这个图,这个图显示了按钮被按压和释放的输入数据,当按压的时候输出1当释放的时候输出0。图片描述模拟信号数字输入和输出组件在Raspberry Pi中很常见,因为GPIO引脚都是数字的。它们只能发送或接收1和0。然而,并非所有组件都是数字的。有些被称为模拟组件。模拟部件可以在1和0之间发送和接收值。模拟输出电机是模拟输出元件的一个例子。你可以把它的值在1和0之间,这将控制电机的速度。如果你发送电机A 1,它将全速驱动。如果你把它发送0.5,它将以半速行驶。发送0将停止电机。图片描述模拟输入模拟输入元件的一个例子是光相关电阻器(LDR)。当没有光照在部件上时,它将发送0,并且随着光的增加,光敏发送的值将逐渐增加,直到它达到最大值1。图片描述下图显示了从LDR发送的信号在一天当中24小时的变化,随着光线越亮值越大,光线越暗值越小。图片描述使用树莓派的模拟组件比使用数字组件更加困难。为了使用GPIO引脚的模拟输出组件,您需要使用一种称为脉冲宽度调制(PWM)的技术。这向分量发送1秒和0秒的非常快的脉冲,当将其作为平均值时,可以将其接收为介于1和0之间的值。请看下面的图表。蓝线显示数字信号,在一段时间内,从0移动到1,然后再次返回。信号为1的是总时间的三分之一,剩下的三分之二是0。然后这个平均值约为0.33,这将是模拟组件接收到的值。你可以看到这是图上的红线。图片描述要使用模拟输入组件与GPIO引脚,您需要使用模拟到数字转换器(ADC),将模拟信号转换为数字信号。你可以买小的adc在你的电路使用。另外一种选择是在电路中使用电容和模拟元件。总结在树莓派上使用模拟输出是采用pwm技术,如果想要读取模拟输入那就需要ADC模拟到数字转换器,因为树莓派上没有ADC,因此我认为如果我们想要读取类似LDR或者模拟输入的情况,我们可以使用类似Nodemcu这种自带ADC的板子,然后再通过MQTT协议发送给树莓派,这样就很方便了。

January 18, 2019 · 1 min · jiezi

shell

1.datedate后边可以接两个参数,一个是[选项],一个是[格式],都是选填。[选项]为空,则为当前时间;[格式]为空,则为默认格式。如下:$ dateSat Jan 12 22:12:50 CST 2019通过date –h得到帮助如下:]$ date –hUsage: date [OPTION]… [+FORMAT] or: date [-u|–utc|–universal] [MMDDhhmm[[CC]YY][.ss]]Display the current time in the given FORMAT, or set the system date. -d, –date=STRING display time described by STRING, not now' -f, --file=DATEFILE like --date once for each line of DATEFILE -r, --reference=FILE display the last modification time of FILE -R, --rfc-2822 output date and time in RFC 2822 format. Example: Mon, 07 Aug 2006 12:34:56 -0600 --rfc-3339=TIMESPEC output date and time in RFC 3339 format. TIMESPEC=date’, seconds', or ns’ for date and time to the indicated precision. Date and time components are separated by a single space: 2006-08-07 12:34:56-06:00 -s, –set=STRING set time described by STRING -u, –utc, –universal print or set Coordinated Universal Time –help display this help and exit –version output version information and exitFORMAT controls the output. Interpreted sequences are: %% a literal % %a locale’s abbreviated weekday name (e.g., Sun) %A locale’s full weekday name (e.g., Sunday) %b locale’s abbreviated month name (e.g., Jan) %B locale’s full month name (e.g., January) %c locale’s date and time (e.g., Thu Mar 3 23:05:25 2005) %C century; like %Y, except omit last two digits (e.g., 20) %d day of month (e.g, 01) %D date; same as %m/%d/%y %e day of month, space padded; same as %_d %F full date; same as %Y-%m-%d %g last two digits of year of ISO week number (see %G) %G year of ISO week number (see %V); normally useful only with %V %h same as %b %H hour (00..23) %I hour (01..12) %j day of year (001..366) %k hour ( 0..23) %l hour ( 1..12) %m month (01..12) %M minute (00..59) %n a newline %N nanoseconds (000000000..999999999) %p locale’s equivalent of either AM or PM; blank if not known %P like %p, but lower case %r locale’s 12-hour clock time (e.g., 11:11:04 PM) %R 24-hour hour and minute; same as %H:%M %s seconds since 1970-01-01 00:00:00 UTC %S second (00..60) %t a tab %T time; same as %H:%M:%S %u day of week (1..7); 1 is Monday %U week number of year, with Sunday as first day of week (00..53) %V ISO week number, with Monday as first day of week (01..53) %w day of week (0..6); 0 is Sunday %W week number of year, with Monday as first day of week (00..53) %x locale’s date representation (e.g., 12/31/99) %X locale’s time representation (e.g., 23:13:48) %y last two digits of year (00..99) %Y year %z +hhmm numeric timezone (e.g., -0400) %:z +hh:mm numeric timezone (e.g., -04:00) %::z +hh:mm:ss numeric time zone (e.g., -04:00:00) %:::z numeric time zone with : to necessary precision (e.g., -04, +05:30) %Z alphabetic time zone abbreviation (e.g., EDT)By default, date pads numeric fields with zeroes.The following optional flags may follow `%’: - (hyphen) do not pad the field _ (underscore) pad with spaces 0 (zero) pad with zeros ^ use upper case if possible # use opposite case if possibleAfter any flags comes an optional field width, as a decimal number;then an optional modifier, which is eitherE to use the locale’s alternate representations if available, orO to use the locale’s alternate numeric symbols if available.Report date bugs to bug-coreutils@gnu.orgGNU coreutils home page: <http://www.gnu.org/software/coreutils/>General help using GNU software: <http://www.gnu.org/gethelp/>For complete documentation, run: info coreutils ‘date invocation’ ...

January 12, 2019 · 3 min · jiezi

Fish Shell 使用笔记

安装Fish Shellbrew install fish安装Oh My Fishcurl -L https://get.oh-my.fish | fish安装Fishercurl https://git.io/fisher –create-dirs -sLo /.config/fish/functions/fisher.fish配置 autojumpclone autojumpgit clone https://github.com/wting/autojump.git安装 autojump 至本地 /.autojump 目录:cd autojump./install.py在fish配置中打开文件/.config/fish/config。在编辑器中查找并添加以下行:begin set –local AUTOJUMP_PATH $HOME/.autojump/share/autojump/autojump.fish if test -e $AUTOJUMP_PATH source $AUTOJUMP_PATH endend退出fish,重新开始。使用cd命令访问常用目录。你现在可以使用j命令跳转到这些目录:exitj testDir跳转到当前目录的子目录:jc chid_dir查看autojump历史记录中的条目统计信息:j -s使用finder打开目录jo dir配置nvm使用fish之后,之前配置的nvm就不能用了,需要在/.config/fish/config当中添加nvm的配 置begin set –local AUTOJUMP_PATH $HOME/.autojump/share/autojump/autojump.fish if test -e $AUTOJUMP_PATH source $AUTOJUMP_PATH end function nvm bass source ~/.nvm/nvm.sh –no-use ‘;’ nvm $argv endend默认shell切换至fishecho /usr/local/bin/fish | sudo tee -a /etc/shellschsh -s /usr/local/bin/fishswitch to fish Fish shell 入门教程 使用版本:Autojump 22.5.1, Fish 3.0.0和Mac 10.14.2 ...

January 10, 2019 · 1 min · jiezi

PHP -------wkhtmltopdf的使用方法

一.问题描述需要用php把html生成pdf,找了了个php扩展,HTML2PDF,只能生成一些简单的HTML代码,复杂的HTML+css无法生成。网上找到了强大的wkhtmltopdf。二.wkhtmltopdf介绍工具全名叫 “wkhtmltopdf” ; 是一个使用 Qt WebKit 引擎做渲染的,能够把html 文档转换成 pdf 文档 或 图片(image) 的命令行工具。(强调一下:是 “命令行工具” )支持多个平台,可在win,linux,os x 等系统下运行。三.wkhtmltopdf安装(linux环境)安装步骤很简单,我在这里就不赘述了,具体可以参照(https://blog.csdn.net/assasin…://blog.csdn.net/qq_26562641/article/details/72875344),里面写的都很详细问题1:测试后发现,生成的pdf文件对中文并不支持(外国人写的软件,这个没办法)?答案:自行下载中文字体,安装在linux的字体库,具体方法可以参照(https://yq.aliyun.com/ziliao/269854)进行操作,其中字体可以在此网站下载(http://www.font5.com.cn/font_download.php?id=150&part=1237886897) 下面是重点戏!!!!!!! 三.php操作wkhtmltopdf方法:1.一个是exec执行 2.扩展包操作(建议采用,本文介绍的就是这个方法,由于我们是采用php语言开发,所以我在composer中选用了 mikehaertl/phpwkhtmltopdf包) 四.mikehaertl/phpwkhtmltopdf包的具体使用1.包的安装(composer很方便)2.具体使用可以参照该包的使用说明3.使用中出现的问题: q.在本地环境测试可以正常的下载,但是在linux环境下却不行? a.php.ini的禁用函数 proc_open()要去除,如果不行(将禁用函数去除) q.php.ini中的禁用函数去除后,还是不行,槽糕的是,把服务器搞的无法访问(解决办法:清除浏览器缓存)? a.说的这个问题,我们来科普一下proc_open()—php内置函数 proc_open():执行一个命令,并且打开用来输入/输出的文件指针。 wkhtmltopdf使用该命令,来进行pdf文件的生成&写入工作(生成&写入基于管道命令),所以在输入&输出有一方出现问题,就会导致,该管道无法关闭,从而出现管道阻塞,从而导致宕机。见代码(取自:vendor/mikehaertl/php-shellcommand/Command.php文件总的execute方法片段): $process = proc_open($command, $descriptors, $pipes, $this->procCwd, $this->procEnv, $this->procOptions); if (is_resource($process)) { if ($this->_stdIn!==null) { if (is_resource($this->_stdIn) && in_array(get_resource_type($this->_stdIn), array(‘file’, ‘stream’), true)) { stream_copy_to_stream($this->_stdIn, $pipes[0]); } else { fwrite($pipes[0], $this->_stdIn); } fclose($pipes[0]); } $this->_stdOut = stream_get_contents($pipes[1]); $this->_stdErr = stream_get_contents($pipes[2]); fclose($pipes[1]); fclose($pipes[2]); $this->_exitCode = proc_close($process); if ($this->_exitCode!==0) { $this->_error = $this->_stdErr ? $this->_stdErr : “Failed without error message: $command”; return false; } } else { $this->_error = “Could not run command $command”; return false; } 代码中的$descriptors,规定了包含了要传送至进程的管道的描述信息。 第一个元素为描述符类型, 第二个元素是针对该描述符的选项。 有效的类型有:pipe (第二个元素可以是: r 向进程传送该管道的读取端,w 向进程传送该管道的写入端), 以及 file(第二个元素为文件名)。表达一个真实文件描述符的流资源类型 (例如:已打开的文件,一个 socket 端口,STDIN)。 而我在实际调试中却发现,我在本地调试时,该参数写入端为w,而linux中写入端为r,这个是宕机的罪魁祸首。进行代码分析: $descriptors = array( 1 => array(‘pipe’,‘w’), 2 => array(‘pipe’, $this->getIsWindows() ? ‘a’ : ‘w’), ); public function getIsWindows() { return strncasecmp(PHP_OS, ‘WIN’, 3)===0; } 将 2 => array(‘pipe’, $this->getIsWindows() ? ‘a’ : ‘w’),=》修改为 2 => array(‘pipe’, ‘a’),问题解决。 ...

January 10, 2019 · 1 min · jiezi

直击六大会场 | 洞察100+创新实践,2018TOP100summit圆满落幕!

北京时间11月30日-12月3日,由msup和中国国际人才交流基金会联合主办的第七届全球软件案例研究峰会(简称:TOP100summit)在北京国家会议中心圆满落幕。TOP100summit是科技界一年一度的案例研究峰会,每年甄选有学习价值的100件技术创新实践,分享他们在本年度最值得的总结、盘点的实践启示。今年,本届大会以“释放AI生产力,让组织向智能化演进”为主方向,来自全球范围内的100+年度优秀软件研发实践案例对2018年的行业发展进行了一次整体复盘。此外,数百位行业翘楚也给技术人带来了新一年的规划和参照。开幕式释放AI生产力,让组织向智能化演进_在11月30日举办的开幕式上,中国国际人才交流基金会主任苏光明先生,msup创始人兼CEO刘付强先生,京东副总裁、大数据平台负责人翁志先生,华为云首席产品专家汪维敏先生 ,搜狗语音交互技术中心语音首席科学家陈伟先生,yelp产品总监杨光先生,网易有道首席科学家段亦涛先生,快手产品副总裁徐欣先生,腾讯区块链高级产品总监秦青先生,分别从产品演进、团队管理、大数据、AI应用 、区块链技术等方面,对2018年软件研发现状及未来发展趋势发表了各自的观点,给诸多参会者带来了深入的思考。具体开幕式详情可点击:释放AI生产力,让组织向智能化演进 | 2018TOP100summit开幕式成功举办!本届大会分为产品、团队、架构、测试、运维及人工智能6个专场,在2018年软件研发行业的大背景下,人工智能、区块链、大前端等话题成为大会的新亮点。TOP100组委会历时125天,3000小时,180000分钟,邀请到了20+国外的讲师,百余位国内讲师共同为1500位参会者呈现出4天的技术盛会。产品创新/体验设计/运营增长_本专场由三位联席主席策划出品:快手产品副总裁徐欣、新浪微博用户运营总经理陈福云、阿里巴巴高级体验设计专家朱斌。纵观壹佰案例产品场榜单 ,不难发现今年甄选的案例全部围绕着互联网产品各个运营细节进行的讨论,也正是为了顺应当下产品的发展趋势,我们邀请了全球产品及运营大咖。本次峰会上,小米物流系统产品经理李宽、网易云信&七鱼市场总监卡爷、个推大数据产品咨询总监沈都分别为我们讲述了B端产品和C端产品根本性的区别、作为一个B端产品经理应该具备的技能以及具体的案例经验分享。卡爷在演讲中提到,数据需要不断测试持续优化 、要重视所有用户的营销价值、以客户为中心。随着AI的发展,人工智能技术已经逐渐在大部分企业落地,如何将人工智能技术结合到业务,形成真正的人工智能产品成为了他们新的诉求,我们邀请了Google产品经理何周舟从智能化产品管理的新挑战出发,带大家理解智能产品管理和直觉型以及经典型产品管理的差别,了解并填补相关鸿沟;“数字化转型”是近几年被高频提到的词汇,喜茶CTO陈霈霖作为众多案例中,唯一的零售餐饮企业为参会者分享了喜茶数字化转型过程中的核心逻辑——数字化三支柱;在无界零售时代的大潮中,京东无人超市产品负责人高颖运用京东X事业部“无人黑科技”的技术实现无人结算,为零售行业降本增效。腾讯CDC 高级设计师 and 产品经理魏仁佳&欧龙为我们深度讲述了To G 项目业务体系的复杂度,结合数字广东在推进政务服务不同触点的迭代阶段,缔造了“互联网+政务”体系。在用户增长领域,主要涉及到用户增长体系的搭建,滴滴、网易、slack、宜人贷、中原集团也分享了各自团队在用户增长方面的实践。滴滴出行体验战略负责人冯伟伦提到,体验需要可衡量、可监控、成体系、能比较。不同的商业模式和阶段,需要体验管理体系不断的迭代和更新。除了产品创新和运营增长外,体验设计也是产品经理离不开的话题,阿里、Spotify、微软、喜马拉雅、有赞的讲师也分别讲述了产品背后的设计思考。诸位产品经理、产品负责人演讲的议题涵盖了产品方法、产品设计、团队管理等核心方法论和进阶理论,结合了各自领域内一线实操经验,涉及了AI、新零售、小程序、增长黑客、社交零售、小程序、企业服务等多个互联网热点领域及方向。组织发展/研发效能/团队管理_本专场由三位联席主席策划出品:阿里巴巴钉钉技术管理负责人,资深技术专家杨威、美团·大众点评技术学院院长刘江、百度工程效率部总监李涛。技术的发展是无休止的,同时技术也存在繁多的问题:技术挑战,技术多样性导致协同困难;人才挑战,缺少跨界领域转接;交付挑战,业务复杂交付难度;组织挑战,协作壁垒严重阻碍合作进度;能力挑战,创新型企业缺少资金和市场能力等。作为技术团队的管理者,如何正确认识和把握团队的战略发展?敏捷、OKR、转型及项目管理的实践方式仍然是最普遍且一直持续使用的方法。本届峰会,来自阿里、美团、百度、平安银行、Trend Micro等企业的讲师分别从企业转型及研发效能方面分享敏捷和OKR的实践细节和操作经验。经过了几年的刷新变革,微软的市值翻番,超过了互联网泡沫以来的高点。微软Windows中国工程团队首席研发总监邹欣为我们讲述了在微软探索复兴之路的过程中,微软文化的重塑、软件工程的创新在管理上的变化。千人规模的研发团队,不同产品部门间既有共同产品规划目标,如何实现研发团队项目化管理,数字化和透明化呈现研发过程?用友开发管理总监侯洪志给出了答案 ;小游戏的项目管理在时间紧的情况下,如何快速地确定目标、规划好版本,制定可落地执行的计划?多人并行开发,如何保证人力最优?腾讯高级项目经理徐州分享了他的实践经验;伴随招银网络科技的发展,组织规模迅速扩大,如何快速提升工程师幸福感?招银网络科技文化牵头人苏妮分享了“Best Brain”项目打造组织技术氛围。作为技术管理者,不止需要协同协作,创新及流程化管理也是企业发展和技术变革的基础,同样将新的技术及思路融合进来,才能取得更十足的进步。爆款架构/数据平台/工程实践_本专场由三位联席主席策划出品:新浪微博研发中心研发总监李庆丰、前趣店集团总架构师&技术总监徐章健、京东副总裁、大数据平台负责人翁志。在数据时代,不少企业学会以数据驱动决策。但是,谈及实践,不少企业又犯了难:如何从海量数据中选择对业务增长有价值的部分?如何清洗并分析数据以驱动决策?如何不让庞大的数据降低整体计算性能?杨波从Uber的数据平台、对Apache Spark的使用,应用挑战和改进三方面全面介绍了Spark在Uber内部的架构设计及大规模实践。数字化转型时代,新技术正在逐渐颠覆传统行业。传统企业要想扭转颓势,必须了解数字化转型的特点以快速适应迅速变化的市场,Liberty Mutual资深产品经理吴疆介绍了Liberty Mutual从技术选型、开发流程等多维度开展数字化转型,并为参会者带来了新的启示。WeiboMesh源自于微博内部对异构体系服务化的强烈需求,同时像大多服务一样,都有历史包袱,网红微博搜索架构师丁振凯在现场为我们解读了现有服务如何高效平滑完成跨语言服务方案,并结合业务实例分析了WeiboMesh的独到之处。随着电商业务快速发展,各个业务系统已经演变成分布式、微服务化平台,因此理清各业务系统的链路关系,快速准确定位系统链路的性能瓶颈变得愈发困难。京东、当当、阿里、咸鱼等电商企业从技术选型到生产环境落地,对持续进化过程中遇到的问题进行了讲述。人工智能/AI驱动/AI实践_本专场由三位联席主席策划出品:网易传媒技术副总经理刘彦东、爱奇艺资深科学家王涛、小米首席架构师、人工智能与云平台副总裁崔宝秋。目前人工智能技术已经进入4.0时代,面对数字时代的飞速发展,前沿技术已悄然改变了人类的生活方式、沟通方式和商业架构。说到人工智能,机器学习作为人工智能的核心技术,是各个行业实践人工智能的基础。360商业产品事业部高级技术经理潘尧振演讲的《360易投放-用机器学习让广告投放进入“自动驾驶”时代》,通过使用机器学习技术帮助中小广告主自动生成和优化创意、快速搭建推广计划,实现“一站式”投放。近年来人工智能不仅正在各个生活场景中加速落地,还可以处理分析一些复杂的数据信息,来自腾讯互娱、来也、Keep、LG、Uber等公司的讲师带来对当下人工智能的相关技术原理和应用成果,并且延伸到在不同场景下的应用即对未来人工智能发展趋势的展望。测试实践/测试工具链建设/大前端&移动端_本专场由两位联席主席策划出品:甲骨文研发总监许峰兵、阿里巴巴淘宝技术质量负责人青灵。在测试技术的领域中,关于Weex、模糊测试、MBT及移动端自动化测试仍然是最热门的话题。本次峰会,深度链接 - 客户端测试效率提升之蹊径、手自一体化的移动云测试平台建设方案、基于机器学习的模糊测试在大型系统产品中的应用、Weex生态质量保障方案等演讲主题为参会者带来了新的启示。大前端技术,自从诞生以来就受到人们的广泛关注,经过几年的发展,这类技术开始落地。但由于大前端的演进与火爆,前端工程师的责任也越来越大,未来对前端岗位的要求也越来越高。同时,业务发展变化迅速,页面变来变去,前端人员忙不过来,但却职业提升缓慢?爱奇艺高级经理段金辰给出了答案;海量的用户规模、跨端用户环境、全球业务部署,给前端监控带来不少新的挑战,任职于阿里巴巴的前端技术专家彭伟春,分享了分前端监控最前沿的思考,到前端监控系统的架构,再到具体的案例分析,体系化地讲述前端监控的点点滴滴。运维体系/AIOps&DevOps/区块链_本专场由三位联席主席策划出品:蘑菇街技术总监赵成、腾讯区块链业务总经理蔡弋戈、JFrog中国区首席架构师王青。运维,是2018年最火的一个话题,也是最有前途的一个话题。随着运维收到越来越多的重视,运维业务多样化和业务规模也在持续增长,并开启了运维自动化向智能化的转型之路。成本、效率和质量是现在通用的运维主要面临的三大问题,三七互娱、百度、虎牙、腾讯、JFrog等团队分别分享了各自在运维平台整体架构建设方面的心得与经验。任职于三七互娱的运维开发负责人童传江分享了他在解决任何计算机问题的过程中,如何把复杂的问题抽象为简单的模块。近两年,DevOps正在成为大家所熟知的实践方法和文化价值观,对DevOps的采用已是主流趋势。京东的研发支持团队负责人唐洪山通过成熟的研发理念和DevOps实践,助力了企业效能提升和业务发展。随着近年来区块链技术的兴起,许多的企业也开始跟随时代开始着手研发,但在面对如此复杂的区块链知识时,一些问题与挑战也开始出现。腾讯、360、华为、中国民生银行等公司的区块链技术专家带来了自己的心得,和运维体系/AIOps&DevOps/区块链专场的每一位参会者共同交流区块链技术在企业中的应用。讲师晚宴_为打造更加深入的参会体验,本届TOP100summit于12月1日晚举办了讲师晚宴活动。多位讲师与部分参会者欢聚一堂,共同就软件研发行业的热点展开思考与共鸣。茶歇&展区互动_开放的茶歇场地,为参会者和大会讲师提供了自由的交流空间,同时由msup、buzz等多家合作伙伴精装打造的特色展位组成的用户体验区域,也为参会嘉宾带来了别样的感受,让参会嘉宾在玩味前沿技术更获得愉悦放松。本届2018TOP100summit讲师阵容中,国际化比例显著提升,大会不仅邀请了国内第一阶梯大厂的领袖,还邀请了来自 Google、yelp、微软、Linkedln、Uber、亚马逊等国际巨头企业负责人。他山之石可以攻玉,这些来自全球各地的不同的产品视角和产品方法,为参会用户带来了不一样的视野和收获。同时,我们在大会设置了反馈表环节,发现今年的评分远远高于往年,整体获得了9.2分以上的好评,在此,TOP100组委会感谢各位联席主席、讲师以及业内同仁的积极参与!此外,还要特别鸣谢华为、个推、来也、亿美软通、JFrog、APICloud、环信、图灵、华章、博文视点、青云、网易云信、猎聘等赞助伙伴及IT168等合作媒体伙伴的大力支持!

December 29, 2018 · 1 min · jiezi

expect+shell实现自动ssh的证书密码自动登录

场景说明client发起ssh携带私钥证书登录server服务器,一般私钥都是设置密码的,所以产生了交互,正常情况下,我们必须手动操作输入密码,登录到服务器,非常的繁琐,所以我们来尝试自动登录尝试方式使用sshpass使用expect+shell组合sshpass尝试[失败]# 尝试直接安装,发现找不到brew install sshpass# 安装sshpassbrew install https://raw.githubusercontent.com/kadwanev/bigboybrew/master/Library/Formula/sshpass.rb# 尝试sshpass自动输入密码,发现携带了证书的一直卡着不动,失败sshpass -p 密码 ssh -i my_pri.pem myaccount@ip -p22expect+shell尝试[成功]安装expectbrew install expectjp.sh 脚本#!/bin/bashssh -i my_pri.pem myaccount@ip -p22chmod a+x jp.shexpjp.exp 脚本#!/usr/bin/expect -fspawn ./jp.sh# 注意 这里的文字需要根据自己提示自行调整expect “Enter passphrase for key ‘my_pri.pem’:“send “密码\n"interactchmod a+x expjp.exp尝试运行//运行成功./expjp.exp总结expect可以在各种产生交互的地方使用,等待大家慢慢挖掘

December 27, 2018 · 1 min · jiezi

netty 基于 protobuf 协议 实现 websocket 版本的简易客服系统

结构netty 作为服务端protobuf 作为序列化数据的协议websocket 前端通讯演示netty 服务端实现Server.java 启动类import io.netty.bootstrap.ServerBootstrap;import io.netty.channel.ChannelFuture;import io.netty.channel.EventLoopGroup;import io.netty.channel.nio.NioEventLoopGroup;import io.netty.channel.socket.nio.NioServerSocketChannel;import io.netty.handler.logging.LogLevel;import io.netty.handler.logging.LoggingHandler;import java.net.InetSocketAddress;//websocket长连接示例public class Server { public static void main(String[] args) throws Exception{ // 主线程组 EventLoopGroup bossGroup = new NioEventLoopGroup(); // 从线程组 EventLoopGroup wokerGroup = new NioEventLoopGroup(); try{ ServerBootstrap serverBootstrap = new ServerBootstrap(); serverBootstrap.group(bossGroup,wokerGroup) .channel(NioServerSocketChannel.class) .handler(new LoggingHandler(LogLevel.INFO)) .childHandler(new ServerChannelInitializer()); ChannelFuture channelFuture = serverBootstrap.bind(new InetSocketAddress(8899)).sync(); channelFuture.channel().closeFuture().sync(); }finally { bossGroup.shutdownGracefully(); wokerGroup.shutdownGracefully(); } }}ServerChannelInitializer.javaimport com.example.nettydemo.protobuf.MessageData;import com.google.protobuf.MessageLite;import com.google.protobuf.MessageLiteOrBuilder;import io.netty.buffer.ByteBuf;import io.netty.channel.ChannelHandlerContext;import io.netty.channel.ChannelInitializer;import io.netty.channel.ChannelPipeline;import io.netty.channel.socket.SocketChannel;import io.netty.handler.codec.MessageToMessageDecoder;import io.netty.handler.codec.MessageToMessageEncoder;import io.netty.handler.codec.http.HttpObjectAggregator;import io.netty.handler.codec.http.HttpServerCodec;import io.netty.handler.codec.http.websocketx.BinaryWebSocketFrame;import io.netty.handler.codec.http.websocketx.WebSocketFrame;import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;import io.netty.handler.codec.http.websocketx.extensions.compression.WebSocketServerCompressionHandler;import io.netty.handler.codec.protobuf.ProtobufDecoder;import io.netty.handler.stream.ChunkedWriteHandler;import java.util.List;import static io.netty.buffer.Unpooled.wrappedBuffer;public class ServerChannelInitializer extends ChannelInitializer<SocketChannel> { @Override protected void initChannel(SocketChannel ch) throws Exception { ChannelPipeline pipeline = ch.pipeline(); // HTTP请求的解码和编码 pipeline.addLast(new HttpServerCodec()); // 把多个消息转换为一个单一的FullHttpRequest或是FullHttpResponse, // 原因是HTTP解码器会在每个HTTP消息中生成多个消息对象HttpRequest/HttpResponse,HttpContent,LastHttpContent pipeline.addLast(new HttpObjectAggregator(65536)); // 主要用于处理大数据流,比如一个1G大小的文件如果你直接传输肯定会撑暴jvm内存的; 增加之后就不用考虑这个问题了 pipeline.addLast(new ChunkedWriteHandler()); // WebSocket数据压缩 pipeline.addLast(new WebSocketServerCompressionHandler()); // 协议包长度限制 pipeline.addLast(new WebSocketServerProtocolHandler("/ws", null, true)); // 协议包解码 pipeline.addLast(new MessageToMessageDecoder<WebSocketFrame>() { @Override protected void decode(ChannelHandlerContext ctx, WebSocketFrame frame, List<Object> objs) throws Exception { ByteBuf buf = ((BinaryWebSocketFrame) frame).content(); objs.add(buf); buf.retain(); } }); // 协议包编码 pipeline.addLast(new MessageToMessageEncoder<MessageLiteOrBuilder>() { @Override protected void encode(ChannelHandlerContext ctx, MessageLiteOrBuilder msg, List<Object> out) throws Exception { ByteBuf result = null; if (msg instanceof MessageLite) { result = wrappedBuffer(((MessageLite) msg).toByteArray()); } if (msg instanceof MessageLite.Builder) { result = wrappedBuffer(((MessageLite.Builder) msg).build().toByteArray()); } // ==== 上面代码片段是拷贝自TCP ProtobufEncoder 源码 ==== // 然后下面再转成websocket二进制流,因为客户端不能直接解析protobuf编码生成的 WebSocketFrame frame = new BinaryWebSocketFrame(result); out.add(frame); } }); // 协议包解码时指定Protobuf字节数实例化为CommonProtocol类型 pipeline.addLast(new ProtobufDecoder(MessageData.RequestUser.getDefaultInstance())); // websocket定义了传递数据的6中frame类型 pipeline.addLast(new ServerFrameHandler()); }}ServerFrameHandler.javaimport com.example.nettydemo.protobuf.MessageData;import io.netty.buffer.ByteBuf;import io.netty.buffer.Unpooled;import io.netty.channel.Channel;import io.netty.channel.ChannelHandlerContext;import io.netty.channel.SimpleChannelInboundHandler;import io.netty.channel.group.ChannelGroup;import io.netty.channel.group.DefaultChannelGroup;import io.netty.handler.codec.http.websocketx.BinaryWebSocketFrame;import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;import io.netty.handler.codec.http.websocketx.WebSocketFrame;import io.netty.util.concurrent.GlobalEventExecutor;import java.util.List;//处理文本协议数据,处理TextWebSocketFrame类型的数据,websocket专门处理文本的frame就是TextWebSocketFramepublic class ServerFrameHandler extends SimpleChannelInboundHandler<MessageData.RequestUser> { private final ChannelGroup channelGroup = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE); //读到客户端的内容并且向客户端去写内容 @Override protected void channelRead0(ChannelHandlerContext ctx, MessageData.RequestUser msg) throws Exception { // channelGroup.add(); Channel channel = ctx.channel(); System.out.println(msg.getUserName()); System.out.println(msg.getAge()); System.out.println(msg.getPassword()); MessageData.ResponseUser bank = MessageData .ResponseUser.newBuilder() .setUserName(“你好,请问有什么可以帮助你!”) .setAge(18).setPassword(“11111”).build(); channel.writeAndFlush(bank); } //每个channel都有一个唯一的id值 @Override public void handlerAdded(ChannelHandlerContext ctx) throws Exception { //打印出channel唯一值,asLongText方法是channel的id的全名 // System.out.println(“handlerAdded:"+ctx.channel().id().asLongText()); } @Override public void handlerRemoved(ChannelHandlerContext ctx) throws Exception { // System.out.println(“handlerRemoved:” + ctx.channel().id().asLongText()); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { System.out.println(“异常发生”); ctx.close(); } }protobuf 文件的使用proto 文件syntax =“proto2”;package com.example.nettydemo.protobuf;//optimize_for 加快解析的速度option optimize_for = SPEED;option java_package = “com.example.nettydemo.protobuf”;option java_outer_classname=“MessageData”;// 客户端发送过来的消息实体message RequestUser{ optional string user_name = 1; optional int32 age = 2; optional string password = 3;}// 返回给客户端的消息实体message ResponseUser{ optional string user_name = 1; optional int32 age = 2; optional string password = 3;}生成 proto 的Java 类批量生成工具,直接找到这个 bat 或者 sh 文件,在对应的平台执行就可以了具体可以自行百度 protobuf 怎么使用Windows 版本set outPath=../../javaset fileArray=(MessageDataProto ATestProto)# 将.proto文件生成java类for %%i in %fileArray% do ( echo generate cli protocol java code: %%i.proto protoc –java_out=%outPath% ./%%i.proto)pausesh 版本 地址: https://github.com/lmxdawn/ne...#!/bin/bashoutPath=../../javafileArray=(MessageDataProto ATestProto)for i in ${fileArray[@]};do echo “generate cli protocol java code: ${i}.proto” protoc –java_out=$outPath ./$i.protodonewebsocket 实现<!DOCTYPE html><html lang=“en”><head> <meta charset=“UTF-8”> <title>WebSocket客户端</title></head><body><script src=“protobuf.min.js”></script><script type=“text/javascript”> var socket; //如果浏览器支持WebSocket if (window.WebSocket) { //参数就是与服务器连接的地址 socket = new WebSocket(“ws://localhost:8899/ws”); //客户端收到服务器消息的时候就会执行这个回调方法 socket.onmessage = function (event) { var ta = document.getElementById(“responseText”); // 解码 responseUserDecoder({ data: event.data, success: function (responseUser) { var content = “客服小姐姐: " + responseUser.userName + “, 小姐姐年龄: " + responseUser.age + “, 密码: " + responseUser.password; ta.value = ta.value + “\n” + content; }, fail: function (err) { console.log(err); }, complete: function () { console.log(“解码全部完成”) } }) } //连接建立的回调函数 socket.onopen = function (event) { var ta = document.getElementById(“responseText”); ta.value = “连接开启”; } //连接断掉的回调函数 socket.onclose = function (event) { var ta = document.getElementById(“responseText”); ta.value = ta.value + “\n” + “连接关闭”; } } else { alert(“浏览器不支持WebSocket!”); } //发送数据 function send(message) { if (!window.WebSocket) { return; } // socket.binaryType = “arraybuffer”; // 判断是否开启 if (socket.readyState !== WebSocket.OPEN) { alert(“连接没有开启”); return; } var data = { userName: message, age: 18, password: “11111” }; requestUserEncoder({ data: data, success: function (buffer) { console.log(“编码成功”); socket.send(buffer); }, fail: function (err) { console.log(err); }, complete: function () { console.log(“编码全部完成”) } }); } /** * 发送的消息编码成 protobuf / function requestUserEncoder(obj) { var data = obj.data; var success = obj.success; // 成功的回调 var fail = obj.fail; // 失败的回调 var complete = obj.complete; // 成功或者失败都会回调 protobuf.load(”../proto/MessageDataProto.proto”, function (err, root) { if (err) { if (typeof fail === “function”) { fail(err) } if (typeof complete === “function”) { complete() } return; } // Obtain a message type var RequestUser = root.lookupType(“com.example.nettydemo.protobuf.RequestUser”); // Exemplary payload var payload = data; // Verify the payload if necessary (i.e. when possibly incomplete or invalid) var errMsg = RequestUser.verify(payload); if (errMsg) { if (typeof fail === “function”) { fail(errMsg) } if (typeof complete === “function”) { complete() } return; } // Create a new message var message = RequestUser.create(payload); // or use .fromObject if conversion is necessary // Encode a message to an Uint8Array (browser) or Buffer (node) var buffer = RequestUser.encode(message).finish(); if (typeof success === “function”) { success(buffer) } if (typeof complete === “function”) { complete() } }); } /* * 接收到服务器二进制流的消息进行解码 */ function responseUserDecoder(obj) { var data = obj.data; var success = obj.success; // 成功的回调 var fail = obj.fail; // 失败的回调 var complete = obj.complete; // 成功或者失败都会回调 protobuf.load(”../proto/MessageDataProto.proto”, function (err, root) { if (err) { if (typeof fail === “function”) { fail(err) } if (typeof complete === “function”) { complete() } return; } // Obtain a message type var ResponseUser = root.lookupType(“com.example.nettydemo.protobuf.ResponseUser”); var reader = new FileReader(); reader.readAsArrayBuffer(data); reader.onload = function (e) { var buf = new Uint8Array(reader.result); var responseUser = ResponseUser.decode(buf); if (typeof success === “function”) { success(responseUser) } if (typeof complete === “function”) { complete() } } }); }</script><h1>欢迎访问客服系统</h1><form onsubmit=“return false”> <textarea name=“message” style=“width: 400px;height: 200px”></textarea> <input type=“button” value=“发送数据” onclick=“send(this.form.message.value);"> <h3>回复消息:</h3> <textarea id=“responseText” style=“width: 400px;height: 300px;"></textarea> <input type=“button” onclick=“javascript:document.getElementById(‘responseText’).value=’’” value=“清空数据”></form></body></html>扩展阅读spring boot 实现的后台管理系统vue + element-ui 实现的后台管理界面,接入 spring boot API接口 ...

December 20, 2018 · 5 min · jiezi

fir.me 自动化发布Android应用并自动添加更新日志

我们再上篇拿到了最新的git log 3条更新日志参考链接:链接地址 点击这里自动上传脚本如下:此脚本在jenkins 构建脚本里,执行任务后自动上传、并添加更新日志(最新3条日志) gradle clean gradle assembleRelease fir login b0ad1098a8fa6950ebc629dfXXXXXXX (您自己的id自己配) fir me git log -3 –pretty=format:’%s’ –abbrev-commit | awk -F ‘:’ ‘{print NR " " $0 }’ | pbcopy fir publish -s 自己定义的短链接地址(英文缩写) app/build/outputs/apk/flavorDev/release/.apk -c “$(pbpaste)” fir publish -s 自己定义的短链接地址(英文缩写 app/build/outputs/apk/flavorTest/release/.apk -c “$(pbpaste)” 实现的效果如下:

December 19, 2018 · 1 min · jiezi

git log 取最近3条提交记录加上编号并复制到剪贴板

最近在写自动发布的构建处理脚本,我在想每次自动打包,上传到fir.im 需要填写每次的更新内容,需要手动的添加更改、增加、fixe内容,如何实现自动化添加 修改记录?我借助了jenkins自动构建工具,每次提交会自动构建,打包发布,发布成功后,需要手动添加 提交记录比较麻烦。此文简述如何拿到提交记录,并复制到剪贴板。我们的需求如下图,实现自动将git log最新日志拿到,并上传到fir.im中。实现方式第一步 用git log获取最近提交的3条记录 命令行如下git log -3 –pretty=format:’%s’ –abbrev-commit 第二步 将每一条提交记录增加编号 比如 1.XXXX 2.XXX 3.XXX想要的效果是:1.change –>>> 审计中 提交、拒绝按钮 变成 扁平样式 不是组合的MenuGroup 2. 审计工单 提交成功 && 增加审计图片、审计评论等 3. add 增加 审计 图片、审计评论 到审计工单数据中用awk实现打印行号 并将每一行效果打印出来。具体命令如下:git log -3 –pretty=format:’%s’ –abbrev-commit | awk -F ‘:’ ‘{print NR " " $0 }‘第三步 复制到剪贴板(命令行实现)这里一定是管道命令哦,将处理好的文本弄到剪贴板中( | pbcop )全部命令如下:git log -3 –pretty=format:’%s’ –abbrev-commit | awk -F ‘:’ ‘{print NR " " $0 }’ | pbcopy自动化构建成功!剪贴板内容为:1 change –>>> 审计中 提交、拒绝按钮 变成 扁平样式 不是组合的MenuGroup2 审计工单 提交成功 && 增加审计图片、审计评论等3 add 增加 审计 图片、审计评论 到审计工单数据中 ...

December 19, 2018 · 1 min · jiezi

【shell技巧】创建并且进入文件夹, 一个小技巧,务必收藏,熟记于心。

创建并且进入文件夹mkdir [folder] && cd $mkdir 创建命令[folder]文件夹名字&& 并且(接着执行后面的命令)cd 进入文件夹$ 上一个命令的最后参数examplemkdir RDhub && cd $_不用我们再次「 cd RDhub 」输入了,在一定程度上节约了效率,一个小技巧,务必收藏,熟记于心。关于我们https://www.yuque.com/rdhub/a…关注订阅号加入RDhub 社群

October 21, 2018 · 1 min · jiezi