Programming - yanbin's Blog

简单 Shell 编程 FAQ(〇)

0.sed 命令如何使用 shell 脚本某个变量的值?
  使用 '“'括起表达式,或者即不用 '‘' 也不用 '“' 括起表达式。
  'expression'  是使用 sed 时常用/常见的方式。
  可以用 "expression" 的形式给 sed 指定表达式参数。
  shell 对待参数的方式:
  1)使用 '‘'  括起来的字符以它的文本形式对待。$ 就是 $ 这个字符,不会对待为展开变量的值;
  2)使用 '“' 括起来的 '$ '用于展开变量的值,'\' 用来转义,其它字符仍然以文本形式对待;
  3)执行命令时指定参数而不用'‘' 或'“'  括起来与使用 '“'类似;
# '$' 在这里用于匹配 '$'字符。
sed 's/A $foobar value/foobar/g' foobar.txt
# $ 展开 foobar 这个变量的值。转义的 \$ 匹配 '$'。
# 参数在传递给 sed 程序时已经完成变量值展开和转义了。
# 完成转义和变量展开的是 shell 而不是 sed.
sed "s/A $foobar \$value/foobar/g" foobar.txt
# 这个与用 '“'括起来的效果是相同的。
sed s/A $foobar \$value/$/g foobar.txt
 
1.sed 如何从文件中直接删除一行或多行?
  使用 sed 的 d 命令。
# 不熟悉 sed 时一般会写这样的代码。这种方式容易出错且耗费资源。
cat foobar.txt | sed 's/patter to match//gp' > tmp_file.txt
mv tm_file.txt foobar.txt
# sed 的 d 命令 加 -i 参数 可以完成直接修改文件的操作。
sed -i '/pattern to match/d' foobar.txt
# 只输未匹配即没有被删除的行,而不修改文件
sed 'pattern to match/d' foobar.txt
  -i[SUFFIX], --in-place[=SUFFIX]
  edit files in place (makes backup if SUFFIX supplied)
# -i 参数接受一个可选的 suffix, 指定这个 suffix, sed 会修改文件前备份文件
sed -i.bak '/pattern to match/d' foobar.txt
 

继续阅读

shell 脚本中,程序的标准输出重定向到 FIFO, 需要注意的问题

FIFO, 又称为命名管道 (named pipes)。与 PIPE 不同和相同之处:
1. PIPE 只能用于关联进程之间,比如: 父子进程之间,两个子进程之间。
2. 关于读写:
   (a) 写一个没有读端的 PIPE,会触发 SIGPIPE(Broken pipe: write to pipe with no readers).
   (b) 写一个没有读端的 FIFO,没有设置 O_NONBLOCKING 会阻塞,直到有进程读;
        设置了 O_NONBLOCKING, 将会返回 -1.
   (c) 没有写端时读 PIPE/FIFO 都是会阻塞,而设置了 O_NONBLOKING 读都会返回 -1.
 
由于 FIFO 没有读端写端会阻塞的特性。
在 shell 脚步中,一个程序的标准输出重定向到 FIFO, 即写 FIFO 的程序启动之时或之后,
确保读 FIFO 的程序是否存在并且及时存在了,否则可能会有如下问题:
1. 读 FIFO 的程序没有启动过,写 FIFO 的程序永远阻塞在 FIFO 操作上。
2. 读 FIFO 的程序后来启动了,写 FIFO 的程序可能丢失了大量数据当其阻塞在写 FIFO 操作之上时。
3. 假如写 FIFO 的程序是一个多线程程序,她的产生数据操作写数据操作是分开,并且有内部数据缓存,
   当读 FIFO 的程序启动较晚时,会有大量数据要读,而且写 FIFO 程序也有大量数据要写,
   这至少会耗费时间。
4. 实在要考虑写 FIFO 程序读 FIFO 程序的启动先后顺序问题,要考虑时间窗口。
5. Linux Programmer's Manual, pipe(7) 给出的设计思想:
   Applications should not rely on a particular capacity: an application should be designed so that
   a reading process consumes data as soon as it is available, so that a writing process does not
   remain blocked.

shell脚本使用 timeout + wait 完成: 超时退出执行,等待执行完毕并处理执行结果

具体需求是:
1.从文件中读取 seq, 使用 pub 程序将 seq 推送给定阅读了 cmd topic 的 peer client.
   client 将处理结果(message)推送到 cmdresp topic. 这个过程是经过 server 的,可以不考虑。
   pub 成功后记录 seq.
