AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • 主页
  • 系统&网络
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • 主页
  • 系统&网络
    • 最新
    • 热门
    • 标签
  • Ubuntu
    • 最新
    • 热门
    • 标签
  • Unix
    • 最新
    • 标签
  • DBA
    • 最新
    • 标签
  • Computer
    • 最新
    • 标签
  • Coding
    • 最新
    • 标签
主页 / server / 问题 / 541852
Accepted
Jack
Jack
Asked: 2013-09-27 04:55:29 +0800 CST2013-09-27 04:55:29 +0800 CST 2013-09-27 04:55:29 +0800 CST

尾 -f 新文件

  • 772

有没有办法做这样的事情:

tail -f logs/

并使标准输出在添加到日志/中已经存在的每个文件的每一行上以及在发出命令后将在日志/中创建的每个文件中更新?

tail
  • 6 6 个回答
  • 6592 Views

6 个回答

  • Voted
  1. Best Answer
    Jack
    2013-09-30T21:58:22+08:002013-09-30T21:58:22+08:00

    感谢大家的支持,但是由于 mutitail 或 tail -F 或 watch tail 似乎都无法满足我的需要,因此我用 C 开发了一个小解决方案。我在这里发布了代码,因为也许有人会发现它有用。(我知道有一些缺失的检查和一些弱点,但到目前为止就足够了)

    #include <stdio.h>
    #include <string.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <errno.h>
    #include <sys/types.h>
    #include <sys/inotify.h>
    #include <sys/stat.h>
    #include <signal.h>
    #include <dirent.h>
    #include <linux/limits.h>
    #define    CHAR_BACK   500
    
    // * File handler structure
    struct file_followed { long last_position; char filename[NAME_MAX]; struct file_followed * next; };
    struct file_followed * file_list = NULL;
    
    // * To quit peacefully
    int cycle = 1;
    void stopCycle(int u) { cycle = 0; }
    
    // * Last tailed filename
    char last_tailed[NAME_MAX];
    
    void fileAdd(char * file) {
        struct file_followed ** list = &file_list;
        struct stat statdesc;
    
        if(stat(file, &statdesc) || !S_ISREG(statdesc.st_mode)) { return; }
        while(*list) { list = &((*list)->next); }
        *list = malloc(sizeof(struct file_followed));
        (*list)->last_position = -1;
        strcpy((*list)->filename, file);
        (*list)->next = NULL;
    }
    
    int fileTail(struct file_followed * item) {
        int ret = 0;
        FILE * fp = fopen(item->filename, "r");
        fseek(fp, 0, SEEK_END);
        long end_position = ftell(fp);
    
        if( end_position != item->last_position ) {
            if(strcmp(item->filename, last_tailed)) { strcpy(last_tailed, item->filename); printf("\n** %s **:\n", item->filename); }
    
            int start_position = item->last_position == -1 || item->last_position > end_position ? (end_position-CHAR_BACK > 0 ? end_position-CHAR_BACK : 0) : item->last_position;
            fseek(fp, start_position, SEEK_SET);
    
            int len = end_position - start_position;
            char * buf = malloc(len+1);
            fread(buf, len, 1, fp);
            buf[len] = '\0';
            printf("%s%s", len == CHAR_BACK ? "[...]" : "", buf);
            free(buf);
    
            item->last_position = end_position;
            ret = 1;
        }
    
        fclose(fp);
        return ret;
    }
    
    void fileRem(char * file) {
        struct file_followed ** list = &file_list;
        while(*list && strcmp((*list)->filename, file)) { list = &((*list)->next); }
        if(*list) { struct file_followed * todel = *list; *list = (*list)->next; free(todel); }
    }
    
    int main(int argc, char ** argv) {
    
        struct dirent **namelist;
        struct stat statdesc;
        struct timeval tv;
        fd_set set;
        int fd;
        int wd;
        int r;
    
        // * Help
        if(stat(argv[1], &statdesc) || !S_ISDIR(statdesc.st_mode)) { printf("[usage] %s dir-to-monitor\n", argv[0]); exit(EXIT_FAILURE); }
    
        // * Init
        chdir(argv[1]);
        memset(last_tailed, 0, sizeof(last_tailed));
        signal(SIGINT, stopCycle);
        signal(SIGTERM, stopCycle);
    
        // * Inotify
        if( (fd = inotify_init()) < 0) { perror("inotify_init"); }
        if( (wd = inotify_add_watch( fd, ".", IN_CREATE | IN_DELETE ) < 0)) { perror("inotify_add_watch"); }
    
        // * File add recursively on dirscan
        if( (r = scandir(".", &namelist, 0, alphasort)) < 0) { perror("scandir"); }
        while (r--) { fileAdd(namelist[r]->d_name); free(namelist[r]); }
        free(namelist);
    
        // * Neverending cycle
        while(cycle) {
            // * Select on inotify
            FD_ZERO(&set);
            FD_SET(fd, &set);
            tv.tv_sec = 0;
            tv.tv_usec = 1000;
            if( (r = select(fd+1, &set, NULL, NULL, &tv)) == -1) { perror("select"); }
    
            // * New add or del on inotify
            if(r) {
                struct inotify_event * event;
                char buf[1024];
                if(read(fd, buf, 1024) <= 0) { perror("read"); }
                event = (struct inotify_event *) buf;
                if(event->mask & IN_CREATE) { fileAdd(event->name); } 
                else if(event->mask & IN_DELETE) { fileRem(event->name); }
            }
    
            // * Check for new tails
            struct file_followed * list = file_list;
            int tailers = 0;
            while(list) { tailers += fileTail(list); list = list->next; }
            if(!tailers) { usleep(500000); }
        }
    
        // * Stop inotify
        inotify_rm_watch( fd, wd );
        close(fd);
    
        return EXIT_SUCCESS;
    }
    
    • 8
  2. ekangas
    2017-04-04T01:41:23+08:002017-04-04T01:41:23+08:00

    我对https://serverfault.com/a/542580/203373进行 了更改, 以修复我系统上的几个编译错误(使用 Ubuntu linux)。(struct file_followed*)我在and中添加了强制转换(char*),并包含 IN_MODIFY在添加监视列表中以监视对当前文件的修改。添加了这一行:

    if(event->mask & IN_MODIFY) { fileMod(event->name, file_list); }

    和fileMod功能

    void fileMod(char* fileName, struct file_followed* file_list)
    

    检查修改后的文件是否被截断,并打印出它是否已被截断,以及更新item->last_position = -1以便重新打印文件。

    #include <stdio.h>
    #include <string.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <errno.h>
    #include <sys/types.h>
    #include <sys/inotify.h>
    #include <sys/stat.h>
    #include <signal.h>
    #include <dirent.h>
    #include <linux/limits.h>
    #define    CHAR_BACK   500
    
    // * File handler structure
    struct file_followed { long last_position; char filename[NAME_MAX]; struct file_followed * next; };
    struct file_followed * file_list = NULL;
    
    // * To quit peacefully
    int cycle = 1;
    void stopCycle(int u) { cycle = 0; }
    
    // * Last tailed filename
    char last_tailed[NAME_MAX];
    
    void fileAdd(char * file) {
        struct file_followed ** list = &file_list;
        struct stat statdesc;
    
        if(stat(file, &statdesc) || !S_ISREG(statdesc.st_mode)) { return; }
        while(*list) { list = &((*list)->next); }
        *list = (struct file_followed*)malloc(sizeof(struct file_followed));
        (*list)->last_position = -1;
        strcpy((*list)->filename, file);
        (*list)->next = NULL;
    }
    
    void fileMod(char* fileName, struct file_followed* file_list) {
        struct file_followed* item = file_list;
        while(item) { 
            if(strcmp(item->filename, fileName) == 0) {
                FILE* fp = fopen(item->filename, "r");
                fseek(fp, 0, SEEK_END);
                long end_position = ftell(fp);
                fclose(fp);
                if (end_position <= item->last_position) {
                    printf("\n** %s truncated **\n", fileName);
                    item->last_position = -1;
                }
                usleep(100);
                return;
            }
            item = item->next;
        }
    }
    
    int fileTail(struct file_followed * item) {
        int ret = 0;
        FILE * fp = fopen(item->filename, "r");
        fseek(fp, 0, SEEK_END);
        long end_position = ftell(fp);
    
        if( end_position != item->last_position ) {
            if(strcmp(item->filename, last_tailed)) { strcpy(last_tailed, item->filename); printf("\n** %s **:\n", item->filename); }
    
            int start_position = item->last_position == -1 || item->last_position > end_position ? (end_position-CHAR_BACK > 0 ? end_position-CHAR_BACK : 0) : item->last_position;
                        fseek(fp, start_position, SEEK_SET);
    
            int len = end_position - start_position;
            char * buf = (char*)malloc(len+1);
            fread(buf, len, 1, fp);
            buf[len] = '\0';
            printf("%s%s", len == CHAR_BACK ? "[...]" : "", buf);
            free(buf);
    
            item->last_position = end_position;
            ret = 1;
        }
    
        fclose(fp);
        return ret;
    }
    
    void fileRem(char * file) {
        struct file_followed ** list = &file_list;
        while(*list && strcmp((*list)->filename, file)) { list = &((*list)->next); }
        if(*list) { struct file_followed * todel = *list; *list = (*list)->next; free(todel); }
    }
    
    int main(int argc, char ** argv) {
    
        struct dirent **namelist;
        struct stat statdesc;
        struct timeval tv;
        fd_set set;
        int fd;
        int wd;
        int r;
    
        // * Help
        if(stat(argv[1], &statdesc) || !S_ISDIR(statdesc.st_mode)) { printf("[usage] %s dir-to-monitor\n", argv[0]); exit(EXIT_FAILURE); }
    
        // * Init
        chdir(argv[1]);
        memset(last_tailed, 0, sizeof(last_tailed));
        signal(SIGINT, stopCycle);
        signal(SIGTERM, stopCycle);
    
        // * Inotify
        if( (fd = inotify_init()) < 0) { perror("inotify_init"); }
        if( (wd = inotify_add_watch( fd, ".", IN_CREATE | IN_MODIFY |IN_DELETE ) < 0)) { perror("inotify_add_watch"); }
    
        // * File add recursively on dirscan
        if( (r = scandir(".", &namelist, 0, alphasort)) < 0) { perror("scandir"); }
        while (r--) { fileAdd(namelist[r]->d_name); free(namelist[r]); }
        free(namelist);
    
        // * Neverending cycle
        while(cycle) {
            // * Select on inotify
            FD_ZERO(&set);
            FD_SET(fd, &set);
            tv.tv_sec = 0;
            tv.tv_usec = 1000;
            if( (r = select(fd+1, &set, NULL, NULL, &tv)) == -1) { perror("select"); }
    
            // * New add or del on inotify
            if(r) {
                struct inotify_event * event;
                char buf[1024];
                if(read(fd, buf, 1024) <= 0) { perror("read"); }
                event = (struct inotify_event *) buf;
                if(event->mask & IN_MODIFY) { fileMod(event->name, file_list);} 
                else if(event->mask & IN_CREATE) { fileAdd(event->name); } 
                else if(event->mask & IN_DELETE) { fileRem(event->name); }
            }
    
            // * Check for new tails
            struct file_followed * list = file_list;
            int tailers = 0;
            while(list) { tailers += fileTail(list); list = list->next; }
            if(!tailers) { usleep(500000); }
        }
    
        // * Stop inotify
        inotify_rm_watch( fd, wd );
        close(fd);
    
        return EXIT_SUCCESS;
    }
    
    • 3
  3. John
    2013-09-27T05:12:39+08:002013-09-27T05:12:39+08:00

    我不认为只有 using 有一种方法tail,但是您应该能够与watchwith 一起使用来达到相同的效果tail。唯一的危险就是确保你没有创建一个目录而不仅仅是一个新文件,这可以通过确保使用适当的 shell glob 传递给 tail 来减轻。例子:watch -n 2 tail *.log

    • 1
  4. rafi
    2013-09-27T06:48:33+08:002013-09-27T06:48:33+08:00

    你可以使用:tail -F logs/*

    额外提示:查看multitail,这是一个很棒的小命令。

    例如:将所有 apache 日志文件 (*access_log/*error_log) 合并到一个窗口中:

    multitail -cS apache --mergeall /var/log/apache2/*access_log --no-mergeall \  
      -cS apache_error --mergeall /var/log/apache2/*error_log --no-mergeall
    

    显示 5 个日志文件,同时合并 2 个并将它们放在 2 列中,左列中只有一个:

    multitail -s 2 -sn 1,3  /var/log/apache/access.log -I /var/log/apache/error.log \ 
      /var/log/messages /var/log/mail.log /var/log/syslog
    
    • 0
  5. Bob
    2013-09-27T06:50:01+08:002013-09-27T06:50:01+08:00

    您可能可以使用类似的东西multitail同时跟踪多个文件。如果将其与-F选项结合使用(如果文件不存在,请重试),它可以为您提供所需的内容。

    • 0
  6. Aaron Iba
    2020-09-23T21:03:38+08:002020-09-23T21:03:38+08:00

    这个线程启发我编写自己的零售脚本版本。我的版本retail跨平台工作,包括在 macOS 上。

    • 0

相关问题

  • 跟踪多个远程文件

  • 实时监控多个 Linux 日志文件

  • 持续监控带有偶尔旋转的尾部的日志

  • 除了 ssh 终端之外,在 Windows 中通过 ssh 跟踪 httpd 日志的应用程序[关闭]

  • 方便的 Windows 相当于 tail -f 日志文件?

Sidebar

Stats

  • 问题 205573
  • 回答 270741
  • 最佳答案 135370
  • 用户 68524
  • 热门
  • 回答
  • Marko Smith

    新安装后 postgres 的默认超级用户用户名/密码是什么?

    • 5 个回答
  • Marko Smith

    SFTP 使用什么端口?

    • 6 个回答
  • Marko Smith

    命令行列出 Windows Active Directory 组中的用户?

    • 9 个回答
  • Marko Smith

    什么是 Pem 文件,它与其他 OpenSSL 生成的密钥文件格式有何不同?

    • 3 个回答
  • Marko Smith

    如何确定bash变量是否为空?

    • 15 个回答
  • Martin Hope
    Tom Feiner 如何按大小对 du -h 输出进行排序 2009-02-26 05:42:42 +0800 CST
  • Martin Hope
    Noah Goodrich 什么是 Pem 文件,它与其他 OpenSSL 生成的密钥文件格式有何不同? 2009-05-19 18:24:42 +0800 CST
  • Martin Hope
    Brent 如何确定bash变量是否为空? 2009-05-13 09:54:48 +0800 CST
  • Martin Hope
    cletus 您如何找到在 Windows 中打开文件的进程? 2009-05-01 16:47:16 +0800 CST

热门标签

linux nginx windows networking ubuntu domain-name-system amazon-web-services active-directory apache-2.4 ssh

Explore

  • 主页
  • 问题
    • 最新
    • 热门
  • 标签
  • 帮助

Footer

AskOverflow.Dev

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve