Linux 判断一个命令是否存在

在命令行时,我们常使用 which xxx 或者 whereis xxx 来判断某个命令是否存在。然而在编写 bash 时,应该避免使用 which 命令。

原因是 which 做为一个外部的工具,并不一定存在,在发行版之间也会有区别,有的系统的 which 命令不会设置有效的 exit status,存在一定的不确定性。

Bash 提供的内建命令如 hash、type、command 可以达到要求。例如:

$ command -v foo >/dev/null 2>&1 || { echo >&2 "I require foo but it's not installed.  Aborting."; exit 1; }
$ type foo >/dev/null 2>&1 || { echo >&2 "I require foo but it's not installed.  Aborting."; exit 1; }
$ hash foo 2>/dev/null || { echo >&2 "I require foo but it's not installed.  Aborting."; exit 1; }

编写方法时,可以这么编写:

gnudate() {
    if hash gdate 2>/dev/null; then
        gdate "$@"
    else
        date "$@"
    fi
}

参考资料


Docker run命令

Docker会在隔离的容器中运行进程。当运行 docker run 命令时,Docker会启动一个进程,并为这个进程分配其独占的文件系统、网络资源和以此进程为根进程的进程组。

docker run可以控制一个容器运行时的行为,它可以覆盖docker build在构建镜像时的一些默认配置,这也是为什么run命令相比于其它命令有如此多的参数的原因。

命令格式

最基本的docker run命令的格式如下:

$ sudo docker run [OPTIONS] IMAGE[:TAG] [COMMAND] [ARG...]

docker run [OPTIONS]可以让用户完全控制容器的生命周期,并允许用户覆盖执行docker build时所设定的参数,甚至也可以修改本身由Docker所控制的内核级参数。

OPTIONS总起来说可以分为两类:

  1. 设置运行方式:

    • 决定容器的运行方式,前台执行还是后台执行 -d -a -t -i
    • 设置containerID –name –cidfile
    • IPC(命名空间) –ipc
    • 设置网络参数
    • 设置容器的CPU和内存参数;
      • 设置权限和LXC参数;
  2. 设置镜像的默认资源,也就是说用户可以使用该命令来覆盖在镜像构建时的一些默认配置。

先对照几个例子:

$ docker run -d -p 54285:54285 oddrationale/docker-shadowsocks -s 0.0.0.0 -p 54285 -k yourpasswd -m aes-256-cfb
$ docker run -d --name web -p 8080:80 joshhu/webdemo

前台vs后台 -d -a -t -i

当我们启动一个容器时,首先需要确定这个容器是运行在前台还是运行在后台。

如果在docker run后面追加-d=true或者-d,那么容器将会运行在后台模式。此时所有I/O数据只能通过网络资源或者共享卷组来进行交互。因为容器不再监听你执行docker run的这个终端命令行窗口。但你可以通过执行docker attach来重新附着到该容器的回话中。

在前台模式下(不指定-d参数即可),Docker会在容器中启动进程,同时将当前的命令行窗口附着到容器的标准输入、标准输出和标准错误中:

-a=[]               : Attach to `STDIN`, `STDOUT` and/or `STDERR`
-t=false            : Allocate a pseudo-tty
--sig-proxy=true : Proxify all received signal to the process (non-TTY mode only)
-i=false            : Keep STDIN open even if not attached

如果在执行run命令时没有指定-a参数,那么Docker默认会挂载所有标准数据流,包括输入输出和错误,你可以单独指定挂载哪个标准流。

$ sudo docker run -a stdin -a stdout -i -t ubuntu /bin/bash

如果要进行交互式操作(例如Shell脚本),那我们必须使用-i -t参数同容器进行数据交互。但是当通过管道同容器进行交互时,就不需要使用-t参数,例如下面的命令:

echo test | docker run -i busybox cat