2. sub 程序订阅 cmdresp topic, 简单处理接收到的消息,并且记录到文件。
3. 对比 pub 成功的 seq 记录与接收到的 message, 获得一个 pub/sub 成功率。
 
问题是:
1. pub 和 sub 分别使用两个不同的管道,pub cmd 成功后并不等待 cmdresp.
   甚至可以说: pub 程序并不知道 cmdresp 的存在,甚至不知道 sub 的存在.
2. sub 是阻塞的:没有任何消息也不退出。
3. pub message 到 peer client 到 cmdresp 返回之间的时间是不确定的。
   消息在两个管道都有可能出现延迟。
4. sub 程序本身没有超时退出选项。
 
伪代码是:
timeout time sub cmdresp_topic > cmdresp_record
while seq in read seqs:
     seq = parse_seq(seq)
     message = create_message(seq)
     pub cmd_topic message
     record(message, msg_record)
     // do something, maybe sleep 1s

wait sub

parse(cmdresp_record, msg_record)

shell 代码是:

继续阅读

Lua 调用的 C 函数保存 state 的两种方式: Storing State in C Functions 笔记

使用 Lua call C 编程,不免需要保存一些 non-local data 在 C 函数中; non-local data 来自 Lua state.
然而,直接使用 C static/global 变量保存 state 是有问题的: (a) C 变量不能保存一个 generic Lua value;
(b) static/global 变量的方式也无法用于 multiple Lua states, 因为不同 Lua state 调用相同函数/库时,
需要 C 函数保存的 state 是独立不相关的。
 
C API 提供了两个地方来保存 non-local data: registry 和 upvalues.
 
Registry
Registry 是一个 global table, 只能在 C 函数中使用 Lua C API 提供的函数和 pseudo-index 访问。
pseudo-index 就像一个普通的 stack index 一样,不同的是它所关联的 value 并不真正的存放在 stack 之上。
访问 registry 的 psedu-index 定义 LUA_REGISTRYINDEX. 对 registry 的访问就像是对 stack 访问一样,
Lua C API 提供的大多数函数都可以用于 registry —— 指定 LUA_REGISTRYINDEX 作为 index ——,
只有 lua_remove(), lua_insert() 这些操作 stack 本身的函数除外。例如:
lua_getfiled(L, LUA_REGISTRYINDEX, "SOME_KEY");
 
既然 registry 是一个 regular table, 那么就可以用所有 index 访问它,当然 nil 是不行的。
使用 registry  index 的选用一定要谨慎,毕竟 registry 是全局的,而且所有库和函数都可以共享的 table;
为避免 key 冲突,应当:
(a) 不要使用 number 作为 key;
(b) 使用 string 作为 key, 尽量加上一些特殊的前缀,比如: LIBRAY_NAME_ 这样的;
(c) 使用 reference system;
 
Reference system 是 auxilibrary 提供的一系列函数,使用这些函数可以保证存储 value 到 table
无须关心 key 的唯一性。如下方式可创建一个新的 reference:
int r = luaL_ref(L, LUA_REISTRYINDEX);
这个函数 pop 一个 value 从 stack, 并用新的 index 保存到指定的 table 中,这里 LUA_REISTRYINDEX
关联到 registry, 所以这个调用可保存一个 value 到 registry.
luaL_ref() 函数会将 key 返回,用于以后的对 table 的访问以及 release reference.
luaL_rawgeti(L, LUA_REGISTRYINDEX, r);
luaL_unref(L, LUA_REGISTRYINDEX, r);
 
指定给 luaL_ref() 的 value 是 nil —— 或者说栈顶的 valu 为 nil ——,luaL_ref() 返回 LUA_REFNIL,
使用 LUA_REFNIL 调用 luaL_unref() 和 lua_rawgeti() 都没有任何效果,算得上人畜无害。
另外一个 reference system 定义的常量是 LUA_NOREF, 这个常量保证不同于任何 valid reference,
可用来判断一个 reference 是否为 invalid reference.
 
reference system 有用之处就在于: 我们不能使用 C pointers 指向任何一个 lua object(包括 string),
这样的引用是未定义行为,但是我们可以使用 reference system 将对象存储在 registry 中,
并且维护这个 reference, 就像使用 C pointer 一样。
 
另外一种选择 key 的方式是:使用 C library 中的 static 变量的地址作为 key(的创建源)。
C 链接器可以保证这些变量的地址是唯一的, 常量的大概不行,比如:字符串常量,宏定义的常量。
不过直接用 static 变量的地址作wei index 是错的,得玩点黑魔法:
lua_pushlightuserdata();或 lua_rawsetp(), lua_rawgetp();

