终端下肉眼看不见的东西

假设test目录下面有a.log、b.log和c.log三个文件:

# ls -1 *.log
a.log
b.log
c.log

现在,我们想要遍历这几个文件,找到关心的信息,例如log文件中是否存在error信息。那么,我们会考虑写一个for循环来处理这个问题:

# for i in `ls -1 *.log | sed '$d'`; do
> grep 'err' $i
> done

非常简单的一个循环,但是执行后会发现以下错误:

grep: a.log: No such file or directory
grep: b.log: No such file or directory
grep: c.log: No such file or directory

非常奇怪,不是*.log这几个文件都在当前目录存在着的吗?为什么这里又找不到呢?
继续阅读

在 Linux 下管理 SSH 连接

工作过程中,往往需要连接到不同的服务器上,有些服务器因为处于特定的集群中,可以方便的通过跳板机跳转过去。而有一些开发机、测试机等零散的服务器就需要凭记忆来记住IP地址。肯定,这种方式非常麻烦,有些客户端可以用来管理 SSH 连接 ,例如Win下的xshell和putty等。

配置 SSH 连接

这里提供一种不需要安装客户端的简单方法,只利用SSH的配置文件(~/.ssh/config)来记录和管理多个 SSH 连接 。关于~/.ssh/config的配置很简单,随便找个手册了解一下就行。 例如,下面定义了连接到 test 这台机器所需的配置:

# Test host
Host test
    HostName 10.1.1.1
    User admin

一旦在配置文件中写好之后,可以简单地通过"ssh test"来连接到相应的服务器,非常简单。

参考Mac OS X 平台有哪些好用的 SSH 客户端? - 知乎,在上面的基础上再添加以下配置:

Host *
    ServerAliveInterval 60
    ControlMaster auto
    ControlPath ~/.ssh/%h-%p-%r
    ControlPersist yes

下面介绍两种方法来利用配置文件管理好 SSH 连接。

继续阅读

Python 日志模块使用

Python内置了很多非常使用的模块,logging 模块是用于控制和输出日志的,它是一个非常强大的日志模块,可以按照不同的需求配置。它的官方文档地址是在这里

我自己也用过几次logging模块,但是前面用的时候都把它用得太复杂了,还自己封装了下。其实,有些代码越是简单越好,写得复杂得反而不容易扩展和使用。

以下是我在脚本中用到的一段代码:

def init_log(log_file):
    '''Initialize logging module.
    '''
    logger = logging.getLogger()
    logger.setLevel(logging.DEBUG)

    formatter = logging.Formatter('[%(levelname)s] %(message)s')

    # Create a file handler to store error messages
    fhdr = logging.FileHandler(log_file, mode = 'w')
    fhdr.setLevel(logging.ERROR)
    fhdr.setFormatter(formatter)

    # Create a stream handler to print all messages to console 
    chdr = logging.StreamHandler()
    chdr.setFormatter(formatter)

    logger.addHandler(fhdr)
    logger.addHandler(chdr)

    return logger

代码的意思很简单,这里就不多加解释了,需要注意的是第5行不能少,要不然会出现问题。因为当logging模块输出的日志分成多个级别,例如DEBUG、INFO、WARN、ERROR等。当没有通过setLevel方法设置日志级别的话,系统默认的日志级别是WARN,也就是说只胡日志级别大于等于WARN的日志才会输出可见,例如ERROR,而日志级别比它低的则被过滤掉了,例如DEBUG。

那为什么这一行不能少呢?因为当一条日志要打印输出时,首先会看这条日志的级别是否大于logging.setLevel设置的级别或者默认的WARN,如果不是则直接过滤掉;如果大于,则会发送给Logger对象绑定的各个handler对象,交由它们进一步处理日志,或者输出到屏幕,或者写入到文件中。而每个handler对象也会有它自己设置的日志级别,然后再进一步地过滤。所以如果不包含第5行,然后StreamHandler对象设置的日志级别是DEBUG的话,其实是没有意义的。

使用方法:

logger = init_logger('test.log')
logger.debug('%s, %s', 'hello', 'world');
logger.info(...)
logger.warn(...)
logger.error(...)

