coreutils-4.5.1 head.c源碼分析03

今天真是把head.c看懂了。
今天天真冷,我網上購了電熱取暖器,沒送到。但邊聽音樂,邊讀代碼,感覺也很愜意。
看代碼不能着急,要慢慢看,也許就像有人講的,郝培強講的,一開始要慢,開始慢,後面才能越看越快。是的。看代碼,開始要慢。不要着急。
head有幾個選項
-n 行數
-c 字節數
-q 不打印文件名
-v 打印文件名
先把命令用法搞明白。這是重要。

 ./head -q -n 3 head.c
/* head -- output first part of file(s)
   Copyright (C) 89, 90, 91, 1995-2002 Free Software Foundation, Inc.
 ./head -v -n 3 head.c
==> head.c <==
/* head -- output first part of file(s)
   Copyright (C) 89, 90, 91, 1995-2002 Free Software Foundation, Inc.
 ./head -v -n 3 head.c cat.c
==> head.c <==
/* head -- output first part of file(s)
   Copyright (C) 89, 90, 91, 1995-2002 Free Software Foundation, Inc.


==> cat.c <==
/* cat -- concatenate files and print on the standard output.
   Copyright (C) 88, 90, 91, 1995-2002 Free Software Foundation, Inc.

 可以把ubuntu命令行的結果複製到gvim,真是幸福。
我以爲,要在本地編譯命令,我是進入到src文件夾下,
再sudo make一把,然後執行本地命令,注意前面的./
然後,再慢慢看。


static void
write_header (const char *filename)
{
  static int first_file = 1;

  printf ("%s==> %s <==\n", (first_file ? "" : "\n"), filename);
  first_file = 0;
}
這個函數控制是否打印文件名,但此處,first_file這個靜態變量感覺有些不明白,每次用之前設置爲1,用完又改爲0,感覺是多此一舉。
int
main (int argc, char **argv)
{

  have_read_stdin = 0;

  print_headers = 0;

  if (1 < argc && argv[1][0] == '-' && ISDIGIT (argv[1][1]))
    {
    }
   while ((c = getopt_long (argc, argv, "c:n:qv", long_options, NULL)) != -1)
    {處理選項,基本是同一套路,不過,要注意的是,此處是設置全局變量,然後函數中根據全局變量進行處理。
    }
   
  if (header_mode == always
      || (header_mode == multiple_files && optind < argc - 1))
    print_headers = 1;

  if (optind == argc)
    exit_status |= head_file ("-", n_units, count_lines);

  for (; optind < argc; ++optind)
    exit_status |= head_file (argv[optind], n_units, count_lines);

  if (have_read_stdin && close (STDIN_FILENO) < 0)
    error (EXIT_FAILURE, errno, "-");

  exit (exit_status == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
}
可以看出,可以一次傳多個文件名,也就是說,head命令要能一次處理多個文件。如
./head h1 h2 h3 h4
這也符合unix習慣,通過shell展開*之類通配符,具體的命令 head要能處理多個文件。
  for (; optind < argc; ++optind)
    exit_status |= head_file (argv[optind], n_units, count_lines);
再分析head_file
根據-n -c先項,來決定是按行顯示還是按字符個數顯示,其中按字符個數顯示較簡單,代碼也少些。
static int
head_bytes (const char *filename, int fd, uintmax_t bytes_to_write)
{
  char buffer[BUFSIZE];
  int bytes_read;
  size_t bytes_to_read = BUFSIZE;
 SET_BINARY2 (fd, fileno (stdout));
我把錯誤處理刪除了
  while (bytes_to_write)
    {
      if (bytes_to_write < bytes_to_read)
    bytes_to_read = bytes_to_write;
      bytes_read = safe_read (fd, buffer, bytes_to_read);
      if (bytes_read == 0)
    break;
      if (fwrite (buffer, 1, bytes_read, stdout) == 0)
    error (EXIT_FAILURE, errno, _("write error"));
      bytes_to_write -= bytes_read;
    }
  return 0;
}
這段代碼較精練,大意就是:
讀bytes_to_write個字符
當沒讀完時
    按緩衝區大小進行讀
    讀到的字節數寫入bytes_read
    如果讀到0個字符,就表示到了文件末尾。
    再將讀到的bytes_read個字符寫到標準輸出
    bytes_to_write -= bytes_read
所以這段代碼很精練,其中safe_read我沒再細究了。

按行讀的函數
head_lines 我沒有看懂,其中有一個錯誤處理:
 while (bytes_to_write < bytes_read)
    if (buffer[bytes_to_write++] == '\n' && --lines_to_write == 0)
      {
        /* If we have read more data than that on the specified number
           of lines, try to seek back to the position we would have
           gotten to had we been reading one byte at a time.  */
        if (lseek (fd, bytes_to_write - bytes_read, SEEK_CUR) < 0)
          {
        int e = errno;
        struct stat st;
        if (fstat (fd, &st) != 0 || S_ISREG (st.st_mode))
          error (0, e, _("cannot reposition file pointer for %s"),
             filename);
          }
        break;
      }
作者調用lseek不知是幹什麼?其中當讀到的字符是'\n'時,進行行的計數,這個很關鍵。我喜歡留點兒尾巴。慢慢看,讀代碼也是一種生活方式。
其中對

  while ((c = getopt_long (argc, argv, "c:n:qv", long_options, NULL)) != -1)
    {
的處理,getopt_long的源碼一直沒看明白。我讀sed時,讀到這兒,就直想吐,你看懂了嗎?哈哈哈,下次吧。

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章