继续阅读

为什么使用 do {} while(0)

有些宏定义含有多行代码,一般使用 do {} while(0) 把代码放在 'do' 和 'while' 之间的 '{}' 中。

#define foorbar(msg, callback) do {\
         struct Task __task = msg_to_task((msg)); \
           if (__task != NULL) { \
              process(__task, (callback)); \
              char *__msg = task_to_msg(__task); \
              if (__msg != NULL) \ {
                 send_msg(__msg); \
                 free(__msg); \
              } \
              destroy_task(__task); \
           } while (0)

这样用的原因是:
1.符合 C/C++ 语法习惯。
每条语句后面都一个';', 而 do {} while 语句后面也要加一个 ';'.

2.避免出现语法错误。
不用 do {} while(0) 把多行代码包起来,在 if else 语句中就会有语法错误,例如:

#define foorbar(a, b) foor((a)); bar((b))
if (something)
  /* 以下有语法错误 */
  foorbar(3, 2);
else
  // do something

仅仅使用 '{}' 把多行代码包起来,如果在调用宏之后加 ';', 也会有语法错误。

#define foorbar(a, b) {\
           foor((a)); bar((b));\
       }
foorbar(3, 2); // 此处有语法错误

/* 编译器提示:
 * error: expected ‘;’ before ‘}’ token
 * 如果不加 ';', 不会有语法错误但是这样不符合 C/C++ 的语法习惯
 */

3.do {} while(0) 可以根据条件跳出执行。

#define foorbar() do {\
        struct condition __cond; \
        if (__cond.wait_cond()) \
           break; // 条件发生退出执行 \

        // 条件没有发生 do something 
      } while(0)

 

4.私以为 do {} while(0) 可以保证代码执行并且只执行一次。

5.需要注意的地方。
(a)宏定义时用 '\' 连接多行语句;
(b)宏定义中定义变量,注意与外部变量名字冲突,不然原本希望用外面的变量,却用了新定义的变量。
(c)有些编译器会优化掉 do {} while(0); 直接展开 '{}' 内的代码, 如(b)所描述,此时会出现语法错误。
   FIXME: 如果内部有 'break' 并且 'break' 的执行依赖运行时条件,编译器就不会优化掉 do {} while(0); 了。
举例:

#define foorbar() do {\
        struct condition cond; \
        if (cond.wait_cond()) \
           break; // 条件发生退出执行 \

        // 条件没有发生 do something 
      } while(0)

struct condition cond;
// do something
foobar(); // 到底用的是哪一个 cond?
#define foorbar(a, b) do {\
         const char *something = get_something(a, b); \
      } while(0)

const char *something;
// do something

foorbar(3, 9); // 如果编译器优化掉了 do {} while(0); 这里有语法错误。

 

感谢 老猫,mike2,MovableType@源赖朝 三位网友。

参考:
do { … } while (0) — what is it good for?
do{}while(0) 的作用



 

Linux 系统中使用 inotify 监视文件或目录的改变

0.注意事项

int inotify_init();
int inotify_add_watch(int fd, const char *name);
int inotify_rm_watch(int fd, int wd);
int inotify_init1(int mask);
 
这些是一系列的 linux 系统调用;
前三个是 linux 2.6.13 引入的, 最后一个是 2.6.27 引入的。

但是一些 C libray(C 语言实现库), 并没有定义这些系统调用的封装。
可以用 syscall(__NR_inotify_init); 这样的形式调用。
__NR_inotify_init 是系统调用号,可以在 unistd.h 中看到。

有些 SDK 中的内核配置没有默认的选定对 inotify 的支持。
可以在 linux 配置中的 kernel setup:
fs-->
   [] support inotify for user space
选上对些系统调用的支持。

如果内核没有对这些系统调用的支持,
int fd = syscall(inotify_init) 总是返回 89,
read(fd, buff, sizeof(buff)) 会返回 -1, errno 被设置为 "Bad file descriptor"。

inotify 会监视目录下所有文件。
inotify 并不自动的递归监视目录下的子目录,需要程序员自己完成这样的工作。

 

1.简介

