编程开发 类目

学会编程我们能够和计算机沟通

实用 Shell 文档

Shell 文档

ChinaUnix上大神網中人总结的Shell十三问?强烈推荐,这本书讲得比较精炼,而且都是一些Shell学习中容易把握不住的一些细节难点。每一问都写得非常精彩。

同样是ChinaUnix上,wingger大神整理的Shell基础二十篇。这份文档涉及的内容比较多,我没记错的话应该有很多命令的用法,而且配备实际使用的例子,对初学者帮助甚大。

Shell脚本专家指南也是一本不可多得的好书,我是之前在学校的时候买的这本书。上面都是作者实际工作中的经验总结,你可以从中学到很多其它书上学不到的实践知识。千万不要被专家两个字吓住了,书名的意思是专家给你总结的学习指南。

注:mingxinglai也在他的博客文章shell脚本学习材料种整理了很多实用的资料。

正则表达式

我个人学习正则表达式是从正则表达式30分钟入门教程开始的,这份文档让我了解了正则表达式的一些基础术语和基本的用法。

查看全文

简洁的 Bash Programming 技巧续篇

简洁的 Bash Programming 技巧系列文章专门介绍Bash编程中一些简洁的技巧,帮助大家提高平时 Bash 编程的效率。继上一篇文章发布后,收到很多读者的反响,所以我决定继续将自己学到的一些新的技巧更新在这篇续篇中,当然也希望其它同学也能一起分享你们的技巧。续篇中有部分的内容已经偏离bash编程了,而是命令行下的技巧,题目我暂时不改,请见谅。

1. bash中alias的使用

alias其实是给常用的命令定一个别名,比如很多人会定义一下的一个别名:

alias ll='ls -l'

以后就可以使用ll,实际展开后执行的是ls -l。现在很多发行版都会带几个默认的别名,比如:

alias grep='grep --color=auto'  # 带颜色显示
alias ls='ls --color=auto' # 同上
alias rm='rm -i'  # 删除文件需要确认

alias在某些方面确实提高了很大的效率,但是也是有隐患的,这点可以看我以前的一篇文章终端下肉眼看不见的东西。那么如何不要展开alias,而是用本来的意思呢?答案是使用转义:

\ls
\grep

在命令前面加一个反斜杠后就可以了。

查看全文

简洁的 Bash Programming 技巧

简洁的 Bash Programming 技巧这一系列文章专门介绍Bash编程中一些简洁的技巧,帮助大家提供 Bash 编程的效率,目前该系列已经有三篇文章,有兴趣的同学可以继续阅读其它两篇续篇(一)(二)

下面这几条是我自己在写shell代码的时候,比较喜欢的几种写法,抛砖引玉。

1. 检查命令执行是否成功

第一种写法,比较常见:

echo abcdee | grep -q abcd

if [ $? -eq 0 ]; then
    echo "Found"
else
    echo "Not found"
fi

简洁的写法:

if echo abcdee | grep -q abc; then
    echo "Found"
else
    echo "Not found"
fi

当然你也可以不要if/else,不过这样可读性比较差:

[Sun Nov 04 05:58 AM] [kodango@devops] ~/workspace 
$ echo abcdee | grep -q abc && echo "Found" || echo "Not found"
Found

2. 将标准输出与标准错误输出重定向到/dev/null

第一种写法,比较常见:

grep "abc" test.txt 1>/dev/null 2>&1

常见的错误写法:

grep "abc" test.txt 2>&1 1>/dev/null

查看全文

Bash 获取当前函数名

在C/C++中,__FUNCTION__常量记录当前函数的名称。有时候,在日志输出的时候包含这些信息是非常有用的。而在Bash中,同样有这样一个常量FUNCNAME,但是有一点区别是,它是一个数组而非字符串,其中数组的第一个元素为当前函数的名称。

可能初看有点难以理解,为什么FUNCNAME要是一个数组呢?看看下面的例子,你就明白了。

#!/bin/bash

function test_func()
{
    echo "Current $FUNCNAME, \$FUNCNAME => (${FUNCNAME[@]})"
    another_func
    echo "Current $FUNCNAME, \$FUNCNAME => (${FUNCNAME[@]})"
}

function another_func()
{
    echo "Current $FUNCNAME, \$FUNCNAME => (${FUNCNAME[@]})"
}

echo "Out of function, \$FUNCNAME => (${FUNCNAME[@]})"
test_func
echo "Out of function, \$FUNCNAME => (${FUNCNAME[@]})"

执行后的结果为:

Out of function, $FUNCNAME => ()
Current test_func, $FUNCNAME => (test_func main)
Current another_func, $FUNCNAME => (another_func test_func main)
Current test_func, $FUNCNAME => (test_func main)
Out of function, $FUNCNAME => ()

查看全文

终端下肉眼看不见的东西

假设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这几个文件都在当前目录存在着的吗?为什么这里又找不到呢?
查看全文

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。

查看全文