每天学习一个命令: Linux 查看磁盘信息命令 di

平时在 Linux 上查看磁盘信息都使用 df -lh , -l 显示本地文件系统, -h 来表示 human readable 。虽然 df 在一定程度上能够满足查询磁盘剩余空间的需求,但是这里要介绍一款强大于 df 的命令 —- di 。

使用如下命令安装

sudo apt install di

di 命令是 disk information 的缩写,直接运行 di 会有如下结果

di
Filesystem         Mount               Size     Used    Avail %Used  fs Type
/dev/sda1          /                 901.2G   188.3G   667.1G   26%  ext4   
tmpfs              /dev/shm            7.8G     0.1G     7.6G    2%  tmpfs  
tmpfs              /run                1.6G     0.1G     1.5G    4%  tmpfs  
cgmfs              /run/cgmanager/     0.1M     0.0M     0.1M    0%  tmpfs  
tmpfs              /run/lock           5.0M     0.0M     5.0M    0%  tmpfs  
tmpfs              /run/user/0         1.6G     0.0G     1.6G    0%  tmpfs  
tmpfs              /run/user/1000      1.6G     0.0G     1.6G    0%  tmpfs  
tmpfs              /sys/fs/cgroup      7.8G     0.0G     7.8G    0%  tmpfs  
/dev/sda1          /var/lib/docker   901.2G   188.3G   667.1G   26%  ext4   

di 默认的输出就是比较人性化的了。

看 di 的使用介绍 man di 就会发现 di 是这么介绍自己的

> di Displays usage information on mounted filesystems.  Block values are reported in a human readable format.  If the user or group has  a  disk  quota,  the  values  reported  are adjusted according the quotas that apply to the user.

一些简单的使用

A 选项打印所有挂载设备

di -A
Mount                fs Type Filesystem 
	Options                                                           
	    Size     Used     Free %Used  %Free 
	    Size     Used    Avail %Used  %Free 
	    Size     Used    Avail %Used  
	   Inodes     IUsed     IFree %IUsed
/                    ext4    /dev/sda1  
	rw,relatime,errors=remount-ro,data=ordered                        
	  901.2G   188.3G   712.9G   21%    79%  
	  901.2G   234.1G   667.1G   26%    74%  
	  855.4G   188.3G   667.1G   22%  
	 60014592   1372538  58642054    2% 
/dev/shm             tmpfs   tmpfs      
	rw,nosuid,nodev                                                   
	    7.8G     0.1G     7.6G    2%    98%  
	    7.8G     0.1G     7.6G    2%    98%  
	    7.8G     0.1G     7.6G    2%  
	  2036725       741   2035984    0% 
/run                 tmpfs   tmpfs      
	rw,nosuid,noexec,relatime,size=1629380k,mode=755                  
	    1.6G     0.1G     1.5G    4%    96%  
	    1.6G     0.1G     1.5G    4%    96%  
	    1.6G     0.1G     1.5G    4%  
	  2036725       777   2035948    0% 
/run/cgmanager/fs    tmpfs   cgmfs      
	rw,relatime,size=100k,mode=755                                    
	    0.1M     0.0M     0.1M    0%   100%  
	    0.1M     0.0M     0.1M    0%   100%  
	    0.1M     0.0M     0.1M    0%  
	  2036725        14   2036711    0% 
/run/lock            tmpfs   tmpfs      
	rw,nosuid,nodev,noexec,relatime,size=5120k                        
	    5.0M     0.0M     5.0M    0%   100%  
	    5.0M     0.0M     5.0M    0%   100%  
	    5.0M     0.0M     5.0M    0%  
	  2036725         4   2036721    0% 
/run/user/0          tmpfs   tmpfs      
	rw,nosuid,nodev,relatime,size=1629380k,mode=700                   
	    1.6G     0.0G     1.6G    0%   100%  
	    1.6G     0.0G     1.6G    0%   100%  
	    1.6G     0.0G     1.6G    0%  
	  2036725         4   2036721    0% 
/run/user/1000       tmpfs   tmpfs      
	rw,nosuid,nodev,relatime,size=1629380k,mode=700,uid=1000,gid=1000 
	    1.6G     0.0G     1.6G    0%   100%  
	    1.6G     0.0G     1.6G    0%   100%  
	    1.6G     0.0G     1.6G    0%  
	  2036725        36   2036689    0% 
/sys/fs/cgroup       tmpfs   tmpfs      
	rw,mode=755                                                       
	    7.8G     0.0G     7.8G    0%   100%  
	    7.8G     0.0G     7.8G    0%   100%  
	    7.8G     0.0G     7.8G    0%  
	  2036725        18   2036707    0% 
/var/lib/docker/aufs ext4    /dev/sda1  
	rw,relatime,errors=remount-ro,data=ordered                        
	  901.2G   188.3G   712.9G   21%    79%  
	  901.2G   234.1G   667.1G   26%    74%  
	  855.4G   188.3G   667.1G   22%  
	 60014592   1372538  58642054    2% 

c 选项逗号分割

使用 -c 选项分割输出

