Tuesday, September 30, 2008

再说find action,提高终端下的工作效率

再说find的action,是个很有用的工具,

对于查看目录下的所有文件,普通人常用的是ls,不带任何参数的ls,碰到有子目录的情况,就cd到子目录里面,进去只为敲个ls,再又cd回来,因为当前还是主要工作目录,需要查看哪个目录时就cd到哪儿去;进阶一点的人会用ls后接目录参数,可是ls还是只能列出一个目录,对于需要同时查看多个目录的情况可以用"ls -R",可是它只列出了文件名,没有全路径,对于下一步的最大可能是cat或调用编辑器,有了全路径才是最有用的;

试试这个: "find -ls"

一次将目录下所有文件都以ls方式列表出来,可以是递归的方式看到所有文件的"ls -l"方式的结果,并且有了全路径,可以用鼠标双击全选中,再敲中键(就是鼠标滚轮)便会粘贴出来,C-A定位光标到达行首,加上个cat或者vim,阅读或者是编辑,一切操作都是连贯的;

在Shell应用中,对/proc/sys/或/sys/下的查看常常既有列表还需要阅读变量内容,以前的做法是:

find /proc/sys/vm -type f -ls -exec cat {} \;

或者需要监视这个变量值的变化情况,加上个watch,每隔1s重读一次:

watch -n1 'find /proc/sys/vm -type f -printf "%p\t" -exec cat {} \;'

find是一个简单而强大的工具,绝不只是如名称一般只有"查找"文件的功用,它的参数有两个基本类型:


  1. test是一个条件判断,如查按文件名查找使用"-name ...";

  2. action是对找到的文件的一个动作,如缺省的-print是打印全路径名,-ls是模拟ls方式列表;



缺省的action只有ls,printf等,(printf另有强大的功能),而-exec扩展方式可以后接程序执行,如上面的-exec cat读出文件内容,

但是-exec cat实在太常用了,今日作了一个patch,在find-4.5.2上面加上-cat这个action,这样上面的监视命令就可以简化为:

find /proc/sys/vm -cat



Download as text

  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
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
---
find/defs.h | 1 +
find/parser.c | 22 ++++++++++++++++++++++
find/pred.c | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++++
find/tree.c | 1 +
4 files changed, 76 insertions(+)

Index: findutils-4.5.2/find/defs.h
===================================================================
--- findutils-4.5.2.orig/find/defs.h
+++ findutils-4.5.2/find/defs.h
@@ -411,6 +411,7 @@ PREDICATEFUNCTION pred_cnewer;
PREDICATEFUNCTION pred_comma;
PREDICATEFUNCTION pred_ctime;
PREDICATEFUNCTION pred_delete;
+PREDICATEFUNCTION pred_cat;
PREDICATEFUNCTION pred_empty;
PREDICATEFUNCTION pred_exec;
PREDICATEFUNCTION pred_execdir;
Index: findutils-4.5.2/find/parser.c
===================================================================
--- findutils-4.5.2.orig/find/parser.c
+++ findutils-4.5.2/find/parser.c
@@ -91,6 +91,7 @@ static boolean parse_cnewer PARAM
static boolean parse_comma PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
static boolean parse_daystart PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
static boolean parse_delete PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
+static boolean parse_cat PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
static boolean parse_d PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
static boolean parse_depth PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
static boolean parse_empty PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
@@ -253,6 +254,7 @@ static struct parser_table const parse_t
{ARG_TEST, "ctime", parse_time, pred_ctime}, /* POSIX */
PARSE_POSOPT ("daystart", daystart), /* GNU */
PARSE_ACTION ("delete", delete), /* GNU, Mac OS, FreeBSD */
+ PARSE_ACTION ("cat", cat), /* GNU */
PARSE_OPTION ("d", d), /* Mac OS X, FreeBSD, NetBSD, OpenBSD, but deprecated in favour of -depth */
PARSE_OPTION ("depth", depth), /* POSIX */
PARSE_TEST ("empty", empty), /* GNU */
@@ -801,6 +803,26 @@ parse_delete (const struct parser_table*
}

