random tips on coding, ops, ...

扩展 bash 内置命令

一些常用的操作如 sleep,cat 等一般是通过调用单独的外部命令来执行。 在对一些 shell 脚本做优化时,或者让系统进程树更清晰,或者尽可能的减少进程 fork 的次数时总是想尽可能的通过调用 bash 的内置命令来实现类似的功能,在使用内置 命令实现过于复杂时通过编写扩展编译出动态库来扩展 bash 内置命令也是一个办法。

对于 sleep,可以通过 bash 内置的 read 命令的超时功能来实现,如下的例子通过创建 命名管道后读写该管道来实现 sleep 的功能:

1
2
3
4
5
waitfifo=/tmp/fifo-demo-only
[ -p ${waitfifo} ] || mkfifo ${waitfifo}
while true ; do
    read -t 1 <>${waitfifo}
done

其他的功能则需要尽可能的利用 bash 已有的内置命令结合一些技巧来实现。这里我们 介绍另一种方法,通过 bash 内置的 enable 命令的 “-f” 参数能动态加载代码来实现 任意内置命令。还是以 sleep 为例,假设其源代码(附在本文最后)为 sleep.c,参考如下过程演示:

$ enable -n sleep # 禁止 sleep 内置命令,如果有
$ type sleep
sleep 是 /usr/bin/sleep
$ gcc -DHAVE_CONFIG_H -fPIC -shared -Wl,-soname,sleep sleep.c -o sleep.so
$ enable -f ./sleep.so  sleep
$ type sleep
sleep 是 shell 内嵌
$ sleep -h
sleep: 用法:sleep seconds[.fraction]

事实上,对于 Gentoo 系统,只需要对 bash 的 USE flags 加上 plugins 即可拥有 bash 源代码里已经实现了的动态加载内置命令,查看目录 /usr/lib/bash/

$ equery uses bash
...
 * Found these USE flags for app-shells/bash-4.2_p45:
...
 + + plugins      : Add support for loading builtins at runtime via 'enable'
...

对 Ubuntu 则需要先通过 apt-get install bash-builtins 安装相应的头文件后再来 编译相关的代码。

sleep.c 的源代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
/**
 * @file
 * @brief sleep -- sleep for bash builtin
 */
#include <unistd.h>

// for Gentoo, replace bash-plugins w/ bash if Ubuntu
#include <bash-plugins/shell.h>
#include <bash-plugins/builtins.h>

int sleep_builtin (WORD_LIST *list)
{
    long    sec, usec;
    struct timespec tv;

    if (list == 0) {
        builtin_usage();
        return(EX_USAGE);
    }

    if (*list->word->word == '-' || list->next) {
        builtin_usage ();
        return (EX_USAGE);
    }

    if (uconvert(list->word->word, &sec, &usec)) {
        tv.tv_sec = sec;
        tv.tv_nsec = usec * 1000;

        nanosleep(&tv, NULL);
        return(EXECUTION_SUCCESS);
    }

    builtin_error("%s: bad sleep interval", list->word->word);
    return (EXECUTION_FAILURE);
}

static char *sleep_doc[] = {
    "Suspend execution for specified period.",
    "sleep suspends execution for a minimum of SECONDS[.FRACTION] seconds.",
    (char *)NULL
};

struct builtin sleep_struct = {
    "sleep",
    sleep_builtin,
    BUILTIN_ENABLED,
    sleep_doc,
    "sleep seconds[.fraction]",
    0
};

comments powered by Disqus