di -c
s,m,b,u,v,p,T
"/dev/sda1","/","901.2G","188.3G","667.1G",26%,"ext4"
"tmpfs","/dev/shm","7.8G","0.1G","7.6G",2%,"tmpfs"
"tmpfs","/run","1.6G","0.1G","1.5G",4%,"tmpfs"
"cgmfs","/run/cgmanager/fs","0.1M","0.0M","0.1M",0%,"tmpfs"
"tmpfs","/run/lock","5.0M","0.0M","5.0M",0%,"tmpfs"
"tmpfs","/run/user/0","1.6G","0.0G","1.6G",0%,"tmpfs"
"tmpfs","/run/user/1000","1.6G","0.0G","1.6G",0%,"tmpfs"
"tmpfs","/sys/fs/cgroup","7.8G","0.0G","7.8G",0%,"tmpfs"
"/dev/sda1","/var/lib/docker/aufs","901.2G","188.3G","667.1G",26%,"ext4"

c 是 --csv-output 的缩写,为了便于程序解析

t 参数增加统计行

-t 参数在最后增加统计行

di -t
Filesystem         Mount               Size     Used    Avail %Used  fs Type
/dev/sda1          /                 901.2G   188.4G   667.0G   26%  ext4   
tmpfs              /dev/shm            7.8G     0.1G     7.6G    2%  tmpfs  
tmpfs              /run                1.6G     0.1G     1.5G    4%  tmpfs  
cgmfs              /run/cgmanager/     0.1M     0.0M     0.1M    0%  tmpfs  
tmpfs              /run/lock           5.0M     0.0M     5.0M    0%  tmpfs  
tmpfs              /run/user/0         1.6G     0.0G     1.6G    0%  tmpfs  
tmpfs              /run/user/1000      1.6G     0.0G     1.6G    0%  tmpfs  
tmpfs              /sys/fs/cgroup      7.8G     0.0G     7.8G    0%  tmpfs  
/dev/sda1          /var/lib/docker   901.2G   188.4G   667.0G   26%  ext4   
                   Total               1.8T     0.4T     1.3T   26%      

s 参数对结果排序

di -s 默认更具 mount point 输出

  • di -sm 默认 mount pont
  • di -sn 不排序,按照挂载表 /etc/fstab 中顺序
  • di -ss 按照特殊设备
  • di -st 根据 filesystem type
  • di -sr 逆序输出

排序方式可以组合使用,如:di –stsrm :按照类型、设备、挂载点逆序排序。 di –strsrm :按照类型、设备逆序、挂载点逆序排序。

f 选项自定义格式

di -fM
Mount               
/                   
/dev/shm            
/run                
/run/cgmanager/fs   
/run/lock           
/run/user/0         
/run/user/1000      
/sys/fs/cgroup      
/var/lib/docker/aufs

只打印 mount point

更多的 f 的选项可以直接参看 man di

总结

虽然 di 提供了比 df 更多更强大的功能,但是也有其缺点,大部分的 Linux 发行版默认是没有预装的。


2017-10-16 linux , 磁盘管理 , disk , df

SonarQube continuous code quality

关键字提取,开源,代码质量管理,多语言支持。

SonarQube is an open-source platform developed by SonarSource for continuous inspection of code quality to perform automatic reviews with static analysis of code to detect bugs, code smells, and security vulnerabilities on 20+ programming languages.

开源协议:Lesser GNU General Public License

SonarQube 可以从以下七个维度检测代码质量,而作为开发人员至少需要处理前 5 种代码质量问题

  • 不遵循代码标准 SonarQube 可以通过 PMD,CheckStyle,Findbugs 等等代码规则检测工具规范代码编写。
  • 潜在的缺陷 SonarQube 可以通过 PMD,CheckStyle,Findbugs 等等代码规则检测工具检测出潜在的缺陷。
  • 糟糕的复杂度分布 文件、类、方法等,如果复杂度过高将难以改变,这会使得开发人员难以理解它们,且如果没有自动化的单元测试,对于程序中的任何组件的改变都将可能导致需要全面的回归测试。
  • 重复 显然程序中包含大量复制粘贴的代码是质量低下的,SonarQube 可以展示源码中重复严重的地方。
  • 注释不足或者过多 没有注释将使代码可读性变差,特别是当不可避免地出现人员变动时,程序的可读性将大幅下降 而过多的注释又会使得开发人员将精力过多地花费在阅读注释上,亦违背初衷。
  • 缺乏单元测试 SonarQube 可以很方便地统计并展示单元测试覆盖率。
  • 糟糕的设计 通过 SonarQube 可以找出循环,展示包与包、类与类之间的相互依赖关系,可以检测自定义的架构规则 通过 SonarQube 可以管理第三方的 jar 包,可以利用 LCOM4 检测单个任务规则的应用情况, 检测耦合。

reference


2017-10-11

jenkins setup and introduction

Jenkins 是什么

Jenkins 是一个独立的开源自动化服务器,可用于自动化各种任务,如构建,测试和部署软件。Jenkins 可以通过本机系统包 Docker 安装,甚至可以通过安装 Java Runtime Environment 的任何机器独立运行。

Jenkins 有什么作用

用于持续、自动构建,测试项目,监控外部任务运行等。

模板类型

新建模板类型

  • Freestyle project 基础功能,执行构建任务
  • Pipeline,真实工作环境可能会包含,代码检查,编译,单元测试,部署等等任务,这个模板就是串行执行任务
  • Multi-configuration project job 跑在不同的机器上

