PS.本文中的 shell 代指 bash

Shell 中的括号

小括号()

# 应用实例
# ()作为一个命令组
if ($i<5)
# "[" 意为test, "-lt" 意为less than 小于, "]"  意为关闭条件判断
if [ $i -lt 5 ]
# "-ne" 意为 不等于,] 后面的 "-a" 为逻辑操作符,等价于 "&&"
if [ $a -ne 1 ] -a [ $a != 2 ]
if [ $a -ne 1] && [ $a != 2 ]
if [[ $a != 1 && $a != 2 ]]

for i in $(seq 0 4);do echo $i;done
for i in `seq 0 4`;do echo $i;done
for ((i=0;i<5;i++));do echo $i;done
for i in {0..4};do echo $i;done



# 循环读取文件中的每一行并将该行作为参数,执行相关的命令
while read p; do
  echo "$p"
done < file.txt

  1. 单小括号 ()
    • 命令组。括号中的命令将会新开一个子shell顺序执行,所以括号中的变量不能够被脚本余下的部分使用。括号中多个命令之间用分号隔开,最后一个命令可以没有分号,各命令和括号之间不必有空格。
    • 命令替换。等同于cmd,shell扫描一遍命令行,发现了$(cmd)结构,便将$(cmd)中的cmd执行一次,得到其标准输出,再将此输出放到原来命令。有些shell不支持,如tcsh。
    • 用于初始化数组。如:array=(a b c d)。
  2. 双小括号 (( ))
    • 整数扩展。这种扩展计算是整数型的计算,不支持浮点型。((exp))结构扩展并计算一个算术表达式的值,如果表达式的结果为0,那么返回的退出状态码为1,或者 是"假",而一个非零值的表达式所返回的退出状态码将为0,或者是"true"。若是逻辑判断,表达式exp为真则为1,假则为0。
    • 只要括号中的运算符、表达式符合C语言运算规则,都可用在$((exp))中,甚至是三目运算符。作不同进位(如二进制、八进制、十六进制)运算时,输出结果全都自动转化成了十进制。如:echo $((16#5f)) 结果为95 (16进位转十进制)
    • 单纯用 (( )) 也可重定义变量值,比如 a=5; ((a++)) 可将 $a 重定义为6。
    • 双括号中的变量可以不使用$符号前缀。括号内支持多个表达式用逗号分开。

中括号[]

  1. 单中括号 []
    • bash 的内部命令,[和test是等同的。如果我们不用绝对路径指明,通常我们用的都是bash自带的命令。if/test结构中的左中括号是调用test的命令标识,右中括号是关闭条件判断的。这个命令把它的参数作为比较表达式或者作为文件测试,并且根据比较的结果来返回一个退出状态码。if/test结构中并不是必须右中括号,但是新版的Bash中要求必须这样。
    • Test和[]中可用的比较运算符只有==和!=,两者都是用于字符串比较的,不可用于整数比较,整数比较只能使用-eq,-gt这种形式。无论是字符串比较还是整数比较都不支持大于号小于号。如果实在想用,对于字符串比较可以使用转义形式,如果比较"ab"和"bc":[ ab \< bc ],结果为真,也就是返回状态为0。[ ]中的逻辑与和逻辑或使用-a 和-o 表示。
    • 字符范围。用作正则表达式的一部分,描述一个匹配的字符范围。作为test用途的中括号内不能使用正则。
    • 在一个array 结构的上下文中,中括号用来引用数组中每个元素的编号或是索引。
  2. 双中括号[[ ]]
    • [[是 bash 程序语言的关键字。并不是一个命令,[[ ]] 结构比[ ]结构更加通用。在[[和]]之间所有的字符都不会发生文件名扩展或者单词分割,但是会发生参数扩展和命令替换。
    • 支持字符串的模式匹配,使用=~操作符时甚至支持shell的正则表达式。字符串比较时可以把右边的作为一个模式,而不仅仅是一个字符串,比如[[ hello == hell? ]],结果为真。[[ ]] 中匹配字符串或通配符,不需要引号。
    • 使用[[ ... ]]条件判断结构,而不是[ ... ],能够防止脚本中的许多逻辑错误。比如,&&、||、<和> 操作符能够正常存在于[[ ]]条件判断结构中,但是如果出现在[ ]结构中的话,会报错。
    • bash把双中括号中的表达式看作一个单独的元素,并返回一个退出状态码。

关于单中括号 [] 和双中括号 [[]] 的区别,可以参照这里

大括号{}

$ ls {ex1,ex2}.sh
ex1.sh  ex2.sh
$ ls {ex{1..3},ex4}.sh
ex1.sh  ex2.sh  ex3.sh  ex4.sh
$ ls {ex[1-3],ex4}.sh
ex1.sh  ex2.sh  ex3.sh  ex4.sh
  1. 常规用法
    • 大括号拓展。

      (通配(globbing))将对大括号中的文件名做扩展。在大括号中,不允许有空白,除非这个空白被引用或转义。第一种:对大括号中的以逗号分割的文件列表进行拓展。如 touch {a,b}.txt 结果为a.txt b.txt。第二种:对大括号中以点点(..)分割的顺序文件列表起拓展作用,如:touch {a..d}.txt 结果为a.txt b.txt c.txt d.txt

    • 代码块,又被称为内部组,这个结构事实上创建了一个匿名函数。

      与小括号中的命令不同,大括号内的命令不会新开一个子shell运行,即脚本余下部分仍可使用括号内变量。括号内的命令间用分号隔开,最后一个也必须有分号。{}的第一个命令和左括号之间必须要有一个空格。

  2. 几种特殊的替换结构

    • ${var:-string}和${var:=string}:若变量var为空,则用在命令行中用string来替换${var:-string},否则变量var不为空时,则用变量var的值来替换${var:-string};对于${var:=string}的替换规则和${var:-string}是一样的,所不同之处是${var:=string}若var为空时,用string替换${var:=string}的同时,把string赋给变量var: ${var:=string}很常用的一种用法是,判断某个变量是否赋值,没有的话则给它赋上一个默认值。
    • ${var:+string}的替换规则和上面的相反,即只有当var不是空的时候才替换成string,若var为空时则不替换或者说是替换成变量 var的值,即空值。(因为变量var此时为空,所以这两种说法是等价的)
    • ${var:?string}替换规则为:若变量var不为空,则用变量var的值来替换${var:?string};若变量var为空,则把string输出到标准错误中,并从脚本中退出。我们可利用此特性来检查是否设置了变量的值。
    • 补充扩展:在上面这五种替换结构中string不一定是常值的,可用另外一个变量的值或是一种命令的输出。
  3. 四种模式匹配替换结构
    • ${variable%pattern},这种模式时,shell在variable中查找,看它是否一给的模式pattern结尾,如果是,就从命令行把variable中的内容去掉右边最短的匹配模式
    • ${variable%%pattern},这种模式时,shell在variable中查找,看它是否一给的模式pattern结尾,如果是,就从命令行把variable中的内容去掉右边最长的匹配模式
    • ${variable#pattern} 这种模式时,shell在variable中查找,看它是否一给的模式pattern开始,如果是,就从命令行把variable中的内容去掉左边最短的匹配模式
    • ${variable##pattern} 这种模式时,shell在variable中查找,看它是否一给的模式pattern结尾,如果是,就从命令行把variable中的内容去掉右边最长的匹配模式
    • 这四种模式中都不会改变variable的值,其中,只有在pattern中使用了匹配符号时,%和%%,#和##才有区别。结构中的pattern支持通配符,表示零个或多个任意字符,?表示零个或一个任意字符,[...]表示匹配中括号里面的字符,[!...]表示不匹配中括号里面的字符
$ var=testcase
$ echo $var
testcase
$ echo ${var%s*e}
testca
$ echo $var
testcase
$ echo ${var%%s*e}
te
$ echo ${var#?e}
stcase
$ ${var##?e}
stcase
$ echo ${var##*e}

$ echo ${var##*s}
e
$ echo ${var##test}
case

Test 与 逻辑运算

判断一串命令的执行结果

# 判断文件夹知否为空
test `ls some-dir | wc -c` -eq 0 && echo "为空" || echo "非空"
# 以上命令等价于
test $(ls some-dir | wc -c) -eq 0 && echo "为空" || echo "非空"
[ $(ls some-dir | wc -c) -eq 0 ] && echo "为空" || echo "非空"
[[ $(ls some-dir | wc -c) -eq ]] && echo "为空" || echo "非空"
[[ $(ls some-dir | wc -c)=0 ]] && echo "为空" || echo "非空"
# 在实际的脚本中推荐使用含 双中括号 的第四种写法 "="等价判断两边不得含有空格,否则报错

为Composer/Git/Gulp/Brew/Npm/Pip/Drush添加自动补全

  1. 安装bash-completion

    Mac下是brew, Debian下面是Apt-get

  2. 从github的bash-it项目下载需要的自动补全bash脚本,放到/etc/bash-completion目录下

  3. vi /etc/bashrc 加入如下代码

function bash_completion_load(){

    local bash_completion=/etc/bash-completion

    if [[ -e $bash_completion ]] && [[ -d $bash_completion ]] && [[ $(ls $bash_completion | wc -c)!=0 ]]; then
        for completion_item in $(ls $bash_completion); do
            if [[ ${completion_item##*.}='bash' ]]; then
                chmod +x $bash_completion/$completion_item
                source $bash_completion/$completion_item
            fi
        done
    fi
}

bash_completion_load;

至于Debian Linux来说,直接把从github下载的补全脚本文件直接放到/etc/bash-completion.d/目录下就可以了。
需要添加执行权限

Vanish

最近忙着总结以往的工作经验,并将实践得出的结果运用于新项目Pegasus中,在Pegasus项目中技术选型的基础为LNMP+Drupal-8.X,由于D8是Heavily Based on OOP,因此在实际不出的时候就不得不考虑到性能的问题。

实际上决定D8响应速度的因素有很多,仅仅就服务端来说,将动态网站进行反向代理静态化是提高响应速度非常有效的解决办法。如果一台服务上有多个网站,而大多都是资源消耗比较高的网站的话,合理配置一台前端反向代理缓存服务器对于提高优化服务器的性能和提高网站的响应就十分重要了。

什么是Vanish

Varnish是一款高性能的、开源的Http反向代理服务器和缓存服务器。
Varnish基于多线程。
Varnish高速缓存于内存中。
Varnish具有更好的稳定性、更快的访问速度、更多的并发连接支持数,可以通过管理端口管理缓存等优势,在高并发状态下,Varnish消耗更多的CPU、I/O和内存资源。Varnish进程一旦挂起、崩溃或者重启,缓存的数据会从内存中释放,此时所有的请求都会转发到后端服务器上,给后端服务器造成很大压力。Varnish基于线程,可与充分应对高并发的使用场景。

Vanish配置示例

针对drupal的Varnish优化

作为悲催的跨境电商,免不了每天都要和GFW做斗争。最近明显的感觉的ssh到服务器延时越来越好,随手ping一下40%以上的丢包率,使我无言以对。

 

ssh到服务器中,shell明显的卡顿和延时,使得工作中大部分的时间都是在等待服务器的响应,这样的工作效率我无法接受,经过相关的研究和了解,mosh作为一个不错的ssh替代方案,使用udp协议传输数据,相信会比tcp协议在应对gfw方面具有更高的存活率和可到达性。我们可以这样理解,mosh在本质上和ssh是一样的,只不过是底层协议发生了变化。想了解关于mosh的具体情况,可以直接google。

实际上我已经在公司自己的本地开发服务器ubuntu上部署了mosh,运行十分完美,没有出现特别延时和卡顿现象,由于本地开发服务器使用的是动态域名解析和网关路由的DMZ技术,以前在家里使用ssh连接开发服务器,还是会时不时的掉线。

当我打算直接的生产服务器部署mosh时,却出现了意料不到的问题。生产服务器环境为debian jessie。

安装很简单,直接apt-cache search mosh结果显示,debian的repository中已包含该软件包。那么直接apt-get install mosh.

安装完之后,使用命令mosh-server检查mosh服务端的状态。意外出现了,mosh服务端没有运行,提示我

mosh-server needs a UTF-8 native locale to run.

Unfortunately, the local environment (LANG=) specifies
the character set "US-ASCII",

The client-supplied environment ([no charset variables]) specifies
the character set "US-ASCII".

LANG=
LANGUAGE=
LC_CTYPE="POSIX"
LC_NUMERIC="POSIX"
LC_TIME="POSIX"
LC_COLLATE="POSIX"
LC_MONETARY="POSIX"
LC_MESSAGES="POSIX"
LC_PAPER="POSIX"
LC_NAME="POSIX"
LC_ADDRESS="POSIX"
LC_TELEPHONE="POSIX"
LC_MEASUREMENT="POSIX"
LC_IDENTIFICATION="POSIX"
LC_ALL=

使用env命令打印一下服务器的环境变量设置,查看服务器的配置情况。

发现默认的有关语言的配置中,变量LANG和LANGUAGE都为空,而Debian默认的语言设置的US-ASCII,而mosh运行需要UTF8的语言包,这里mosh官方有完整的说明,为何要使用UTF8,这里不做解释。所以问题就出在服务器环境中语言的设置问题。

//由于mosh会接管ssh,所以mosh会读取ssh的相关配置,这里我们需要打开debian的sshd配置文件。

那么如何配置debian的系统环境,将语言设置为utf8呢。这里debian的官方文档有十分详细的说明。

https://wiki.debian.org/Locale#SSH_Server

第一步使用dpkg-reconfigure locales命令来配置查看系统语言包。

或是修改/etc/locale.gen文件,将en_US.UTF-8和zh_CN.UTF-8这两行的注释取消。

再使用命令locale-gen命令重新生成后语言包。

然后使用命令locale -a检查系统现在可用的语言包


C
C.UTF-8
en_US.utf8
POSIX
zh_CN.utf8

可以发现中英文的UTF-8语言包都有了,这样无论我们客户端环境,语言是中文还是英文都无所谓,只要是UTF-8版本就可以直接使用mosh来通信。

 

这里还有一个问题,由于sshd_config配置文件中,我将AcceptEnv LANG LC_*选项激活了,就使得远程的terminal客户端可以将自己的本地环境变量LANG值传递到sshd服务器,从而覆写生产服务器debian的LANG配置,如果ssh客户端和服务端的LANG值不相同,那么客户端和服务端是无法通信的,mosh就无法使用。所以,这里不建议将此选项激活,而是注释掉。而实际上debian sshd配置文件,默认该选项是禁用的。

该配置的都已经配置完了,下面使用命令mosh-server检查mosh服务端的状态。

MOSH CONNECT 60001 UGAt9iWfJD0zB37eO9MIlA

[mosh-server detached, pid = 20348]

以上的命令执行结果表明,mosh服务端正常运行。

 

在客户端mosh到服务器,连接正常,从此再也不怕shell卡了。

这里还有一个问题,就是mosh由于在实现方式上与ssh的不同,导致了mosh界面scroll功能只能借助于快捷键上下翻页,而无法使用鼠标滚轮或是触控板。

根据mosh官方的解释,他们目前正在修复这一bug,在下一个版本的mosh中可能会解决这个问题。

安装git

 

brew install git

git config --global user.name "Your Name"
git config --global user.email "[email protected]"

创建版本库

//进入git 目录
cd git-directory
//把目录变成git管理仓库
git init
//把文件添加到版本库,注意可以一次添加多个文件
git add filename.file
//将文件添加到仓库
git commit -m "add a new file to git repository"
//查看仓库当前状态
git status
//查看文件被修改的内容
git diff filename.file
//查看文件被修改的内容后便可以提交文件到仓库
git add filename.file
//显示从最近到最远的提交日志
git log
//以时间线方式显示log
git log --pretty=oneline
//在git中,HEAD表示当前版本号,上个版本是HEAD^,上上个版本是HEAD^^
//往上100个版本HEAD-100,使用reset命令返回上个版本
git reset --hard HEAD^
//回滚到以前特定某个版本
git reset --hard 【commit id】
//Git 版本回退非常快,Git在内部有个指向当前版本HEAD指针,当回退版本的时候,Git仅仅是把HEAD从当前HEAD指向目标HEAD,之后工作区文件便会被更新。
//回滚以前版本,可以使用git log查看提交的历史,以确定回退到哪个版本
//回滚将来的版本,可以用git reflog查看历史命令,以确定要回到将来的版本





首先我想说的是,在windows下配置vagrant环境比linux和mac下要恶心和麻烦的多。

因为vagrant启动默认需要的很多服务,windows下面都没有,比如用来做目录映射的rsync

  1. 下载相关的软件包,这个不必多说。我用的是vagrant+virtualbox。Box最好先下载到本地,然后在使用本地路径而不是url加载,Box基本都host在国外,一个几百MB,下载速度是在是太慢了,光等待的时间就够你烦的了。可以试用国外的远程服务器wget下载,然后再传到网盘里,然后再从网盘里下载。
  2. 确认BIOS的虚拟化选项开启,如果你的BIOS虚拟化选项没有开启,你会发现打开virtualbox你只可以虚拟32位的操作系统,而且安装完vagrant之后,根本就没有办法启动vagrant。
  3. 为了映射本地目录到虚拟机目录,你需要在windows和虚拟机上面都安装rsync。我是先安装的mingw,然后通过mingw安装的rsync到windows。

折腾了一晚上,rsync装好了,但是还需要进一步配置脚本

  1. 我用的是debian-jessie的box,官方文档里说,root用户的默认密码是vagrant,其实是不对的,默认的root账户用该没有设置密码,好在可以sudo -i切换到root然后passwd root设置新的密码了。

Vagrant Box下载

点击到确定的发行版版本页面

https://atlas.hashicorp.com/debian/boxes/jessie64/versions/8.5.2

一般都是如上的链接

在链接后面加入/providers/virtualbox.box

https://atlas.hashicorp.com/debian/boxes/jessie64/versions/8.5.2/providers/virtualbox.box

上面的链接就是最终的下载链接。

以下为symfony.dev:80(默认端口为例)

  • 建立站点目录

一般是位于/var/www/html目录下,建立目录symfony

sudo mkdir symfony
  • 配置apache,映射域名到目录

配置文件一般位于/etc/apache2/sites-available文件夹下

复制000-default.conf,在此文件的基础上修改即可。

sudo cp 000-default.conf symfony.dev.conf
sudo vim symfony.dev.conf

配置文件可以参照如下设置

<VirtualHost symfony.dev:80>  #域名和端口这只
	ServerName symfony.dev  #服务器名,可选
	ServerAdmin webmaster@localhost  #管理员邮箱,可选
	DocumentRoot /var/www/html/symfony  #站点目录,重要

	# Available loglevels: trace8, ..., trace1, debug, info, notice, warn,
	# error, crit, alert, emerg.
	# It is also possible to configure the loglevel for particular
	# modules, e.g.
	#LogLevel info ssl:warn

	ErrorLog ${APACHE_LOG_DIR}/error.log  #错误日志设置,可定制
	CustomLog ${APACHE_LOG_DIR}/access.log combined #访客日志设置,可定制

	# For most configuration files from conf-available/, which are
	# enabled or disabled at a global level, it is possible to
	# include a line for only one particular virtual host. For example the
	# following line enables the CGI configuration for this host only
	# after it has been globally disabled with "a2disconf".
	#Include conf-available/serve-cgi-bin.conf
</VirtualHost>

使用Apache的命令a2ensite来激活站点配置

#注意站点名是sites-available文件夹下的配置文件去除conf后缀
sudo a2ensite symfony.dev
sudo service apache2 reload #不重启Apache就可以使配置站点立即生效

 

  • 修改本地域名解析,实现symfony.dev解析到127.0.0.1
sudo echo "symfony.dev" >> /etc/hostname
sudo echo -e "127.0.0.1\tsymfony.dev" >> /etc/hosts