一个奇怪的 echo 结果

昨天经阮哥指点后,写一个检查redis-server的内存消耗的脚本。脚本本身不复杂,因为redis-cli已经提供了查询接口。

# redis-cli -p yourt_port info | grep 'used_memory'

# resuts:
# used_memory:122088016
# used_memory_human:116.43M
# used_memory_rss:128057344
# used_memory_peak:122079336
# used_memory_peak_human:116.42M

一般会选择提取出used_memory_human的结果,但是考虑到万一内存消耗上G了呢?那么这里也要进行一个单位转换,干脆就提取used_memory这个值,以字节为单位的,然后在这个值基础上除以1024*1024换算成MB。

继续阅读

Sed 命令地址匹配问题总结

这个问题来源于ChinaUnix的一篇帖子“sed地址和模式匹配的问题”。

man sed手册说明

Sed默认的命令执行范围是全局的,如果想仅对其中部分行执行命令,可以使用地址限制。在Manual手册中有一节关于地址的描述,摘取部分如下:

Sed commands can be given with no addresses, in which case the command will be executed for all input lines;

Sed默认是全局编辑的,因此如果不明确指定行的话,命令会在所有输入行上执行。

with one address, in which case the command will only be executed for input lines which match that address;

如果指定一个行地址,那么sed命令就限制在那一行执行。

or with two addresses, in which case the command will be executed for all input lines which match the inclusive range of lines starting from the first address and continuing to the second address.

如果给了两个地址,即地址对(或者地址范围),则命令在匹配的这个地址范围内执行。但是需要注意的几点是:

The syntax is addr1,addr2 (i.e., the addresses are separated by a comma); the line which addr1 matched will always be accepted, even if addr2 selects an earlier line; If addr2 is a regexp, it will not be tested against the line that addr1 matched.

继续阅读

什么是交互式登录 Shell

今天在看~/.bashrc文件的时候看到一行,对它的意思不是非常了解:

[[ $- != *i* ]] && return

就特意上Google搜索了一下,对于搜索关键字是符号(这里是$-)的,我是不怎么抱希望的。所以干脆将$-的结果(himBh)放到Google一起搜索。果然找到两篇不错的帖子:

这两个帖子都是在ChinaUnix论坛上的。这些老论坛虽然人气不行,但是一些资源还是很赞的,这些总结帖都非常好。尤其是第一篇非常值得一读,我只是看了其中一部分。对sh和bash的区别并没有仔细看,觉得这些没必要去死记,到时候碰到问题再去理解就好,要不然就太枯燥了。

Linux用了这么多年,Shell一直在用,执行命令,安装软件,配置文件,以及编写脚本,但是说真地,我对Shell一直不是非常了解,几乎没看过和它相关的任务手册和文档。一般系统中最常见的Shell还是Bash。Bash的手册非常长,我是坚决不看的,头痛。后果就是对Bash的一些现象一知半解,其实这些基本上在手册上都是有写的。

继续阅读

使用命令行启动 VirtualBox 虚拟机

装上 VirtualBox 就琢磨着如何让它开机自动启动,又或者能够通过命令行的形式直接启动指定的虚拟机。看了下VirtualBox的官方文档,发现有一个命令可以满足我的需求,即VBoxManage。VBoxManage提供了一系列的虚拟机管理命令,包括创建/删除/启动/修改等等,这里不一一列举。有点像Xen的XM命令。不过这里只关心启动虚拟机的命令:VBoxManage startvm。VBoxManage的完整命令列表可以参考这里

VBoxManage startvm子命令可以开启一台状态为关闭或者保存的虚拟机。该命令的语法为:

VBoxManage startvm  uuid>|name... [--type gui|sdl|headless]

可以通过虚拟机的uuid或者name来指定某台虚拟机,可以通过另外一个子命令list列出系统已有的虚拟机:

$ VBoxManage list vms
"XP" {8842d793-228c-458e-a880-8051193fd2db}

我系统上已经安装了一台名为XP的虚拟机,后面括号内部的是它的UUID。

继续阅读