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 个回答 Voted 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; } 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; } 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 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 Bob 2013-09-27T06:50:01+08:002013-09-27T06:50:01+08:00 您可能可以使用类似的东西multitail同时跟踪多个文件。如果将其与-F选项结合使用(如果文件不存在,请重试),它可以为您提供所需的内容。 Aaron Iba 2020-09-23T21:03:38+08:002020-09-23T21:03:38+08:00 这个线程启发我编写自己的零售脚本版本。我的版本retail跨平台工作,包括在 macOS 上。
感谢大家的支持,但是由于 mutitail 或 tail -F 或 watch tail 似乎都无法满足我的需要,因此我用 C 开发了一个小解决方案。我在这里发布了代码,因为也许有人会发现它有用。(我知道有一些缺失的检查和一些弱点,但到目前为止就足够了)
我对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
功能检查修改后的文件是否被截断,并打印出它是否已被截断,以及更新
item->last_position = -1
以便重新打印文件。我不认为只有 using 有一种方法
tail
,但是您应该能够与watch
with 一起使用来达到相同的效果tail
。唯一的危险就是确保你没有创建一个目录而不仅仅是一个新文件,这可以通过确保使用适当的 shell glob 传递给 tail 来减轻。例子:watch -n 2 tail *.log
你可以使用:
tail -F logs/*
额外提示:查看multitail,这是一个很棒的小命令。
例如:将所有 apache 日志文件 (*access_log/*error_log) 合并到一个窗口中:
显示 5 个日志文件,同时合并 2 个并将它们放在 2 列中,左列中只有一个:
您可能可以使用类似的东西
multitail
同时跟踪多个文件。如果将其与-F
选项结合使用(如果文件不存在,请重试),它可以为您提供所需的内容。这个线程启发我编写自己的零售脚本版本。我的版本
retail
跨平台工作,包括在 macOS 上。