Chinaunix首页 | 论坛 | 博客
  • 博客访问: 915572
  • 博文数量: 177
  • 博客积分: 8613
  • 博客等级: 中将
  • 技术积分: 2835
  • 用 户 组: 普通用户
  • 注册时间: 2006-03-12 04:16
文章分类
文章存档

2012年(12)

2011年(24)

2010年(24)

2009年(75)

2008年(42)

我的朋友

分类: C/C++

2009-12-13 12:23:19

     tee这个程序读完,主要学习到了文件输入输出,读写的结果系统调用,更重要的是程序的简洁和为标准化做的努力。

/* tee - read from standard input and write to standard output and files.
从标准输入中读,写到标准输出和文件中去
   Copyright (C) 85,1990-2006 Free Software Foundation, Inc.

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2, or (at your option)
   any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software Foundation,
   Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */

/* Mike Parker, Richard M. Stallman, and David MacKenzie */

#include
#include
#include
#include

#include "system.h"
#include "error.h"
#include "stdio--.h"//这个名字起得。。。

/* The official name of this program (e.g., no `g' prefix).  */
#define PROGRAM_NAME "tee"//定义宏

#define AUTHORS "Mike Parker", "Richard M. Stallman", "David MacKenzie"//定义宏

//函数原型声明,此函数接受两个形参,一个是整型,一个是指向常量字符的指针的指针,函数返回的类型是布尔类型,作用域仅限于本文件
static bool tee_files (int nfiles, const char **files);

/* If true, append to output files rather than truncating them.这句话的意思是,如果append这个布尔类型为真的话,向输出文件中追加,而不是截断他们 */
static bool append;//声明全局布尔变量

/* If true, ignore interrupts.如果这个布尔变量是真的话,忽略中断,我测试了一下,按^C的确不管用了,^D还是可以的 */
static bool ignore_interrupts;//声明全局布尔变量,在soureinsight的左边缩略图里,也显示出了这些全局变量了

/* The name that this program was run with. */
char *program_name;//声明全局字符指针变量

static struct option const long_options[] =
{
  {"append", no_argument, NULL, 'a'},
  {"ignore-interrupts", no_argument, NULL, 'i'},
  {GETOPT_HELP_OPTION_DECL},
  {GETOPT_VERSION_OPTION_DECL},
  {NULL, 0, NULL, 0}
};//初始化一个option类型的结构体数组

void
usage (int status)//帮助函数
{
  if (status != EXIT_SUCCESS)
    fprintf (stderr, _("Try `%s --help' for more information.\n"),
         program_name);
  else
    {
      printf (_("Usage: %s [OPTION]... [FILE]...\n"), program_name);
      fputs (_("\
Copy standard input to each FILE, and also to standard output.\n\
\n\
  -a, --append              append to the given FILEs, do not overwrite\n\
  -i, --ignore-interrupts   ignore interrupt signals\n\
"), stdout);
      fputs (HELP_OPTION_DESCRIPTION, stdout);
      fputs (VERSION_OPTION_DESCRIPTION, stdout);
      fputs (_("\
\n\
If a FILE is -, copy again to standard output.\n\
"), stdout);
      printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
    }
  exit (status);
}

int
main (int argc, char **argv)//main函数不算长,一屏而已
{
  bool ok;//声明布尔类型变量
  int optc;//声明整型变量

  initialize_main (&argc, &argv);
  program_name = argv[0];
  setlocale (LC_ALL, "");
  bindtextdomain (PACKAGE, LOCALEDIR);
  textdomain (PACKAGE);

  atexit (close_stdout);//登记出口函数

  append = false;//初始化布尔变量append的值为假
  ignore_interrupts = false;//同上

  while ((optc = getopt_long (argc, argv, "ai", long_options, NULL)) != -1)//while循环里只有一个case判断,循环条件是调用库函数getopt_long,对命令行选项进行解析,全部解析一遍
    {
      switch (optc)//optc是在循环初始,通过getopt_long得到的命令行选项
    {
    case 'a'://如果命令行里有-a,则将布尔变量append的值设置为真
      append = true;
      break;

    case 'i':
      ignore_interrupts = true;
      break;

    case_GETOPT_HELP_CHAR;

    case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);

    default:
      usage (EXIT_FAILURE);//默认,以上条件都不满足的话,调用帮助函数,并退出程序
    }
    }//while循环结束,循环的作用是解析命令行选项,并视情况看是否设置两个全局布尔变量

  if (ignore_interrupts)//这里比较有意思啊,如果ignore_interrupts的值是真的话,就调用signal函数,signal函数做的事情就是对中断进行了忽略,apue上有详细说明
    signal (SIGINT, SIG_IGN);

  /* Do *not* warn if tee is given no file arguments.
     POSIX requires that it work when given no arguments.
     正好看到apue的标准化,看来下面的这段代码考虑到了POSIX标准  */

  ok = tee_files (argc - optind, (const char **) &argv[optind]);//这里调用下面的tee_files函数,并将返回值放在ok这个变量里,这个变量在最后程序退出的时候用来决定返回值,是成功还是失败
  if (close (STDIN_FILENO) != 0)//关闭标准输入
    error (EXIT_FAILURE, errno, _("standard input"));

  exit (ok ? EXIT_SUCCESS : EXIT_FAILURE);//退出程序
}