问题

master/slave 节点配置

  • https://www.howtoforge.com/tutorial/ubuntu-jenkins-master-slave/

2017-10-10 jenkins , ci-cd , ci

mockito 使用

单元测试的目的是在不涉及依赖的情况下测试代码(隔离)。

Target and challenge of unit testing

Unit test 目标是针对一个模块或者一段代码隔离测试,应该消除其他类,或者系统依赖带来的副作用。

测试替身(Test Doubles)用来消除这类副作用,test Doubles 可以分为:

  • dummy object 传入不使用
  • fake object 有基本实现,但是非常简单,比如内存数据库
  • stub class 带着特殊目的的接口或者类的部分实现
  • mock object 接口或者类的虚假实现,可以用来定义固定的输出,mock object 在测试中用来执行特定的行为

通常情况下可以通过手工代码来 mock objects 或者使用 mock framework 来模拟类的行为。Mockito 是一个非常流行的 Mock framework,使用 Mockito 的三段论:

  • Mock 外部依赖,插入 mock 代码
  • 执行 unit test
  • 验证输出

Maven

http://search.maven.org 中搜索 a:"mockito-core" 或者 g:"org.mockito"

启用注解

第一种方法,使用 JUnit 的 @RunWith

@RunWith(MockitoJUnitRunner.class)
public class MockitoAnnotationTest {
    ...
}

或者用代码启用

@Before
public void init() {
    MockitoAnnotations.initMocks(this);
}

@Mock 注解

最常用的注解就是 @Mock 注解,使用 @Mock 注解来创建和插入 mocked 实例,这样就省去了手动调用 Mockito.mock() 方法。

@Spy 注解

@Spy 注解可以 mock 真实对象的方法,让真实对象方法被调用时,就像 mock object 一样可以被配置。

@Captor 注解

参数捕获器,用于捕获 mock 方法参数,进行验证使用

@InjectMocks 注解

@InjectMocks 注解会自动将 mock fields 注入到被测试的 object 中。

when thenReturn

通过 when().thenReturn() 方法链可以用来指定一个方法调用预先定义好的返回值。这个方法也可以指定抛出一个异常

Properties properties = mock(Properties.class);

when(properties.get(”Anddroid”)).thenThrow(new IllegalArgumentException(...));

try {
    properties.get(”Anddroid”);
    fail(”Anddroid is misspelled”);
} catch (IllegalArgumentException ex) {
    // good!
}

thenReturn vs thenAnswer 区别

当 mock 方法是知道确定的返回值,那么可以使用 thenReturn 或者 doReturn,方法会 mock 一个确定的返回值。

thenReturn(T value) Sets a return value to be returned when the method is called.

Answer 当需要根据不同条件来 mock 方法并且返回不同返回值时需要 Answer,比如需要根据方法传入参数来返回对应的返回值的情况。

reference


2017-09-27 mockito , unit-test , java , mock

从 Clonezilla 恢复系统学习 Linux 启动过程

最近又一次使用 Clonezilla 来克隆系统,和以往不同的是,这一次我是备份了整块硬盘到镜像,然后从镜像恢复系统到另外一块硬盘,而不是以往是复制一个分区,所以又产生了一些问题,所以有了这篇文章,一方面来记录一下中间遇到的问题,另一方面也学习巩固一下关于 Linux 启动过程中的必要流程。

基础知识

关于最基础的计算机启动过程也就不展开说,阮一峰,和网上大部分文章已经讲的非常清晰了,这里只简单的列举一些基础名词,必须要知道的概念。

BIOS

全称是 Basic Input Output System,也就是一块被写入开机程序的只读内存,电脑通电之后第一时间会读取的芯片。

POST

POST 不是 HTTP 请求方法的 POST,而是 Power On Self Test,开机自检,在通电之后 BIOS 加载后会自动执行。

MBR

MBR 是主引导分区,全称是 Master Boot Record,该分区决定该设备是否能够启动。如果设备能够启动,那么该设备第一个扇区,512 字节就需要表明该设备能够启动。

主引导记录告诉计算机去哪一块硬盘寻找操作系统,主引导记录由三部分组成,1-446 字节,调用系统机器码,447-510 字节,分区表,511-512 字节,主引导记录签名 (0x55,0xAA)。

mbr

GRUB

GRUB 是 Linux 下最流行的启动管理器,全称是 GRand Unified Bootloader。计算机在读取 MBR 前 446 字节机器码之后,将运行事先安装的启动管理器 boot loader 也就是 GRUB。

GRUB 设计兼容 multiboot specification,为了使得 GRUB 能够引导启动各种版本的 Linux。GRUB 也能够让用户选择从不同的 kernels 启动。Grub 的配置文件地址在 /boot/grub/grub.conf.

GRUB1 现在正在被 GRUB2 代替,Grub2 在 /boot/grub2/grub.cfg 配置。

所以总结来看,计算机通电之后,先通过 BIOS,到 MBR,通过 GRUB 引导操作系统启动。以上就是计算机 boot sequence 的过程。

System startup

通过 GRUB 引导启动操作系统,之后操作系统就会接管系统启动,操作系统启动过程也分为很多步骤。

Stage 1.0