容器识别 –name –cidfile

  1. Name –name

    可以通过三种方式为容器命名:

    1. 使用UUID长命名(”f78375b1c487e03c9438c729345e54db9d20cfa2ac1fc3494b6eb60872e74778”)
    2. 使用UUID短命令(”f78375b1c487”)
    3. 使用Name(“evil_ptolemy”)

    这个UUID标示是由Docker deamon生成的。如果你在执行docker run时没有指定–name,那么deamon会自动生成一个随机字符串UUID。但是对于一个容器来说有个name会非常方便,当你需要连接其它容器时或者类似需要区分其它容器时,使用容器名称可以简化操作。

  2. PID –cidfile

    如果在使用Docker时有自动化的需求,你可以将containerID输出到指定的文件中(PIDfile),类似于某些应用程序将自身ID输出到文件中,方便后续脚本操作。 –cidfile=””: Write the container ID to the file

Image[:tag]

当一个镜像的名称不足以分辨这个镜像所代表的含义时,你可以通过tag将版本信息添加到run命令中,以执行特定版本的镜像。例如: docker run ubuntu:14.04

命名空间 –ipc

默认情况下,所有容器都开启了IPC命名空间。

--ipc=""  : Set the IPC mode for the container,
        'container:<name|id>': reuses another container's IPC namespace
        'host': use the host's IPC namespace inside the container

IPC(POSIX/SysV IPC)命名空间提供了相互隔离的命名共享内存、信号灯变量和消息队列。

共享内存可以提高进程数据的交互速度。共享内存一般用在数据库和高性能应用(C/OpenMPI、C++/using boost libraries)上或者金融服务上。如果需要容器中部署上述类型的应用,那么就应该在多个容器直接使用共享内存了。

网络设置

默认情况下,所有的容器都开启了网络接口,同时可以接受任何外部的数据请求。

-h HOSTNAME or --hostname=HOSTNAME --配置容器主机名
--link=CONTAINER_NAME:ALIAS --添加到另一个容器的连接
--net=bridge|none|container:NAME_or_ID|host --配置容器的桥接模式
-p SPEC or --publish=SPEC --映射容器端口到宿主主机
-P or --publish-all=true|false --映射容器所有端口到宿主主机
--dns=[]         : Set custom dns servers for the container
--add-host=""    : Add a line to /etc/hosts (host:IP)
--mac-address="" : Sets the container's Ethernet device's MAC address

例如开篇的例子:

$ docker run -d --name web -p 8080:80 joshhu/webdemo

-p 8080:80 把 host 的 8080端口流量转发到 web 的80端口。

Clean up (–rm)

默认情况下,每个容器在退出时,它的文件系统也会保存下来,这样一方面调试会方便些,因为你可以通过查看日志等方式来确定最终状态。另外一方面,你也可以保存容器所产生的数据。但是当你仅仅需要短暂的运行一个容器,并且这些数据不需要保存,你可能就希望Docker能在容器结束时自动清理其所产生的数据。

注意:–rm 和 -d不能共用!

--rm=false: Automatically remove the container when it exits (incompatible with -d)

安全设置 –security-opt

--security-opt="label:user:USER"   : Set the label user for the container
--security-opt="label:role:ROLE"   : Set the label role for the container
--security-opt="label:type:TYPE"   : Set the label type for the container
--security-opt="label:level:LEVEL" : Set the label level for the container
--security-opt="label:disable"     : Turn off label confinement for the container
--secutity-opt="apparmor:PROFILE"  : Set the apparmor profile to be applied  to the container

性能设置 -m -c

-m="": Memory limit (format: <number><optional unit>, where unit = b, k, m or g)
-c=0 : CPU shares (relative weight)

运行时特权 LXC设置

  --cap-add: Add Linux capabilities
  --cap-drop: Drop Linux capabilities
  --privileged=false: Give extended privileges to this container
  --device=[]: Allows you to run devices inside the container without the --privileged flag.
  --lxc-conf=[]: (lxc exec-driver only) Add custom lxc options --lxc-conf="lxc.cgroup.cpuset.cpus = 0,1"