static boolean
+parse_cat (const struct parser_table* entry, char *argv[], int *arg_ptr)
+{
+ struct predicate *our_pred;
+ (void) argv;
+ (void) arg_ptr;
+
+ our_pred = insert_primary (entry);
+ our_pred->side_effects = our_pred->no_default_print = true;
+ our_pred->need_stat = our_pred->need_type = false;
+ our_pred->est_success_rate = 1.0f;
+ open_stdout(&our_pred->args.printf_vec);
+
+#ifdef DEBUG
+ fprintf(stderr, "%s(%s:%d) called.\n", __func__, __FILE__, __LINE__);
+#endif
+
+ return true;
+}
+
+static boolean
parse_depth (const struct parser_table* entry, char **argv, int *arg_ptr)
{
(void) entry;
Index: findutils-4.5.2/find/pred.c
===================================================================
--- findutils-4.5.2.orig/find/pred.c
+++ findutils-4.5.2/find/pred.c
@@ -445,6 +445,58 @@ pred_delete (const char *pathname, struc
}

boolean
+pred_cat (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)
+{
+ FILE * stream = pred_ptr->args.printf_vec.stream;
+ FILE * input_stream;
+ char buf[BUFSIZ];
+ int fd;
+ size_t read_len;
+
+ (void) pred_ptr;
+ (void) stat_buf;
+
+#ifdef DEBUG
+ fprintf(stderr, "%s(%s:%d) called.\n", __func__, __FILE__, __LINE__);
+#endif
+
+ if ((fd = openat(state.cwd_dir_fd, state.rel_pathname, O_RDONLY
+#if defined O_LARGEFILE
+ |O_LARGEFILE
+#endif
+ )) <>
+ {
+ error (0, errno, "%s", safely_quote_err_filename(0, pathname));
+ state.exit_status = 1;
+ return false;
+ }
+
+ input_stream = fdopen(fd, "r");
+ if (input_stream == NULL)
+ {
+ error (0, errno, "%s", safely_quote_err_filename(0, pathname));
+ state.exit_status = 1;
+ return false;
+ }
+
+ while ((read_len = fread(buf, 1, sizeof buf, input_stream)) > 0)
+ {
+#ifdef DEBUG
+ fprintf(stderr, "%s(%s:%d): read_len = %d, stream = %p\n",
+ __func__, __FILE__, __LINE__, read_len, stream);
+#endif
+ fwrite(buf, 1, read_len, stream);
+
+ if (read_len <>
+ break;
+ }
+
+ fclose(input_stream);
+
+ return true;
+}
+
+boolean
pred_empty (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)
{
(void) pathname;
Index: findutils-4.5.2/find/tree.c
===================================================================
--- findutils-4.5.2.orig/find/tree.c
+++ findutils-4.5.2/find/tree.c
@@ -906,6 +906,7 @@ static struct pred_cost_lookup costlooku
{ pred_comma , NeedsNothing, },
{ pred_ctime , NeedsStatInfo, },
{ pred_delete , NeedsSyncDiskHit },
+ { pred_cat , NeedsNothing },
{ pred_empty , NeedsStatInfo },
{ pred_exec , NeedsEventualExec },
{ pred_execdir , NeedsEventualExec },



Download as text


参看这个帖子来个实用的:查看文件链接,回复find命令的人是我:有人需要一些shell功能,想到了用python去写,想法不错,却没想到的是,那一段的python代码只对应了一行find命令。

Monday, September 22, 2008

KS2008与LPC2008刚结束

著名黑客GregKroahHartman在这个会上对Ubuntu幕后的Canonical公司的不贡献进行了猛烈的批评:

Canonical的内核开发者在过去三年,从2.6.19到2.6.27-rc5之间,只向内核提交了约100个补丁!(与我在过去两年内做的差不多~)

作为对比,Greg也同样列出了各大Distro的数据:

RedHat排名第二,Novell排名第五,Mandriva是第45,Canonical是第81,

而社区发行版中,Debian和Gentoo分别排名第41和第49,

其实,早在上半年JonathonCorbet就以LWN文章提出了对Canonical的批评,但这次Greg的这次更猛烈的批评则是第一次将问题的严重性推向聚光灯下:

  1. Kernel Log: The second day of Kernel Summit 2008; criticism of Ubuntu at the opening of the Linux Plumbers Conference
  2. Linux Plumbers Conference 2008 Keynote

说到这个排名,可能有人对更为神秘的第一名感兴趣,那么,这个神秘的能够超越RedHat的第一名究竟是谁呢?

那就是我们伟大的Amateurs(业余爱好者)!Kernel的发展自始至现在仍然保持着平民化的Amateurs为主导的发展!

Tuesday, September 02, 2008

manually upgrade x11-drivers/nvidia-drivers

Upgrade kernel to v2.6.27-rc5, x11-drivers/nvidia-drivers have not support so late kernel,

So let's upgrade it manually:

http://paste.ubuntu.com/42782/

Index: NVIDIA-Linux-x86-173.14.12-pkg0/usr/src/nv/nv-linux.h
===================================================================
--- NVIDIA-Linux-x86-173.14.12-pkg0.orig/usr/src/nv/nv-linux.h
+++ NVIDIA-Linux-x86-173.14.12-pkg0/usr/src/nv/nv-linux.h
@@ -77,6 +77,7 @@


#if !defined(KERNEL_2_4)
#include <linux/sched.h> /* suser(), capable() replacement */
+#include <linux/pid.h> /* for find_pid */
#include <linux/moduleparam.h> /* module_param() */
#include <linux/smp_lock.h> /* kernel_locked */
#include <asm/tlbflush.h> /* flush_tlb(), flush_tlb_all() */

@@ -104,7 +105,7 @@
#endif

#include <linux/spinlock.h>
-#include <asm/semaphore.h>
+#include <linux/semaphore.h>
#include <linux/completion.h>
#include <linux/highmem.h>


Index: NVIDIA-Linux-x86-173.14.12-pkg0/usr/src/nv/os-interface.c
===================================================================
--- NVIDIA-Linux-x86-173.14.12-pkg0.orig/usr/src/nv/os-interface.c
+++ NVIDIA-Linux-x86-173.14.12-pkg0/usr/src/nv/os-interface.c
@@ -704,7 +704,7 @@ RM_STATUS NV_API_CALL os_kill_process(
U032 sig
)
{
- return kill_proc(pid, sig, 1) ? RM_ERR_OPERATING_SYSTEM : RM_OK;
+ return kill_pid(find_vpid(pid), sig, 1) ? RM_ERR_OPERATING_SYSTEM : RM_OK;

}

/*******************************************************************************/


this patch also reflects some changes in 2.6.27:
1. asm/semaphore.h => linux/semaphore.h stands a common implementation in all arch;
2. kill_proc => kill_pid

But there's still a problem haven't been resolved, it reports license issues in modpost phase:
1. kill_pid is exported with EXPORT_SYMBOL_GPL, can be used only by GPL code;
2. nvidia.ko is licensed with "NVIDIA", not GPL;

# cd /var/tmp/portage/x11-drivers/nvidia-drivers-173.14.12/work/NVIDIA-Linux-x86-173.14.12-pkg0/usr/src/nv/

# make -C /usr/src/linux-2.6.27-rc5/ M=$PWD modules

the core functionality of nvidia.ko is hidden in nv-kernel.o, that's completely binary, there's no corespoding nv-kernel.c, results of `strings` told that the license is declared in this binary file;

the last killer approach is hexeditor! use it to hexedit the string to "GPL\0", compiling well, insmod well, all runs OK!

Hexediting is an ugly hack, hope my next machine can run toally under free operating system, no hidden binary anymore!

Friday, August 29, 2008

monitor the system totally with dstat

You must have ever monitored the system with different tools:
  1. "vmstat 2" to get a total system "proc,mem,swap,io,sys,cpu" load;
  2. "iostat 2" to get all block devices with seperated I/O bandwidth data;
  3. "ifstat 2" to get different network interfaces network activity;
  4. "top" to get the most CPU-consuming processes;
Now you can do all these things within dstat:
gektop@tux ~ 0 $ dstat -M proc,cpu,mem,sys,page,net,disk,topcpu,topmem,topio,topbio 5
---procs--- ----total-cpu-usage---- ------memory-usage----- ---system-- ---paging-- -net/total- -dsk/total- -most-expensive- -most-expensive- ----most-expensive---- ----most-expensive----
run blk new|usr sys idl wai hiq siq| used buff cach free| int csw | in out | recv send| read writ| cpu process | memory process | i/o process | block i/o process
0 0 6| 39 9 37 14 0 1| 272M 25M 193M 12M| 760 2178 | 0 21B| 0 0 |5577k 1645k|firefox 10|firefox 125M|mount.ntfs- 10M:8223k|mount.ntfs-2699k: 736k
3 0 8| 93 6 0 0 0 1| 252M 25M 189M 37M| 546 980 | 0 0 |3197B 564B| 109k 675k|firefox 15|firefox 125M|mount.ntfs- 771k:3056B|pdflush 0 : 82k
3 0 71| 77 16 0 7 0 1| 257M 26M 191M 29M| 548 1247 | 0 0 |5445B 689B| 230k 544k|firefox 13|firefox 125M|mount.ntfs- 771k:3056B|pdflush 0 : 94k
2 0 24| 85 14 0 1 0 0| 251M 26M 192M 33M| 550 1046 | 0 0 |1173B 418B| 50k 344k|firefox 12|firefox 125M|mount.ntfs- 771k:3056B|pdflush 0 : 85k
2 0 25| 85 12 0 2 0 0| 275M 26M 194M 8044k| 517 1024 | 0 0 |1206B 448B| 104k 642k|cc1 22|firefox 125M|mount.ntfs- 771k:3056B|pdflush 0 : 47k
2 0 9| 88 9 0 2 0 1| 278M 26M 193M 5568k| 558 1056 | 0 0 |1383B 615B| 110k 451k|cc1 26|firefox 125M|mount.ntfs- 771k:3056B|pdflush 0 : 51k
The meanings of "proc,cpu,mem,sys,page,net,disk,topcpu,topmem,topio,topbio" is obvious, it speaks for itself, the design of dstat is all modular, you can use many other modules than this:
$ (cd /usr/share/dstat/; ls *.py)
dstat_battery.py dstat_gpfs.py dstat_mysql5_com.py dstat_mysql_keys.py dstat_postfix.py dstat_thermal.py dstat_topoom.py dstat_vzcpu.py
dstat_cpufreq.py dstat_helloworld.py dstat_mysql5_conn.py dstat_nfs3op.py dstat.py dstat_topbio.py dstat_utmp.py dstat_vzubc.py
dstat_dbus.py dstat_innodb_buffer.py dstat_mysql5_io.py dstat_nfs3.py dstat_rpcd.py dstat_topcpu.py dstat_vmkhba.py dstat_wifi.py
dstat_freespace.py dstat_innodb_io.py dstat_mysql5_keys.py dstat_nfsd3op.py dstat_rpc.py dstat_topio.py dstat_vmkint.py
dstat_gpfsop.py dstat_innodb_ops.py dstat_mysql_io.py dstat_nfsd3.py dstat_sendmail.py dstat_topmem.py dstat_vmknic.py
So with "dstat -M battery 5", you can get the most battery consuming components when hardware supports ACPI.

Its module is just a simple py file with a simple class derived from dstat, so if there's no one fit for you, you can write your own dstat module.

Note: only CLI-based utils discussed here, I know there are many GUI-based or graph-generating tools can do better graphics, but on ther server side, "
Simple is better than complex.
"

Saturday, August 09, 2008

(setq cscope-do-not-update-database t)

if you use the kernel kbuild system `make mrproper cscope` with its cscope databases index files, use this to tell cscope not to update every time:

(setq cscope-do-not-update-database t)

Sunday, July 13, 2008

some most useful bash features

edit-and-execute-command (C-xC-e)
Invoke an editor on the current command line, and execute the result as shell com‐
mands. Bash attempts to invoke $FCEDIT, $EDITOR, and emacs as the editor, in that
order.

insert-completions (M-*)
Insert all completions of the text before point that would have been generated by pos‐
sible-completions.

complete-into-braces (M-{)
Perform filename completion and insert the list of possible completions enclosed
within braces so the list is available to the shell (see Brace Expansion above).

insert-comment (M-#)
Without a numeric argument, the value of the readline comment-begin variable is
inserted at the beginning of the current line. If a numeric argument is supplied,
this command acts as a toggle: if the characters at the beginning of the line do not
match the value of comment-begin, the value is inserted, otherwise the characters in
comment-begin are deleted from the beginning of the line. In either case, the line is
accepted as if a newline had been typed. The default value of comment-begin causes
this command to make the current line a shell comment. If a numeric argument causes
the comment character to be removed, the line will be executed by the shell.

Keyboard Macros
start-kbd-macro (C-x ()
Begin saving the characters typed into the current keyboard macro.
end-kbd-macro (C-x ))
Stop saving the characters typed into the current keyboard macro and store the defini‐
tion.
call-last-kbd-macro (C-x e)
Re-execute the last keyboard macro defined, by making the characters in the macro
appear as if typed at the keyboard.

display-shell-version (C-x C-v)
Display version information about the current instance of bash.

Friday, July 11, 2008

HOWTO ptrace a multi-process programs?

HOWTO-ptrace-multi-process-programs?



References:

  • Playing with ptrace, Part I (LinuxJournal)

  • Playing with ptrace, Part II (LinuxJournal)

  • 以 ptrace 系統呼叫來追蹤/修改行程 (JservBlog)

  • one line curl paste

    Recently an utility named wgetpaste run into my field, but after coming across it, I found it was really a shell script, it just organizes arguments and transfer to wget, in use of post mode of wget,



    $ head -n1 /usr/bin/wgetpaste
    #!/bin/sh


    In fact, I found that curl is better at this job, when I post some code to a paste service, just one curl command is enough:



    $ curl -d poster=chengrq -d syntax=c --data-urlencoded content@file.c \
    http://paste.ubuntu.com


    And, if you would like to read from a pipe other than a file, you could use '-' to replace the filename, it always did like what you think of that!



    BTW, ubuntu did a good job on the paste service, supporting many syntax highting.

    undocumented getopt

    首先说说getopt的规则用法,查看相关手册:



    $ man 3 getopt:

    #include <unistd.h>

    int getopt(int argc, char * const argv[],
    const char *optstring);

    extern char *optarg;
    extern int optind, opterr, optopt;

    #define _GNU_SOURCE
    #include <getopt.h>

    int getopt_long(int argc, char * const argv[],
    const char *optstring,
    const struct option *longopts, int *longindex);

    int getopt_long_only(int argc, char * const argv[],
    const char *optstring,
    const struct option *longopts, int *longindex);



    这一段是说只需要包含了unistd.h头文件就可以使用getopt了,它的函数原型使用argc,argv,optstring这三个参数;而如果需要使用GNU的扩展函数getopt_long,则还需要包含头文件getopt.h,并且在头文件之前定义_GNU_SOURCE宏,可以在C源文件中包含getopt.h之前直接define,而我一般是在Makefile里面写在CFLAGS上直接传参数给gcc;



    于是具体的编译命令是

    gcc -Wall -D_GNU_SOURCE filename.c
    这样如果大型项目使用了多个C文件则不必在每一个C文件中都写上define _GNU_SOURCE,只在Makefile写一次,减少了总体字节数。



    将其中的例子取出来测试:




    Download as text

     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
    #include <unistd.h>
    #include <stdlib.h>
    #include <stdio.h>

    int main(int argc, char *argv[])
    {
    int flags, opt;
    int nsecs, tfnd;

    nsecs = 0;
    tfnd = 0;
    flags = 0;
    while ((opt = getopt(argc, argv, "nt:")) != -1) {
    switch (opt) {
    case 'n':
    flags = 1;
    break;
    case 't':
    nsecs = atoi(optarg);
    tfnd = 1;
    break;
    default: /* ? */
    fprintf(stderr, "Usage: %s [-t nsecs] [-n] name\n",
    argv[0]);
    exit(EXIT_FAILURE);
    }
    }

    printf("flags=%d; tfnd=%d; optind=%d, nsecs=%d\n",
    flags, tfnd, optind, nsecs);

    if (optind >= argc) {
    fprintf(stderr, "Expected argument after options\n");
    exit(EXIT_FAILURE);
    }

    while (optind < argc)
    printf("name argument = %s\n", argv[optind++]);

    /* Other code omitted */

    exit(EXIT_SUCCESS);
    }



    Download as text


    编译和运行:



    $ gcc -Wall opt1.c
    $ ./a.out -t 3 -n arg1 -t 4 arg2
    flags=1; tfnd=1; optind=6, nsecs=4
    name argument = arg1
    name argument = arg2


    可见:




    1. 同一个参数指定多项时以最后一项指定生效,(-t4覆盖了-t3选项值);

    2. 最后多作的参数可以使用optind索引开始访问,getopt在调用过程中对argv字符串数组进行了permute,把所有非option的argument都移到最后去了;



    那么如果需要一个非option的argument参数并且是以"-"开头的,怎么输入?



    manual附带提到了一点,可以使用"--"来关闭option扫描:



    $ ./a.out -t 3 -n arg1 -t 4 arg2 -- -t 6 --new null
    flags=1; tfnd=1; optind=7, nsecs=4
    name argument = arg1
    name argument = arg2
    name argument = -t
    name argument = 6
    name argument = --new
    name argument = null



    在这次运行中,"-t 6"已经不被看作是option,而是作为多余的argument交给了程序。




    然后来说说optarg,optind,optopt,opterr的用途:





    1. optarg作为后接":"字符的option对应所需要的参数;

    2. optind是getopt处理完成(返回EOF表示完成)之后argument的位置,罽getopt只对argv数组重排序,不改变argc的大小,因此从optind到argc循环便可得所有非option参数;

    3. optopt和opterr都是用于处理用户输入未定义的option字符的情况,此时getopt返回'?'字符,将用户输入的真实的option放在optopt,并且自动向stderr打印一条错误消息,如"invalid option -- h",如果不想要这个错误提示可以预先设置opterr为0可关闭这个错误提示;



    手册中还详细介绍了optstring的用法:



    1. 单个的字符是开关量;

    2. 单个字符后接":"则意味着需要参数,在getopt调用后这个参数会被放入optarg这个全局量中;
    3. 如果接上两个冒号"::",则意味着参数是可选的,如"f::"对应输入"-ffoo"时,optarg获得"foo",而"-f foo"则optarg获得NULL,"foo"被计入argument部分;

    4. 如果optstring以"+"开头,则getopt停止于第一个非option处,如"-t 3 -n arg1 -t 4 arg2"会停止于"arg1"处将从它开始的"arg1 -t 4 arg2"都自动作为argument;

    5. 如果optstring以"-"开头,则getopt会将所有非option项(即argument)都以optiont为1处理(注意是数字1不是字符'1'),将其argument作为optarg;

    6. optstring中在前缀"+"或"-"后的第一个字符如果是':'冒号,则对未识别的option字符返回':'冒号而不是'?',参见optopt和opterr描述;



    以上部分都是手册里面提到过的正常的使用getopt,下面是一段在实用过程中发现的getopt的undocumented特性:


    有一个程序需要对getopt作多次循环调用:

    一般的getopt对argc,argv调用返回了所有参数后,在最后一次返回EOF表示argv数组已处理完,这里我将它称为一轮getopt循环调用;

    多次循环调用指的定在一次循环调用之后如何在程序里继续使用getopt处理其它的argc,argv?


    可能有人说,对新的argc,argv直接调用getopt就是了,~~~,其实不然:

    getopt能够循环调用的原理是它在内部使用了静态变量,保存了对argc,argv操作的状态,当第一轮getopt处理完时,这些内部的静态变量已记录处理到argc,argv到最后;因此如果直接使用getopt对新的argc,argv组进入调用时,会发现其根本不工作,直接返回EOF;


    于是有必要对getopt的内部原理作一番研究,已知它是标准C库提供的函数,于是找到glibc:

    apt-get source glibc
    或者
    ebuild /usr/portage/sys-libs/glibc/glibc-x.x.ebuild unpack
    取得源代码,找到实现这个库函数的posix/getopt.c文件,这里将它上传到了paste,分析其中初始化这些静态变量可以发现规律是:

    它会根据optind的值进行初始化(406行,1133行);

    调用顺序及相关数据结构是:



    static struct _getopt_data getopt_data;


    getopt (int argc, char *const *argv, const char *optstring)


    _getopt_internal (int argc, char *const *argv, const char *optstring,


      getopt_data.optind = optind;


     _getopt_internal_r (int argc, char *const *argv, const char *optstring,


      if (d->optind == 0 || !d->__initialized)


          optstring = _getopt_initialize (argc, argv, optstring, d);


    至此可知在d->optind为0或者d->__initialized为假的情况下会调用_getopt_initialize重新初始化;而调用_getopt_internal_r传入的d指针参数始终是&getopt_data,这是一个模块级全局变量(即static全局变量),在C库的此文件之外不可见,因此无法以直接的getopt_data.optind方式设置它;但从前面的调用路径可知d->optind每次都从全局变量optind初始化;因此我们可以设置optind为0然后再调用getopt让传入的d->optind为0使_getopt_initialize再一次被调用,让它从新初始化getopt所使用的内部静态变量(即getopt_data),达到可以重新一轮调用getopt的目的;



    于是程序中,需要新一轮调用getopt之前作一次

    optind=0;
    的设置,于是getopt继续工作!



    总结起来:


    1. getopt(3)手册中中提到了optarg,optind,optopt的输出(out类型参数)作用,即对用户程序是只读的,在getopt调用之后从它们可以读到一些信息;而opterr是只写(in类型参数)的,须在getopt调用之前进行设置为0或1,getopt函数实现中不改变opterr,而只根据opterr值有一些不同的行为;

    2. 通过对C库中的getopt函数实现的阅读可以了解到:optind还可以作为输入(in类型)参数,在getopt调用之前将optind置0可以使得getopt函数内部重新初始化;

    3. getopt.c:114行对optind的注释中提到"zero means the first call",而手册(manual)中未提到optind的in参数功能。


    Saturday, March 29, 2008

    I'm back again.

    Wednesday, March 21, 2007


    var d = {
    pre: 'file',
    exit: function() {
    return false;
    }
    }
    for (i in d) {
    document.writeln("attr:" + i + d[i]);
    }
    疏影横斜水清浅,暗香浮动月黄昏。

    Saturday, February 10, 2007

    为什么没有?
    the pros and cons of Shanghai

    i lived in a long term in Shanghai, but there are many faults.

    Saturday, September 23, 2006

    测试
    测试
    测试

    Wednesday, December 14, 2005