BIOS 在 MBR 中搜索 boot record,因为主引导分区必须保证尽量小,所以通过 stage 1.0 找到 1.5 GRUB 引导,GRUB 引导必须在 boot record 和第一个分区之间,将 GRUB 加载到内存之后进入 stage 1.5.

Stage 1.5

上面提到 GRUB 必须在引导记录和磁盘第一个分区之间,这一块区域因为历史原因被预留,磁盘的第一个分区从 sector 63 开始,MBR 在 sector 0,所以留下了 62512 byte sectors,大概 31774 bytes,用来保存 core.img, core.img 只有 25389 字节,所以完全足够。

需要注意的是,/boot 目录需要存放在支持 GRUB 的文件系统上。stage 1.5 在找到 /boot 文件和加载必要的驱动之后执行。

Stage 2

GRUB stage 2 阶段会加载 Linux kernel 到内存中,kernel 和相关的文件在 /boot 目录中。kernel 文件都以 vmlinuz 开头。

kernel 都是压缩的格式以节省空间,当计算机解压并加载内核到内存之后,会加载 systemd,自此系统启动过程结束,内核和 systemd 都已经在运行。

systemd 是所有进程的创始者,他负责将系统启动至一个可工作的状态,他负责包括挂载系统文件,启动管理系统服务等等工作。

systemd 启动流程

首先 systemd 会尝试使用 /etc/fstab 中定义的内容挂载文件系统,包括定义的根分区,swap 分区文件等等。

# /etc/fstab: static file system information.
#
# Use 'blkid' to print the universally unique identifier for a
# device; this may be used with UUID= as a more robust way to name devices
# that works even if disks are added and removed. See fstab(5).
#
# <file system> <mount point>   <type>  <options>       <dump>  <pass>
# / was on /dev/sda1 during installation
UUID=3d1b7e3e-c184-4664-9555-2b088997f2c8 /              ext4    errors=remount-ro 0       1
# swap was on /dev/sda5 during installation
UUID=b99bf592-a25b-4ca0-b597-fc62e121aae1 none          swap    sw            0    0

关于 systemd 的启动顺序,可以参考阮一峰的这篇

修改 GRUB

默认情况下 GRUB 会有 10 秒时间来给用户选择系统,这个设置可以通过修改 /etc/default/grub 来修改。

GRUB_DEFAULT=0          # 默认启动的系统序号
GRUB_TIMEOUT=4          # 默认等待多久以预设系统开机

更新之后使用 sudo update-grub 来更新 GRUB。

GRUB Manual 手册中提供了完整的参数解释。

遇到的问题

No bootable device

使用 Clonezilla 复制硬盘所有分区到另外一块硬盘,而我这边遇到的情况是复制结束之后硬盘没有 boot 分区,导致 BIOS 无法找到主引导分区。

解决办法是使用 Clonezilla 的专家(高级)模式,在高级模式中会自动修复 grub 的问题

无法启动 Linux Mint 桌面

这个问题表现形式可能是各种各样的,开机黑屏,或者在 grub 引导之后出现各种乱码命令。对于这个问题的解决方法可能需要是修改 /etc/fstab ,将其中硬盘的 UUID ,通过 sudo blkid 查看获取后保证 /etc/fstab 中启动的硬盘是一块。可以参考之前的文章

reference


2017-09-25 linux , boot , mbr , uefi , bios , clonezilla

每天学习一个命令:使用 grep 查找文件内字符串

一直都知道 grep 很强大,但是一直都没有办法来定义它,直到看到 man 介绍第一行,非常简洁精炼 “print lines matching a pattern”,一下子就知道了 grep 的作用。

grep 全称是 Global Regular Expression Print。grep 的工作方式是这样的,它在一个或多个文件中搜索字符串模板。如果模板包括空格,则必须被引用,模板后的所有字符串被看作文件名。搜索的结果被送到标准输出,不影响原文件内容。

grep 可用于 shell 脚本,因为 grep 通过返回一个状态值来说明搜索的状态,如果模板搜索成功,则返回 0,如果搜索不成功,则返回 1,如果搜索的文件不存在,则返回 2。我们利用这些返回值就可进行一些自动化的文本处理工作。

grep 名字的由来

在 unix 早期的编辑器中,如果要查找比如 junk 这个单词,需要输入 /junk/p 来打印找到的一个行,如果要找所有行,就使用 g/junk/p,g 是 global search 的意思。

这个功能被独立出来作为了命令,只做一件事情,就是全局搜索,并打印,叫做 grep,其实是 g/regular expression/p 的缩写,可以理解为 g/re/p,在 vi 或者 sed 中类似的功能也经常能见到。

必须知道的使用方式

下面这些选项非常常用,记住:

-i 忽略大小写
-v invert match
-n 行号

-B NUM -A NUM
-C NUM

使用实例

比如说在当前目录下,搜索文件中包含的 password 词

grep password *

grep 会自动在当前目录下搜索并将包含 password 行打印出来,所以千万不要在本地文件中存放密码一类的敏感信息,这一无异于将密码写在显示器上。

忽略大小写匹配

使用 -i 来忽略大小写

grep -i password *

这一行命令会匹配,比如 Password,PASSWORD 等等。

将 grep 作为过滤器来过滤标准输出的内容

cat /etc/passwd | grep 'sshd'

强制让 grep 输出文件名