重写Dockerfile

这些参数中,有四个是无法被覆盖的:FROM、MAINTAINER、RUN和ADD,其余参数都可以通过docker run进行覆盖。

  1. CMD (Default Command or Options)
  2. ENTRYPOINT (Default Command to Execute at Runtime)
  3. EXPOSE (Incoming Ports)
  4. ENV (Environment Variables)
  5. VOLUME (Shared Filesystems)
  6. USER
  7. WORKDIR   

参考资料


更新Linux内核至4.9

aptitude install initramfs-tools
apt-get install --reinstall grub

mkdir /boot/grub

Searching for GRUB installation directory ... found: /boot/grub
Probing devices to guess BIOS drives. This may take a long time.
Unknown partition table signature
Unknown partition table signature
Searching for default file ... Generating /boot/grub/default file and setting the default boot entry to 0
Searching for GRUB installation directory ... found: /boot/grub
Testing for an existing GRUB menu.lst file ...


Generating /boot/grub/menu.lst
Searching for splash image ... none found, skipping ...
Found kernel: /boot/vmlinuz-4.10.0-041000-generic
Found kernel: /boot/vmlinuz-3.16.0-4-amd64
Updating /boot/grub/menu.lst ... done

参考资料


github的辅助项目们

时常在github项目上看到类似下面这样的小标签,用于展示项目的一些信息。

今天稍微接触了一下这些(小)项目。目前还没有在blog上使用,过段时间会尝试一下。

Shields.io

提供高质量Github项目进度角标图片的API.shields.io 。引用起来也非常简单

<div id="container">
    <img src="http://progressed.io/bar/28?title=progress" alt="">
    <img src="http://progressed.io/bar/30?title=nice" alt="">
    <img src="http://progressed.io/bar/60?title=极客标签" alt="">
    <img src="https://img.shields.io/teamcity/http/teamcity.jetbrains.com/s/bt345.svg" alt="">
    <img src="https://img.shields.io/pypi/dw/Django.svg" alt="">
    <img src="https://img.shields.io/badge/soul-GBTAG-red.svg" alt="">
</div>
<script type="text/javascript" src="http://cdn.gbtags.com/jquery/1.11.1/jquery.min.js"></script>

显示出来就是这个样子:

总的说来就是显示项目构建、下载、版本号、sns情况和其他五花八门的信息显示图标。

gitter.im

Gitter.im是一款支持Markdown的针对开发者的即时通讯软件。

  • Gitter.im 基于 Github 进行构建,紧密地集成到您的 organisations, repositories, issues 和 activity。
  • Gitter.im还提供与 Trello, Jenkins, Travis CI, Heroku, Sentry, BitBucket, HuBoard, Logentries, Pagerduty 以及 Sprintly 的集成。同样支持自定义的 webhook ,为集成提供了开源库以及灵活的API。
  • 支持MarkDown语法
  • 免费用户就可拥有 无限制的公开及私密聊天室数量
  • 免费用户就可拥有 无限制的历史聊天记录
  • 拥有Macos,Linux,Windows,苹果IOS,安卓Andriod APP客户端,还有几十个第三方APP。

Go Report Card

Go_Report_Card,一个可以为你的开源 Go 代码生成质量报告的网站,挺不错的,利用的常见的几个工具,golint, go vet等。

microbadger

microbadger是一个管理你的Docker镜像的工具。

Coveralls

使用coveralls在代码自动化测试时统计测试覆盖率

CI持续集成平台

appveyor

随着SaaS的兴起,appveyor把持续集成搬到了云端,我们无需架设自己的CI服务器,只需注册一个账号,然后把GitHub, BitBucket或者TFS 连上AppVeyor就可以了

travis-ci.org