使用这些 API 可以监视文件系统的 events.
当文件或者目录有改变时,内核产生 inotify events, 用户使用这些 API 获取关注的 events.
不同于陈旧的 dnotify API, inotify API 既可以监视 files 也可以监视 directories.
监视一个目录,不仅可以获取目录自身改变的 event(e.g. 从目录移除文件),也可以监视目录内文件内容改变产生的 event.
另外:称这些函数为 API 是因为它封装了 system call。每一个函数对应一个 system call. 

继续阅读

读取 /dev/urandom or /dev/random 生成随机数

< /dev/urandom tr -dc A-NP-Za-kmnp-z2-9 | head -c 8
 获取一个 8 位的随机数,除了 0, 1, o,O, l 之外.
 
tr -dc a-z < /dev/urandom 从 /dev/urandom 读入数据并且把所有的小写字母输出。
-d, delete characters in SET1 (a-z);
-c, use the complement of SET1
一个删除,一个补全,最终的效果就是获得读入数据中的所有小写字母。
tr 是从标准输入读入数据,直到遇到 EOF 才会停止,但是 /dev/urandom 是没有 EOF 的。
head -c 8 的作用很明显,就是获得 tr 标准输出中的前 8 个字符,这样这条命令/程序,才可以退出。
 
节省熵池的方法, 生成了长度为 12 的随机数:
dd if=/dev/urandom bs=1 count=6 2> /dev/null | od -t x1 | tee test | sed '2d;s/^0\+ //;s/ //g'
 
生成一个随机 MAC 地址, 节省熵池的方法:
dd if=/dev/urandom bs=1 count=6 2> /dev/null | od -t x1 | sed '2d;s/^0\+ //;s/ /:/g'
 
不建议使用的方法:
< /dev/urandom tr -dC a-f0-9 | head -c 12 | sed s/../\&:/g | head -c 17
 
/dev/urandom:u 是 unlocked 的意思.
/dev/urandom 是 /dev/random 的非阻塞副本。
/dev/random 是 Linux/Unix  操作系统中的一个设备文件,用来作为随机数发生器/伪随机数发生器。
它允许程序访问来自设备驱动程序或其它来源的背景噪声,Linux是第一个以背景噪声产生真正的随机的实现。
 

继续阅读

Regex Unicode 笔记

 
1. Unicode 字符集使用一个或多个 code point 代表一个字符或者说字符映射到特定的数字上。
    
2. 字符又分为基本字符和组合字符。
   一个基本字符与一个或多个组合字符构成组合序列,用来表示一些非ASCII特殊字符。
   比如:
   à 这样的音标符号, 表示为 U+0061 (a) + U+0300 (`) 这两个字符的组合;其中 a 是基本字符,
   ` 是组合字符。
 
3. 一个字符可以被多个代码点表示.
   à 即可表示为: U+0061 和 U+0300 的组合; 为了与 Latin-1 转化方便, à 也表示为 U+00E0,
   很可能是法语单词。也就是说 Unicode 代码点与字符间不是一一映射的关系。
 

继续阅读

类型安全的 max(), min()

类型安全的 max(), min();
#define max(a, b) \
          ({ __typeof__ (a) (_a) = (a); \
             __typeof__ (b) (_b) = (b); \
             _a > _b ? _a : _b; })
#define min(a, b) \
         ({ __typeof__ (a) (_a) = (a); \
            __typeof__ (b) (_b) = (b); \
           _a < _b ? _a : _b; })
AISN C 使用 __typeof__ , GUN C 可以使用 typeof.

 

valgrind 简单使用

valgrind 是一款 memorycheck 工具,用于 c/c++ 程序的内存检查/调试。
0. 为了使用 valgrind 编译程序时要使用 gcc 的 -g 编译选项,链接libc 的 debug 版本,
   自动链接,但是要求有, 如果没有 valgrind 不能正常启动内存检查.
   程序用到的其他 C 库也要有 debug 版本的。
   也可以使用 gcc 的 -O0 编译选项。据说使用了更好。(大写 O, 数字 0)

   -g 是表示编译出的二进制代码中包含调试符号

   -O0 是 gcc 的默认选项。可能一些低版本的 gcc 需要加上这个选项。

1. Memorycheck 是 valgrind 的默认工具,--leak-check 选项可以打开 valgrind 详细的 memory leak 探测工具。
$ gcc -O0 -g example.c -o example
$ valgrind --leack-check=yes ./example arg1 arg2

 

2. valgrind 的输出。
下面是一个正常分配并正常释放内存的程序用 valgrind 调试的输出:

继续阅读




Host by is-Programmer.com | Power by Chito 1.3.3 beta | © 2007 LinuxGem | Design by Matthew "Agent Spork" McGee