grep 'sshd' /etc/passwd /dev/null

这时 grep 会打印文件名,冒号,结果

显示行号

-n 参数会显示行号

grep -n pattern file.txt

显示不包含正则的行

正常情况下 grep 会过滤出匹配 正则的行,使用 -v 参数反之,显示不包含正则的行

grep -v 'junk' *

再比如,如果想要设置 every 但是不想搜索 everyoneeverybodyeverywhere 可以使用

grep every * | grep -v one | grep -v body | grep -v where

显示匹配行前后的行

grep -B 10 -A 20 pattern file.txt

输出匹配 PATTERN 行的前 10 行和 后 20 行。

打印一行中正则匹配的部分

很多时候一行日志中有非常多的内容,我们往往只关心特定的部分,这个时候可以使用 grep 的正则来过滤出我们关心的部分,比如日志中的耗时我们可能会打印出 cost 10ms 这样的内容

grep -o -P "cost [0-9]+ms"

说明:

  • -o 表示只输出匹配到的内容,每一行显示一个
  • -P "regex" 表示开启正则过滤

当然如果组合使用 sed 也能够做到同样的事情。

扩展

在 grep 之上,后人又开发了很多很有用的命令,比如不解压的情况下搜索压缩包中的内容,再比如简便版的 ack-grep

reference


2017-09-23 grep , linux , egrep , command , regular

jinja2 笔记

jinja2 是基于 Python 的模板引擎。

种类

注意下面的 \ 是因为模板需要转义,使用时需要去掉,另外大括号和 % 之间的空格也需要去掉。

