coreutils4.5.1 paste.c源码分析
今天天冷,几天没出门了。把新买的电热取暖器放在电脑房,然后拉上窗帘,开始读源码,学习linus,很好玩。
paste这个命令有啥应用场景呢?先学习使用:
head -n 100 a1 a2
==> a1 <==
this is
hell owrld
==> a2 <==
this ath
paste a1 a2
this is this ath
hell owrld
paste -d: a1 a2
this is :this ath
hell owrld:
paste -s a1 a2
this is hell owrld
this ath
注意,我在另一个文件夹下新建立了两个文件,然后随便折腾。
可以看到,-s选项,把每个文件变成一行。
-d:以:为分隔符,把两个文件每个文件行为一栏,进行合并。
开始读程序时,有些害怕,毕竟每进入一个新的程序,就象进入一个陌生的场景,生怕其中的怪兽吃了自己,人总是害怕陌生的环境。
昨天睡得香,今天早上就起来看。发现慢慢也看出了眉目。请听我讲。
/* If nonzero, merge subsequent lines of each file rather than
corresponding lines from each file in parallel. */
static int serial_merge;
这个全局全量,当设置-s选项时,把每个文件变成一行进行汇总。在getopt中处理如下:
serial_merge = 0;
delims = default_delims;
strcpy (delims, "\t");
strcpy (zero_delims, "\\0");
while ((optc = getopt_long (argc, argv, "d:s", longopts, NULL)) != -1)
{
switch (optc)
{
case 0:
break;
case 'd':
/* Delimiter character(s). */
if (optarg[0] == '\0')
optarg = zero_delims;
delims = optarg;
break;
case 's':
serial_merge++;
break;
开始设置好变量serial_merge为0,当有-s选项时,值为1,而后面进行处理。
if (!serial_merge)
exit_status = paste_parallel (argc - optind, &argv[optind]);
else
exit_status = paste_serial (argc - optind, &argv[optind]);
也就是,要达到每个文件变成一行的目的,在子程序paste_serial中实现。
首先,这个子程序很有意思,
static int
paste_serial (int nfiles, char **fnamptr)
也就是说,fnamptr是个字符串数组指针。类似于int main(int argc,char *argv[])
中的,argv,当时我在想,如何进行文件的递进呢?也就是说,当前文件处理完了,开始处理下一文件,是这样的:
for (; nfiles; nfiles--, fnamptr++)
{
}
看到没,nfiles表示文件数量,而fnamptr++表示开始处理下一个文件,太神奇了。我开始总是在找++,后来在循环条件中找到,真厉害。
static int
paste_serial (int nfiles, char **fnamptr)
{
int errors = 0; /* 1 if open or read errors occur. */
register int charnew, charold; /* Current and previous char read. */
register char *delimptr; /* Current delimiter char. */
register FILE *fileptr; /* Open for reading current file. */
for (; nfiles; nfiles--, fnamptr++)
{
if (STREQ (*fnamptr, "-"))
{
have_read_stdin = 1;
fileptr = stdin;
}
else
{
fileptr = fopen (*fnamptr, "r");
if (fileptr == NULL)
{
error (0, errno, "%s", *fnamptr);
errors = 1;
continue;
}
}
delimptr = delims; /* Set up for delimiter string. */
charold = getc (fileptr);
if (charold != EOF)
{
/* `charold' is set up. Hit it!
Keep reading characters, stashing them in `charnew';
output `charold', converting to the appropriate delimiter
character if needed. After the EOF, output `charold'
if it's a newline; otherwise, output it and then a newline. */
while ((charnew = getc (fileptr)) != EOF)
{
/* Process the old character. */
if (charold == '\n')
{
if (*delimptr != EMPTY_DELIM)
putc (*delimptr, stdout);
if (++delimptr == delim_end)
delimptr = delims;
}
else
putc (charold, stdout);
charold = charnew;
}
/* Hit EOF. Process that last character. */
putc (charold, stdout);
}
if (charold != '\n')
putc ('\n', stdout);
if (ferror (fileptr))
{
error (0, errno, "%s", *fnamptr);
errors = 1;
}
if (fileptr == stdin)
clearerr (fileptr); /* Also clear EOF. */
else if (fclose (fileptr) == EOF)
{
error (0, errno, "%s", *fnamptr);
errors = 1;
}
}
return errors;
}
这个函数较长,我就用自己的理解把它完成的事说一下。
对每个需要合并的文件进行处理
对当前文件,判断是否是标准输入,并进行处理。
对当前文件,把每一个换行符替换为另一字符,直到把文件处理完
这样,这个程序也基本算看完了,其中还有另一个-d选项,没看懂,另个,-s选项中,的处理逻辑还有些难懂,先算看完了。
其实要看懂程序和要看懂到能修改程序是有很大区别的。如果只是满足于大致看懂,应该很快。重点是抓大的逻辑块。建立处理事务的高层抽象。
而如果要能修改程序的话,那要求就更高些。反正,这些程序我可写不出来。