Travis CI 是目前新兴的开源持续集成构建项目,它与jenkins,GO的很明显的特别在于采用yaml格式,简洁清新独树一帜。目前大多数的github项目都已经移入到Travis CI的构建队列中。Travis-CI 使用 PostgreSQL 数据库。

circleci

CircleCI 是又一个持续集成平台。

参考资料


转载 | Composer 入门

前言: 最近总结了好多和 composer 相关的文章。这一篇对 composer 做个简单介绍。文章大部分来源于Composer 中文文档

对于现代语言而言,包管理器基本上是标配。Java 有 Maven,Python 有 pip,Ruby 有 gem,Nodejs 有 npm。PHP 的则是 PEAR,不过 PEAR 坑不少:

依赖处理容易出问题
配置非常复杂
难用的命令行接口

composer 也是 PHP 用来管理依赖关系的工具。它做的如此之好,以至于如果你只是纯粹地进行 php 开发,在平时使用中对几乎可以忽略 composer。 你可以在自己的项目中声明所依赖的外部工具库,Composer 会帮你安装这些依赖的库文件。它实际上包含了两个部分:ComposerPackagist

Composer 是由 Jordi Boggiano 和 Nils Aderman 创造的一个命令行工具,它的使命就是帮你为项目自动安装所依赖的开发包。Composer 包含了一个依赖解析器,用来处理开发包之间复杂的依赖关系;另外,它还包含了下载器、安装器等有趣的东西。 Packagist 是 Composer 的默认的开发包仓库。你可以将自己的安装包提交到 packagist,当你在自己的 Git 仓库中新建了 tag 或更新了代码,packagist 都会自动构建一个新的开发包。

安装 Composer

Composer 需要 PHP 5.3.2+ 才能运行。

$ curl -sS https://getcomposer.org/installer | php

这个命令会将 composer.phar 下载到当前目录。PHAR(PHP 压缩包)是一个压缩格式,可以在命令行下直接运行。

你可以使用 –install-dir 选项将 Composer 安装到指定的目录,例如:

$ curl -sS https://getcomposer.org/installer | php -- --install-dir=bin

当然也可以进行全局安装:

$ curl -sS https://getcomposer.org/installer | php
$ mv composer.phar /usr/local/bin/composer

在 Mac OS X 下也可以使用 homebrew 安装:

brew tap josegonzalez/homebrew-php  
brew install josegonzalez/php/composer  

不过通常情况下只需将 composer.phar 的位置加入到 PATH 环境变量就可以,不一定要全局安装。

声明依赖

在项目目录下创建一个 composer.json 文件,指明依赖,比如,你的项目依赖 monolog:

{
    "require": {
        "monolog/monolog": "1.2.*"
    }
}

安装依赖

安装依赖非常简单,只需在项目目录下运行:

composer install   如果没有全局安装的话,则运行:

php composer.phar install  

自动加载

Composer 提供了自动加载的特性,只需在你的代码的初始化部分中加入下面一行:

require 'vendor/autoload.php';  

模块仓库

packagist.org 是Composer的仓库,很多著名的 PHP 库都能在其中找到。你也可以提交你自己的作品。

高级特性

Composer 还有一些高级特性,虽然不是必需的,但是往往能给 PHP 开发带来方便。

几个小技巧

1. 仅更新单个库

composer update foo/bar  

2. 不编辑composer.json的情况下安装库

你可能会觉得每安装一个库都需要修改composer.json太麻烦,那么你可以直接使用require命令。

composer require "foo/bar:1.0.0"  

这个方法也可以用来快速地新开一个项目。init命令有–require选项,可以自动编写composer.json:(注意我们使用-n,这样就不用回答问题)

$ composer init --require=foo/bar:1.0.0 -n
$ cat composer.json
{
    "require": {
        "foo/bar": "1.0.0"
    }
}

3. create-project

composer create-project doctrine/orm path 2.2.0  

这会自动克隆仓库,并检出指定的版本。克隆库的时候用这个命令很方便,不需要搜寻原始的URI了。

4. 考虑缓存,dist包优先

最近一年以来的Composer会自动存档你下载的dist包。默认设置下,dist包用于加了tag的版本,例如”symfony/symfony”: “v2.1.4”,或者是通配符或版本区间,”2.1.*“或”>=2.2,<2.3-dev”(如果你使用stable作为你的minimum-stability)。

dist包也可以用于诸如dev-master之类的分支,Github允许你下载某个git引用的压缩包。为了强制使用压缩包,而不是克隆源代码,你可以使用install和update的–prefer-dist选项。

下面是一个例子(我使用了–profile选项来显示执行时间):

$ composer init --require="twig/twig:1.*" -n --profile
Memory usage: 3.94MB (peak: 4.08MB), time: 0s

$ composer install --profile
Loading composer repositories with package information  
Installing dependencies  
  - Installing twig/twig (v1.12.2)
    Downloading: 100%

Writing lock file  
Generating autoload files  
Memory usage: 10.13MB (peak: 12.65MB), time: 4.71s

$ rm -rf vendor

$ composer install --profile
Loading composer repositories with package information  
Installing dependencies from lock file  
  - Installing twig/twig (v1.12.2)
    Loading from cache

Generating autoload files  
Memory usage: 4.96MB (peak: 5.57MB), time: 0.45s  

这里,twig/twig:1.12.2的压缩包被保存在~/.composer/cache/files/twig/twig/1.12.2.0-v1.12.2.zip。重新安装包时直接使用。

5.为生产环境作准备

在部署代码到生产环境的时候,别忘了优化一下自动加载:

composer dump-autoload --optimize   安装包的时候可以同样使用--optimize-autoloader。不加这一选项,你可能会发现20%到25%的性能损失。

如果你需要帮助,或者想要了解某个命令的细节,你可以阅读官方文档或者中文文档,也可以查看JoliCode做的这个交互式备忘单

参考资料


php 数组增加元素的方法 array_push 和 array_merge

本文的起因是今天需要给一个数组添加成员,一个是无序数组,直接往里扔就可以,另一个是key-value形式的数组。

无序数组只要array_push即可,key-value数组如果数量少,也可以用 $data['pussy'] = 'wagon'形式。

array_push(array,value1,value2…)

参数	描述
array	必需。规定数组。
value1	必需。规定要添加的值。
value2	可选。规定要添加的值。

例子:

$config = require_once __DIR__ . '/../vendor/xxx.php';
array_push($config['providers'],
    xxx\Providers\HttpClientServiceProvider::class,
    xxx\Providers\SmsServiceProvider::class,
    xxx\Providers\QrCodeServiceProvider::class,
    \Intervention\Image\ImageServiceProvider::class,
    xxx\Providers\ImageServiceProvider::class,
    xxx\Providers\WordSegmentServiceProvider::class,
    xxx\Providers\WechatServiceProvider::class,
    xxx\Providers\RecommendServiceProvider::class,
    Ignited\LaravelOmnipay\LaravelOmnipayServiceProvider::class
);

array_merge(array1,array2,array3…)

参数	描述
array1	必需。规定数组。
array2	可选。规定数组。
array3	可选。规定数组。

例子接上:

$config['aliases'] = array_merge($config['aliases'], [
        'HttpClient' => xxx\Facades\HttpClient::class,
        'Sms' => xxx\Facades\Sms::class,
        'QrCode' => xxx\Facades\QrCode::class,
        'Image' => \Intervention\Image\Facades\Image::class,
        'ImageUpload' => xxx\Facades\Image::class,
        'WordSegment' => xxx\Facades\WordSegment::class,
        'Recommend' => xxx\Facades\Recommend::class,
        'Wechat' => xxx\Facades\Wechat::class,
        'Omnipay' => Ignited\LaravelOmnipay\Facades\OmnipayFacade::class
    ]
);