\{\% ... \%\}  Statements 控制结构,比如 if/elif/else for-loops,macros,block 等等
\{\{ ... \}\}  Expressions 表达式,用来输出结果
\{\# ... \#\}  Comments 注释
\#  ... \#\#  Line Statements

表达式及过滤器

\{\{ name \}\} 结构表示一个变量

过滤器用法

Hello, \{\{ name|capitalize \}\}

常用的过滤器

过滤器 说明
safe 渲染值时不转义
capitalize 把值的首字母转换成大写,其他字母转换成小写
lower 把值转换成小写形式
upper 把值转换成大写形式
title 把值中每个单词的首字母都转换成大写
trim 把值的首尾空格去掉
striptags 渲染之前把值中所有的 HTML 标签都删掉
length 输出长度

完整的过滤器清单可以在文档中找到:http://jinja.pocoo.org/docs/templates/#builtin-filters

控制结构

用来控制流程

常见的 if-else

{ % if user % }
    Hello, !
{ % else % }
    Hello, Stranger!
{ % endif % }

for 循环

<ul>
{ % for comment in comments % }
    <li></li>
{ % endfor % }
</ul>

宏,类似方法

{ % macro render_comment(comment) % }
    <li></li>
{ % endmacro % }

<ul>
{ % for comment in comments % }
    
{ % endfor % }
</ul>

保存到文件中使用 import

{ % import 'macros.html' as macros % }
<ul>
    { % for comment in comments % }
        
    { % endfor % }
</ul>

需要多次使用的模板可以单独放在文件中,然后 使用 include

{ % include 'common.html' % }

模板继承,先定义父模板

<html>
<head>
    { % block head % }
        <title>{ % block title % }{ % endblock % } - My Application</title>
    { % endblock % }
</head>
<body>
{ % block body % }
{ % endblock % }
</body>
</html>

然后继承

{ % extends "base.html" % }
{ % block title % }Index{ % endblock % }
{ % block head % }
    
    <style>
    </style>
{ % endblock % }
{ % block body % }
    <h1>Hello, World!</h1>
{ % endblock % }

表单

WTForms 支持的字段类型

字段类型 说明
StringField 文本字段
TextAreaField 多行文本字段
PasswordField 密码文本字段
HiddenField 隐藏文本字段
DateField 文本字段,值为 datetime.date 格式
DateTimeField 文本字段,值为 datetime.datetime 格式
IntegerField 文本字段,值为整数
DecimalField 文本字段,值为 decimal.Decimal
FloatField 文本字段,值为浮点数
BooleanField 复选框,值为 True 和 False
RadioField 一组单选框
SelectField 下拉列表
SelectMultipleField 下拉列表,可选择多个值
FileField 文件上传字段
SubmitField 表单提交按钮
FormField 把表单作为字段嵌入另一个表单
FieldList 一组指定类型的字段

WTForms 支持的验证函数

验证函数 说明
Email 验证电子邮件地址
EqualTo 比较两个字段的值;常用于要求输入两次密码进行确认的情况
IPAddress 验证 IPv4 网络地址
Length 验证输入字符串的长度
NumberRange 验证输入的值在数字范围内
Optional 无输入值时跳过其他验证函数
Required 确保字段中有数据
Regexp 使用正则表达式验证输入值
URL 验证 URL
AnyOf 确保输入值在可选值列表中
NoneOf 确保输入值不在可选值列表中

在模板中使用

<form method="POST">
\{\{ form.hidden_tag() \}\}
\{\{ form.name.label \}\} \{\{ form.name(id='my-text-field') \}\}
\{\{ form.submit() \}\}
</form>

2017-09-22 python , flask , jinja2 , template , web

Python 笔记之赋值语句和表达式

赋值语句比较简单,Learning Python 这本书中对赋值语句介绍比较详细,分类也讲述的比较细,这篇文章就只简单的记录一些容易混乱的知识点,并不记录所有赋值语句需要注意的点。

Augmented assignment and shared references

在之前的文章中就交代过共享引用是需要特别注意的,在 augmented assignment 中也是

>>> L = [1, 2]
>>> M = L                           # L M 共享同一个对象
>>> L = L + [3, 4]                  # Concatenation 会创建新的对象
>>> L, M                            # 所以他们的值不相同
([1, 2, 3, 4], [1, 2])

>>> L = [1, 2]
>>> M = L                           # Share
>>> L += [3, 4]                     # 但是 `+=` 其实用的是 extend 方法,所以是原地修改
>>> L, M
([1, 2, 3, 4], [1, 2, 3, 4])

Expression Statements and In-Place Changes

表达式同样也分为很多种

>>> L = [1,2]
>>> L.append(3)
>>> L
[1, 2, 3]

不过需要注意的是,比如 append() 方法并没有返回值,所有返回的 L 会是 None

>>> L = L.append(4)
>>> L
None

2017-09-20 python , notes , assignment , expression

Maven 介绍

Maven 是一个项目管理工具,主要用于项目构建,依赖管理,项目信息管理。自动化构建过程,从清理、编译、测试和生成报告、再到打包和部署。Maven 通过一小段描述信息来管理项目。

Maven 安装

安装之前先把 JDK 环境配置好。

Debian/Ubuntu/Linux Mint 下

sudo apt install maven

如果要手动安装则按照下面步骤,选择一个合适的版本

  • 下载 Maven 最新安装包,地址 http://maven.apache.org/download.cgi 比如 apache-Maven-3.3.9-bin.tar.gz
  • tar zxvf apache-Maven-3.3.9-bin.tar.gz
  • 将 apache-Maven-3.3.9 目录移动到 /opt/ 目录 命令: sudo mv apache-maven-3.3.9/ /opt/
  • 创建链接 sudo ln -s /opt/apache-maven-3.3.9/ /opt/maven
  • root 身份修改配置命令 sudo vi ~/.bashrc 在文件最后添加:

      #set Maven environment
      #export Maven_OPTS="-Xms256m -Xmx512m"
      export M2_HOME=/opt/maven
      export M2=$M2_HOME/bin
      export PATH=$M2:$PATH
    

    保存并关闭。

  • 使配置生效必须重启机器或者在命令行输入: source ~/.bashrc
  • 查看 Maven 是否安装成功: mvn -version

如果进行了上面步骤在任意目录中 mvn 命令不能使用,可以在 /etc/profile 文件后面加入下面三行 sudo vim ~/.bashrc

然后输入以下内容

Maven_HOME=/usr/local/apache-maven-3.3.9
export Maven_HOME
export PATH=${PATH}:${Maven_HOME}/bin

设置好 Maven 的路径之后,需要运行下面的命令 source ~/.bashrc 使刚刚的配置生效

Maven 作用

Maven 最熟悉的一个概念就是 POM,Maven 项目会有一个 pom.xml 文件, 在这个文件里面添加相应配置,Maven 就会自动帮你下载相应 jar 包

<dependency>
  <groupId>com.google.firebase</groupId>    项目名
  <artifactId>firebase-admin</artifactId>   项目模块
  <version>5.3.1</version>                  项目版本
</dependency>

项目名 - 项目模块 - 项目版本 三个坐标定义了项目在 Maven 世界中的基本坐标,任何 jar,pom, war 都是基于这些坐标进行区分的。

  • groupId 定义了项目组,组和项目所在组织或公司,或者开源项目名称,一般为公司域名反写,比如 com.google.firebase 等等。Maven 项目和实际项目并不一定是一对一关系,比如 SpringFramework 实际项目,对应的 Maven 项目会有很多,spring-core, spring-context 等等,更进一步推荐 groupId 应当定义项目隶属的实际项目,如果定义到组织或者公司,那么一个组织下可能会有很多实际项目,造成混乱
  • artifactId 定义了 Maven 项目的名称,在组中的唯一 ID,在同一个项目中可能有不同的子项目,可以定义不同的 artifactId,可以理解为 Maven 项目的模块。artifactId 也是构建完成项目后生成的 jar 包或者 war 包的文件名的一部分。
  • version 顾名思义,就是项目的版本号,如果项目维发布,一般在开发中的版本号习惯性加上 SNAPSHOT, 比如 1.0-SNAPSHOT

根据上面的例子,比如上面定义的 Maven 坐标,可以在对应的中央仓库中 https://repo1.maven.org/maven2/com/google/firebase/firebase-admin/5.3.1/ 目录下找到对应的文件。

  • scope 定义了依赖范围,如果依赖范围为 test ,那么该依赖只对测试有效,也就是说在测试代码中引入 junit 有效,在主代码中用 junit 会造成编译错误。如果不声明依赖范围则默认为 compile ,表示该依赖对主代码和测试代码都有效。

Maven 有以下几种依赖范围:

  • compile 编译依赖,在编译、测试、运行时都有效
  • test 测试依赖,只对于测试 classpath 有效, JUnit 典型
  • provided 已提供依赖,只在编译和测试有效,运行时无效, servlet-api 编译和测试项目时需要该依赖,但是在运行项目时,由于容器已经提供,不需要 Maven 重复引入
  • runtime 运行时依赖,对于测试和运行有效,编译主代码无效, JDBC 驱动实现,项目主代码编译只需要 JDK 提供的 JDBC 接口,只有执行测试或者运行项目才需要实现上述接口的具体 JDBC 驱动
  • system 系统依赖范围,和 provided 范围依赖一致,但是使用 system 范围的依赖时必须通过 systemPath 元素显示地指定依赖文件的路径。
  • import 导入依赖,一般不用

依赖范围和 classpath 的关系

依赖范围 Scope 编译 classpath 有效 测试 classpath 有效 运行 classpath 有效 例子
compile Y Y Y spring-core
test - Y - junit
provided Y Y - servlet-api
runtime - - Y JDBC 驱动
system Y Y - 本地的, Maven 仓库之外的类库

Maven 依赖调解 Dependency Mediation ,第一原则:路径最近者优先;第二原则:第一声明者优先

SNAPSHOT 快照版本只应该在组织内部的项目或者模块之间的依赖使用,组织对于这些快照版本的依赖具有完全的理解和控制权。项目不应该依赖于任何组织外部的快照版本,由于快照的不稳定性,依赖会产生潜在的危险,即使项目构建今天是成功的,由于外部快照版本可能变化,而导致未来构建失败。

Maven 核心概念 仓库

在上面介绍 Maven 的作用的时候提到了 Maven 的两个核心概念:坐标和依赖,这也是 Maven 首要解决的问题。这里要引入 Maven 另外一个核心概念:仓库。 Maven 时间中通过坐标来定位依赖,Maven 通过仓库来统一管理这些依赖。

Maven 项目的每一个构件对应着仓库的路径为: groupId/artifactId/version/artifactId-version.packageing

Maven 的仓库分为远程仓库和本地仓库,当第一次运行 Maven 命令时,需要网络连接,从远程仓库下载可用的依赖和插件。而当以后在运行 Maven 命令的时候,Maven 会自动先检查本地 ~/.m2/repository 目录下的依赖,如果本地有缓存优先从本地获取,在找不到的情况下去远程仓库寻找。

常用的在线 maven 依赖查看网站

Maven 核心概念 生命周期和插件

Maven 的生命周期是抽象的,实际行为都有插件完成。

clean 生命周期

清理项目,包含三个阶段

  • pre-clean 执行清理前需要完成的工作
  • clean 清理上一次构建生成的文件
  • post-clean 执行一次清理后需要完成的工作

default 生命周期

定义了真正构建时所需要执行的步骤

  • validate 验证工程是否正确,所有需要的资源是否可用。
  • initialize
  • generate-sources
  • process-sources 主要资源文件
  • generate-resources
  • process-resources
  • compile 编译主源码
  • process-classed
  • generate-test-sources
  • process-test-sources 测试资源
  • generate-test-resources
  • process-test-resources
  • test-compile 编译项目测试代码
  • process-test-classes 已发布的格式,如 jar,将已编译的源代码打包
  • prepare-package
  • pre-integration-test 在集成测试可以运行的环境中处理和发布包
  • post-integration-test
  • verify 运行任何检查,验证包是否有效且达到质量标准。
  • install 将包安装到 Maven 本地仓库,供本地 Maven 项目使用
  • deploy 复制到远程仓库,供其他人使用

site 生命周期

建立和发布项目站点

  • pre-site 生成项目站点之前需要完成的工作
  • site 生成站点文档
  • post-site 生成之后
  • site-deploy 生成的站点发布到服务器上

具体的流程可以参考下图

Maven 生命周期

Maven 项目文件结构

下面是一个标准的 Maven 工程

src/main/java - 存放项目.java 文件;
src/main/resources - 存放项目资源文件;
src/test/java - 存放测试类.java 文件;
src/test/resources - 存放测试资源文件;
target - 项目输出目录;
pom.xml - Maven 核心文件(Project Object Model);

Maven 常用命令

mvn archetype:create 创建 Maven 项目
mvn compile 编译源代码
mvn deploy 发布项目
mvn test-compile 编译测试源代码
mvn test 运行应用程序中的单元测试
mvn site 生成项目相关信息的网站
mvn clean 清除项目目录中的生成结果
mvn package 根据项目生成的 jar
mvn install 在本地 Repository 中安装 jar
mvn eclipse:eclipse 生成 eclipse 项目文件
mvn jetty:run 启动 jetty 服务
mvn tomcat:run 启动 tomcat 服务
mvn clean package -DMaven.test.skip=true 清除以前的包后重新打包,跳过测试类
mvn clean package 清除以前的包后重新打包

简单例子

创建一个 Maven 项目

mvn

reference


2017-09-20 maven , java , build , management

IntelliJ IDEA 中使用 Resin 调试

平时开发环境使用的是 jetty,而 Java Web 有一个更好更快的服务器 Resin,这篇文章就来说一下什么是 Resin,以及在 Debug 中如何使用。

什么是 Resin

Resin 是一个提供高性能的,支持 Java/PHP 的应用服务器。目前有两个版本:一个是 GPL 下的开源版本,提供给一些爱好者、开发人员和低流量网站使用;一种是收费的专业版本,增加了一些更加适用于生产环境的特性。

Resin 也可以和许多其他的 web 服务器一起工作,比如 Apache Server 和 IIS 等。Resin 支持 Servlets 2.3 标准和 JSP 1.2 标准。熟悉 ASP 和 PHP 的用户可以发现用 Resin 来进行 JSP 编程是件很容易的事情。Resin 支持负载平衡,可以增加 WEB 站点的可靠性。方法是增加服务器的数量。比如一台 Server 的错误率是 1% 的话,那么支持负载平衡的两个 Resin 服务器就可以使错误率降到 0.01%。到目前为止,Resin 对 WEB 应用的支持已经远远超过 Tomcat 等各种大型的 Server。

Resin 安装

在 Resin 的官方 quick start 教程中有各大平台详细的安装指导。我在使用 apt 安装时没有成功,这里就记录下手工安装的过程。

http://caucho.com/download 网址下载, Resin 有两个版本, Pro 版和 GPL 开源版,个人使用开源基础版本已经足够。安装过程如下:

  1. 安装 JDK 6 或者以上版本,将 java 可执行程序配置到环境变量 JAVA_HOME
  2. tar -vzxf resin-4.0.x.tar.gz 根据下载的最新版本解压
  3. cd resin-
  4. ./configure 更多参数参考 configure options
  5. make
  6. sudo make install
  7. 执行 sudo resinctl start, 或者运行 java -jar lib/resin.jar start
  8. 浏览 http://localhost:8080

可以使用 sudo resinctl stop 来停止运行,更多内容可以参考官方指南

IntelliJ IEDA 中使用 Resin 调试

第一步,添加 Resin Local 选项,在 IDEA 中 Run/Debug Configuration 中添加 Resin Local 选项

resin-local

点击 configure 按钮,在弹出窗 Application Servers 中选择部分一中安装的 Resin 目录路径和目录下 Resin 的配置文件路径。

resin-configure

Run/Debug Configurations 中 Server 页面配置,基本都是默认。

Run/Debug Configurations 中 Deployment 页面配置,注意红色方框部分选择。选择 resin.xml 而不是 JMX 否则项目的 index 路径是 localhost:8080/appname/ 而不是 localhost:8080/

resin-deplyment

在 Resin 的服务器配置下 Depolyment 中 Depolyment method:有 JMX 和 resin.xml 两种选择,JMX 是把项目打包的文件放在 resin 服务器下 webapp 下 只有在服务器启动时 才把项目给拷贝过去 无法在 intellij 中实时更新;resin.xml 是在 C 盘 Temp 目录下 copy 了一份 resin.xml 的配置文件 然后把服务器目录空间指向了你的项目工作空间 可以实现 IntelliJ 修改实时更新。IntelliJ 默认的选择是 JMX 所以我们要选中 resin.xml 模式。同时当项目 Artifacts 指向的目录是 ROOT 时 上图中的 Use default context name(always true if depolyment method is JMX) 取消勾选

执行 build,得到 war 文件。执行 resin run/debug,会自动在你选择的浏览器中打开项目 index 页面。也可以在 IDEA 下方的 Application Servers 面板中进行 Resin 的启动,停止等操作。Resin 启动的打印信息也在此窗口显示。

run-resin


2017-09-19 Resin , IntelliJ , Java

电子书

最近文章

  • Vim 插件之全局搜索:ack.vim 这篇文章看开始陆陆续续记录一下用过的 Vim Plugin,虽然有些一直也在用但从没有好好整理过,正好这篇开一个计划吧。
  • 我可能要抛弃用了很多年的 Chrome 换用 Vivaldi 大概一两年前就听说了 Vivaldi 这样一款浏览器,它使用 Chromium 做内核,创始人是从 Opera 浏览器出来的,不满 Chrome 横扫浏览器市场,没有给 Opera 一点喘息的机会,很多 Opera 的特性在 Chrome 上都被摘掉了。所以他们就开始搞了这一个 Vivaldi 浏览器,我记得当时听到的第一个宣传语是,快,想来 Chrome 最初打开市场时的宣传语也是快,但是多少年过去了 Chrome 虽然自身保持非常干净,但是加上自定义的扩展,也变得越来越臃肿了,但倒是也在一个可以接受的范围内。两年前 Vivaldi 依靠这一条宣传没有吸引到我,但是我也下载尝试,并且这两年来一直存在我的电脑中。我记得之前在我看来最大的缺点便是不能同步数据,而这个功能也在之前的更新中被加上了。所以今天就非常值得拿出来讲一讲。
  • 使用 port knocking 隐藏 SSH daemon 端口 暴露在互联网上的服务器非常容易被恶意程序进行端口扫描,以前也整理过一篇 VPS 安全设置 的文章,但都是一些比较基础的设置,能够绕过一些简单的端口扫描,但是并不能从根本上解决端口扫描的问题。
  • 威联通折腾篇十四:迁移系统盘 当时安装系统的时候就直接插入了一块硬盘,安装在了第一块机械硬盘上面,虽然读写也没有遇到什么瓶颈,但是记录以做备份,可以用于将系统迁移到 SSD 上。
  • Intellij IDEA 支持 jQuery 在 设置中进行如下操作开启 jQuery 支持: