分享

Understanding Unix/Linux Programming习题:tail命令...

 WUCANADA 2013-06-29

tail这个命令实在变态,在参考了BSD和GNU的版本后,终于写出自己的版本。。。

ps:BSD那个版本很难看懂,可能是因为是1977年写的关系吧。。。

下面是代码,仅供参考(小弟水平有限,只实现了主要的功能:显示倒数n行,显示倒数n字节,标准输入显示倒数10行)

#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <getopt.h>
#include <sys/types.h>
#include <sys/stat.h>

#define PIPE_FILE "/tmp/tail_fifo"

void tail_lines(int fd, int lines);
void tail_bytes(int fd, off_t bytes);
void pipe_lines();

/*
 * pipe_lines function is usef for read the 10 lines from the end of standard input
 * File description res is the file read from the standard input
 * Finally, call "tail_lines" function to aim the target.
 */
void pipe_lines()
{
    int nread;
    int res;
    char buf[BUFSIZ];
    res = open(PIPE_FILE, O_RDWR|O_CREAT|O_TRUNC, S_IRUSR|S_IWUSR);
    if(res == -1)
    {
        fprintf(stderr, "Could not read the pipe file\n");
        exit(1);
    }
    while((nread = read(fileno(stdin), &buf, BUFSIZ)) > 0)
    {
        write(res, &buf, nread);
    }
    tail_lines(res, 10);
    close(res);
}

/*
 * tail_lines function is used for print the last "lines" lines from the end of file fd
 */
void tail_lines(int fd, int lines)
{
    char buf[BUFSIZ];/*buffer*/
    int nread;
    int i;/*for scanning the buf*/
    off_t pos = lseek(fd, (off_t)0, SEEK_END);/* locate the pos to the end of file */
    int size = (int)pos;
    nread = (int)pos % BUFSIZ; /* partitioning the file with BUFSIZ, get the remaining part */
    if(nread == 0)
        nread = BUFSIZ;
    pos -= nread;

    /*
     * Scanning the remaining part
     * It is also useful for the condition that file size is smaller than BUFSIZ
     */
    lseek(fd, pos, SEEK_SET);
    if((nread = read(fd, &buf, nread)) <= 0)
    {
        fprintf(stderr, "Could not open the file\n");
        exit(1);
    }
    for(i = nread - 1; i >= 0; i--)
    {
        if(buf[i] == '\n' || buf[i] == EOF)
            lines--;
        if(lines < 0)
        {
            pos += i + 1;
            break;
        }
    }
    /*
     * If lines is not enough, print all the file
     */
    if(lines >= 0)
        pos = (off_t)0;
    /*
     * scannning for N * BUFSIZ
     */
    while(lines >= 0 && size > BUFSIZ)
    {
        pos -=BUFSIZ;
        if(pos < 0)
        {
            pos = (off_t)0;
            break;
        }
        lseek(fd, pos, SEEK_SET);
        if((nread = read(fd, &buf, BUFSIZ)) <= 0)
        {
            perror("Could not read the file");
            exit(1);
        }
        for(i = nread - 1; i >=0; i--)
        {
            if(buf[i] == '\n'|| buf[i] == EOF)
                lines--;
            if(lines < 0)
            {
                pos += i + 1;
                break;
            }
        }
    }
    /*
     * Locate the target
     * Print the result
     */
    lseek(fd, pos, SEEK_SET);
    while((nread = read(fd, &buf, BUFSIZ)) > 0)
    {
        write(fileno(stdout), &buf, nread);
    }
}

/*
 * Tail_bytes function is used to print the last "bytes" bytes from the end of file fd
 */
void tail_bytes(int fd, off_t bytes)
{
    off_t pos;
    int nread;
    char buf[BUFSIZ];
   
    pos = lseek(fd, (off_t)0, SEEK_END);
    if(pos < bytes)
    {
        fprintf(stderr, "The offset %d is bigger than file's size\n", (int)bytes);
        exit(1);
    }

    pos -= bytes;
    lseek(fd, pos, SEEK_SET);
    while((nread = read(fd, &buf, BUFSIZ)) > 0)
    {
        write(fileno(stdout), &buf, nread);
    }
}





int main(int argc, char *argv[])
{
    int fd;
    int opt;
    int off_bytes = -1;
    int off_lines = -1;
    char *ep;
    char *path;

    if(argc == 1)
    {
        pipe_lines();
        exit(0);
    }

    struct option options[] = {
        {"bytes", 1, NULL, 'c'},
        {"lines", 1, NULL, 'n'},
        {0, 0, 0, 0}
    };


    while((opt = getopt_long(argc, argv, ":n:c:", options, NULL)) != -1)
    {
        switch(opt)
        {
            case 'c':
                off_bytes = strtoimax(optarg, &ep, 10);
                if(*ep || off_bytes == 0)
                {
                    fprintf(stderr, "Illegal bytes count\n");
                    exit(1);
                }
                break;
            case 'n':
                off_lines = strtol(optarg, &ep, 10);
                if(*ep || off_bytes == 0)
                {
                    fprintf(stderr, "Illegal lines count\n");
                    exit(1);
                }
                break;
            case ':':
                perror("Option needs a value");
                exit(1);
                break;
            case '?':
                perror("Unknown options");
                exit(1);
                break;
        }
    }
    path = argv[optind];
    if(access(path, F_OK) != 0)
    {
        fprintf(stderr, "File not found %s\n", path);
        exit(1);
    }
   
    fd = open(path, O_RDONLY);
    if(fd == -1)
    {
        perror(path);
        exit(1);
    }
   
    if(off_bytes == -1)
        tail_lines(fd, off_lines);
    else
        tail_bytes(fd, off_bytes);

    close(fd);
    exit(0);
}


收藏到:Del.icio.us


  struct option {
               const char *name;
               int         has_arg;
               int        *flag;
               int         val;
           };

       The meanings of the different fields are:

       name   is the name of the long option.

       has_arg
              is: no_argument (or 0) if the option does not take an argument; required_argument (or 1) if the option requires an argument; or optional_argument (or 2) if the  option  takes  an
              optional argument.

       flag   specifies  how  results  are  returned for a long option.  If flag is NULL, then getopt_long() returns val.  (For example, the calling program may set val to the equivalent short
              option character.)  Otherwise, getopt_long() returns 0, and flag points to a variable which is set to val if the option is found, but left unchanged if the option is not found.

       val    is the value to return, or to load into the variable pointed to by flag.

       The last element of the array has to be filled with zeros.

       If longindex is not NULL, it points to a variable which is set to the index of the long option relative to longopts.

       getopt_long_only() is like getopt_long(), but '-' as well as "--" can indicate a long option.  If an option that starts with '-' (not "--") doesn't match a long option, but does match a
       short option, it is parsed as a short option instead.

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多