/* Copy the standard input into each of the NFILES files in FILES
   and into the standard output.
   Return true if successful.  */

static bool
tee_files (int nfiles, const char **files)//tee_files的具体实现,tee的意思是高尔夫球的发球基座
{
  FILE **descriptors;//声明指向文件的指针的指针
  char buffer[BUFSIZ];//声明一个字符数组,在文件操作中经常使用了
  ssize_t bytes_read; //ssize_t类型的变量,查了一下,ssize_t 就是 signed int /long,32位系统上是有符号整型,64位系统上是有符号长整型
  int i;//声明整型变量
  bool ok = true;//初始化布尔变量ok的值为真,ok用来保存最后函数返回的值
  char const *mode_string =
    (O_BINARY
     ? (append ? "ab" : "wb")
     : (append ? "a" : "w"));//这个条件语句学习了,节省了很多if判断,也没有造成多少的可读性降低,实在是受教,希望自己将来也能写出这样简洁的代码来^_^

  descriptors = xnmalloc (nfiles + 1, sizeof *descriptors);//为descriptors分配存储空间

  /* Move all the names `up' one in the argv array to make room for
     the entry for standard output.  This writes into argv[argc].  */
  for (i = nfiles; i >= 1; i--)
    files[i] = files[i - 1];//这里不太理解,数组的元素都后移一个

  if (O_BINARY && ! isatty (STDIN_FILENO))
    freopen (NULL, "rb", stdin);//打开标准输入
  if (O_BINARY && ! isatty (STDOUT_FILENO))
    freopen (NULL, "wb", stdout);//打开标准输出

  /* In the array of NFILES + 1 descriptors, make
     the first one correspond to standard output.   */
  descriptors[0] = stdout;//原来是这样,将第一个输出文件设置为标准输出
  files[0] = _("standard output");
  setvbuf (stdout, NULL, _IONBF, 0);//调用setvbuf库函数,这里的作用是不缓冲

  for (i = 1; i <= nfiles; i++)
    {
    //这个for循环的作用是打开需要打开的文件,并设置好不缓冲标记,想想我写的程序,直接就fopen了,其他啥都没有管,这就是符合标准程序的样子
      descriptors[i] = (STREQ (files[i], "-")
            ? stdout
            : fopen (files[i], mode_string));
      if (descriptors[i] == NULL)
    {
      error (0, errno, "%s", files[i]);
      ok = false;
    }
      else
    setvbuf (descriptors[i], NULL, _IONBF, 0);
    }

  while (1)
    {
      bytes_read = read (0, buffer, sizeof buffer);//调用read系统调用进行读操作,失败则跳出while循环
#ifdef EINTR
      if (bytes_read < 0 && errno == EINTR)
        continue;
#endif
      if (bytes_read <= 0)
    break;

      /* Write to all NFILES + 1 descriptors.
     Standard output is the first one.
可以看到,这里具体实现了向目标文件中写的操作,是调用的库函数fwrite进行的,所有前面设置好的文件都写了
     */
      for (i = 0; i <= nfiles; i++)
    if (descriptors[i]
        && fwrite (buffer, 1, bytes_read, descriptors[i]) != bytes_read)
      {
        error (0, errno, "%s", files[i]);
        descriptors[i] = NULL;
        ok = false;
      }
    }

  if (bytes_read == -1)//最后,如果bytes_read等于-1,说明read系统调用失败了
    {
      error (0, errno, _("read error"));
      ok = false;
    }

  /* Close the files, but not standard output.
调用fclose关闭文件描述符,i从1开始,不关闭标准输出,是因为程序退出的时候会统一关闭,还记得那个标准的,登记出口函数
  */
  for (i = 1; i <= nfiles; i++)
    if (!STREQ (files[i], "-")
    && descriptors[i] && fclose (descriptors[i]) != 0)//其值这个if的条件语句写的也是很简洁啦
      {
    error (0, errno, "%s", files[i]);
    ok = false;
      }

  free (descriptors);//释放当初申请的内存空间

  return ok;//函数返回
}


    ok,来strace一把了:
[root@test t]# strace tee -a file1 file2
execve("/usr/bin/tee", ["tee", "-a", "file1", "file2"], [/* 21 vars */]) = 0
uname({sys="Linux", node="test.16.com", ...}) = 0
brk(0)                                  = 0x81ca000
access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY)      = 3
fstat64(3, {st_mode=S_IFREG|0644, st_size=120505, ...}) = 0
old_mmap(NULL, 120505, PROT_READ, MAP_PRIVATE, 3, 0) = 0xb7fe2000
close(3)                                = 0
open("/lib/tls/libc.so.6", O_RDONLY)    = 3
read(3, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0\20_m\000"..., 512) = 512
fstat64(3, {st_mode=S_IFREG|0755, st_size=1454462, ...}) = 0
old_mmap(0x6c1000, 1219772, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x6c1000
old_mmap(0x7e5000, 16384, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x124000) = 0x7e5000
old_mmap(0x7e9000, 7356, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7e9000
close(3)                                = 0
old_mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb7fe1000
mprotect(0x7e5000, 4096, PROT_READ)     = 0
mprotect(0x6b9000, 4096, PROT_READ)     = 0
set_thread_area({entry_number:-1 -> 6, base_addr:0xb7fe1aa0, limit:1048575, seg_32bit:1, contents:0, read_exec_only:0, limit_in_pages:1, seg_not_present:0, useable:1}) = 0
munmap(0xb7fe2000, 120505)              = 0
brk(0)                                  = 0x81ca000
brk(0x81eb000)                          = 0x81eb000
open("/usr/lib/locale/locale-archive", O_RDONLY|O_LARGEFILE) = 3
fstat64(3, {st_mode=S_IFREG|0644, st_size=48517104, ...}) = 0
mmap2(NULL, 2097152, PROT_READ, MAP_PRIVATE, 3, 0) = 0xb7de1000
close(3)                                = 0
rt_sigaction(SIGPIPE, {SIG_IGN}, NULL, 8) = 0
open("/usr/share/locale/locale.alias", O_RDONLY) = 3
fstat64(3, {st_mode=S_IFREG|0644, st_size=2528, ...}) = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb7de0000
read(3, "# Locale name alias data base.\n#"..., 4096) = 2528
read(3, "", 4096)                       = 0
close(3)                                = 0
munmap(0xb7de0000, 4096)                = 0
open("/usr/share/locale/en_US.UTF-8/LC_MESSAGES/coreutils.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/usr/share/locale/en_US.utf8/LC_MESSAGES/coreutils.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/usr/share/locale/en_US/LC_MESSAGES/coreutils.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/usr/share/locale/en.UTF-8/LC_MESSAGES/coreutils.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/usr/share/locale/en.utf8/LC_MESSAGES/coreutils.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/usr/share/locale/en/LC_MESSAGES/coreutils.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
open("file1", O_WRONLY|O_APPEND|O_CREAT|O_LARGEFILE, 0666) = 3
fstat64(3, {st_mode=S_IFREG|0644, st_size=0, ...}) = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb7de0000
fstat64(3, {st_mode=S_IFREG|0644, st_size=0, ...}) = 0
_llseek(3, 0, [0], SEEK_SET)            = 0
munmap(0xb7de0000, 4096)                = 0
open("file2", O_WRONLY|O_APPEND|O_CREAT|O_LARGEFILE, 0666) = 4
fstat64(4, {st_mode=S_IFREG|0644, st_size=0, ...}) = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb7de0000
fstat64(4, {st_mode=S_IFREG|0644, st_size=0, ...}) = 0
_llseek(4, 0, [0], SEEK_SET)            = 0
munmap(0xb7de0000, 4096)                = 0
read(0, hello(我输入的字符串)
"hello\n", 8192)                = 6
write(1, "hello\n", 6hello(向标准输出进行输出)
)                  = 6
write(3, "hello\n", 6)                  = 6(向文件描述符3进行输出)
write(4, "hello\n", 6)                  = 6(向文件描述符4进行输出)
read(0, seconds(我输入的字符串)
"seconds\n", 8192)              = 8
write(1, "seconds\n", 8seconds
)                = 8
write(3, "seconds\n", 8)                = 8
write(4, "seconds\n", 8)                = 8
read(0, 
[root@test t]# ll
total 8
-rw-r--r--  1 root root 14 Dec 13 12:13 file1
-rw-r--r--  1 root root 14 Dec 13 12:13 file2
[root@test t]# sdiff file1 file2
hello                                                           hello
seconds                                                         seconds

可以看到,shell默认地将标准输出的文件描述符设置为1了,以上跟踪中主要关注的是红色标记出来的行
阅读(1172) | 评论(0) | 转发(0) |
0

上一篇:清晰的目标

下一篇:顺义的温泉

给主人留下些什么吧!~~