Python 实现 Unix 'tail' 命令的完整解决方案

本文涉及的产品
实时数仓Hologres,5000CU*H 100GB 3个月
检索分析服务 Elasticsearch 版,2核4GB开发者规格 1个月
智能开放搜索 OpenSearch行业算法版,1GB 20LCU 1个月
简介: 这是一个关于Python实现的 tail 命令库的摘要,最初由一个斯里兰卡程序员编写。代码中存在一个问题,在获取文件大小时可能抛出文件不存在的异常,已修复此 bug,添加了错误处理。修复后的代码包含一个名为 `wait_file_get_size` 的函数,用于安全地获取文件大小。此外,提供了类 `Tail`,用于监视文件变化,并可注册回调函数处理新行。

该文章是我发布在某sdn上的,搬运过来。


Python 实现 Unix 'tail' 命令的完整解决方案

在本文中,我们将探讨如何用 Python 编写一个类似于 Unix 中 'tail -f' 功能的库。此库能够监测文件的变化,并在文件有新行添加时执行相应的操作。我们还将修复在特定场景下的 bug,确保代码的健壮性和实用性。

一、初始版本与问题

最初的 Python 'tail' 库由一个斯里兰卡程序员创建并发布在 GitHub 上,

https://github.com/kasun/python-tail

第二版作者:http://www.cnblogs.com/bufferfly/p/4878688.html

然而,经过实际应用,发现存在一些问题,特别是当被监控的文件在监控过程中被清空或因日志轮转而重命名时,库会失去追踪能力。


二、问题分析与修复

在原始代码中,_size = os.path.getsize(self.tailed_file) 这一行代码可能抛出异常,因为被监控的文件可能在某些时刻不存在。这会导致监控程序意外终止,影响了其持续监控的能力。为了修复这个问题,我们在获取文件大小的操作周围添加了异常处理逻辑,并在文件不存在时引入了重试机制。


三、优化后的代码示例

下面是优化后的代码示例,包括异常处理和重试机制,以及如何注册回调函数和开始监控文件的流程。




#!-*- coding: utf-8 -*-
 
################################################################################
#
# Copyright (c) 2015 XX.com, Inc. All Rights Reserved
#
################################################################################
 
################################################################################
# This module provide ...
# Third libs those depend on:
################################################################################
 
"""
Compiler Python 2.7.10
Authors:  xingxinghuo1000@163.com
Date: 2017-07-31
Desc:类似于tail命令的python lib , 可以注册一个回调函数,每读取一行,则触发一次回调
Modify: 1、修复一个bug,   _size=os.path.getsize 那一行,会出现异常,提示文件不存在,加了try和等待
"""
 
"""SYS LIBS
"""
 
 
import os
import re
import sys
import time
 
 
"""THIRD LIBS
"""
 
try:
    # import the third libs there.
    pass
except ImportError as e:
    print e
    os._exit(-1)
 
"""CUSTOM libs
Strongly recommend using abs path when using custmo libs.
"""
 
# Good behaviors.
# It means refusing called like from xxx import *
# When `__all__` is []
__all__ = []
 
reload(sys)
sys.setdefaultencoding('utf-8')
 
 
def send_error(msg):
    """ Send error to email.
    """
 
    print msg
 
 
#********************************************************
#* Global defines start.                                *
#********************************************************
 
#********************************************************
#* Global defines end.                                  *
#********************************************************
 
 
class Tail(object):
    """
    Python-Tail - Unix tail follow implementation in Python.
    python-tail can be used to monitor changes to a file.
    Example:
        import tail
        # Create a tail instance
        t = tail.Tail('file-to-be-followed')
        # Register a callback function to be called when a new line is found in the followed file.
        # If no callback function is registerd, new lines would be printed to standard out.
        t.register_callback(callback_function)
        # Follow the file with 5 seconds as sleep time between iterations.
        # If sleep time is not provided 1 second is used as the default time.
        t.follow(s=5)
    """
 
    ''' Represents a tail command. '''
    def __init__(self, tailed_file):
        ''' Initiate a Tail instance.
            Check for file validity, assigns callback function to standard out.
            Arguments:
                tailed_file - File to be followed. '''
        self.check_file_validity(tailed_file)
        self.tailed_file = tailed_file
        self.callback = sys.stdout.write
 
        self.try_count = 0
 
        try:
            self.file_ = open(self.tailed_file, "r")
            self.size = os.path.getsize(self.tailed_file)
 
            # Go to the end of file
            self.file_.seek(0, 2)
        except:
            raise
 
    def reload_tailed_file(self):
        """ Reload tailed file when it be empty be `echo "" > tailed file`, or
            segmentated by logrotate.
        """
        try:
            self.file_ = open(self.tailed_file, "r")
            self.size = os.path.getsize(self.tailed_file)
 
            # Go to the head of file
            self.file_.seek(0, 1)
 
            return True
        except:
            return False
 
 
    def wait_file_get_size(self, file):
        while 1:
            try: 
                _size = os.path.getsize(self.tailed_file)
                return _size
            except:
                print("Error when getsize of tailed_file")
                time.sleep(0.1)
                continue
 
 
    def follow(self, s=0.01):
        """ Do a tail follow. If a callback function is registered it is called with every new line.
        Else printed to standard out.
        Arguments:
            s - Number of seconds to wait between each iteration; Defaults to 1. """
 
        while True:
            _size = self.wait_file_get_size(self.tailed_file)
            if _size < self.size:
                while self.try_count < 10:
                    if not self.reload_tailed_file():
                        self.try_count += 1
                    else:
                        self.try_count = 0
                        self.size = os.path.getsize(self.tailed_file)
                        break
                    time.sleep(0.1)
 
                if self.try_count == 10:
                    raise Exception("Open %s failed after try 10 times" % self.tailed_file)
            else:
                self.size = _size
 
            curr_position = self.file_.tell()
            line = self.file_.readline()
            if not line:
                self.file_.seek(curr_position)
            elif not line.endswith("\n"):
                self.file_.seek(curr_position)
            else:
                self.callback(line)
            time.sleep(s)
 
    def register_callback(self, func):
        """ Overrides default callback function to provided function. """
        self.callback = func
 
    def check_file_validity(self, file_):
        """ Check whether the a given file exists, readable and is a file """
        if not os.access(file_, os.F_OK):
            raise TailError("File '%s' does not exist" % (file_))
        if not os.access(file_, os.R_OK):
            raise TailError("File '%s' not readable" % (file_))
        if os.path.isdir(file_):
            raise TailError("File '%s' is a directory" % (file_))
 
 
class TailError(Exception):
    """ Custom error type.
    """
 
    def __init__(self, msg):
        """ Init.
        """
        self.message = msg
 
    def __str__(self):
        """ str.
        """
        return self.message
 
 
 
 
if __name__ == '__main__':
    t = Tail(sys.argv[1])
    def print_msg(msg):
        print msg
         
        #print msg.split(']')[0]
 
    t.register_callback(filter_and_post)
 
    t.follow()
 
""" vim: set ts=4 sw=4 sts=4 tw=100 et: """


四、总结

通过以上的修改,我们的 Python 'tail' 库现在能够更稳定地监控文件变化,即使在文件被清空或日志轮转的情况下也能继续工作。这使得它成为一个可靠的工具,可用于各种需要实时监控文件更新的场景

相关文章
|
2月前
|
安全 网络安全 文件存储
思科设备巡检命令Python脚本大集合
【10月更文挑战第18天】
86 1
思科设备巡检命令Python脚本大集合
|
28天前
|
数据可视化 搜索推荐 Shell
Python与Plotly:B站每周必看榜单的可视化解决方案
Python与Plotly:B站每周必看榜单的可视化解决方案
|
19天前
|
数据采集 API 定位技术
Python技术进阶:动态代理IP的跨境电商解决方案
Python技术进阶:动态代理IP的跨境电商解决方案
|
2月前
|
Python
Python PDB命令介绍
【10月更文挑战第15天】 使用PDB的方式有两种,其中一种是在脚本中添加代码,不觉得这种方式比print好在哪里,所以这种方式此文不表。这里我们只学习PDB的命令行使用方式
49 4
|
2月前
|
机器学习/深度学习 算法 Python
深度解析机器学习中过拟合与欠拟合现象:理解模型偏差背后的原因及其解决方案,附带Python示例代码助你轻松掌握平衡技巧
【10月更文挑战第10天】机器学习模型旨在从数据中学习规律并预测新数据。训练过程中常遇过拟合和欠拟合问题。过拟合指模型在训练集上表现优异但泛化能力差,欠拟合则指模型未能充分学习数据规律,两者均影响模型效果。解决方法包括正则化、增加训练数据和特征选择等。示例代码展示了如何使用Python和Scikit-learn进行线性回归建模,并观察不同情况下的表现。
428 3
|
2月前
|
机器学习/深度学习 缓存 PyTorch
pytorch学习一(扩展篇):miniconda下载、安装、配置环境变量。miniconda创建多版本python环境。整理常用命令(亲测ok)
这篇文章是关于如何下载、安装和配置Miniconda,以及如何使用Miniconda创建和管理Python环境的详细指南。
476 0
pytorch学习一(扩展篇):miniconda下载、安装、配置环境变量。miniconda创建多版本python环境。整理常用命令(亲测ok)
|
2月前
|
算法 Unix 数据安全/隐私保护
Python编程--UNIX口令破解机
Python编程--UNIX口令破解机
27 1
|
2月前
|
Python
python中3种获取cookie解决方案
python中3种获取cookie解决方案
25 0
|
2月前
|
机器学习/深度学习 缓存 Linux
python环境学习:pip介绍,pip 和 conda的区别和联系。哪个更好使用?pip创建虚拟环境并解释venv模块,pip的常用命令,conda的常用命令。
本文介绍了Python的包管理工具pip和环境管理器conda的区别与联系。pip主要用于安装和管理Python包,而conda不仅管理Python包,还能管理其他语言的包,并提供强大的环境管理功能。文章还讨论了pip创建虚拟环境的方法,以及pip和conda的常用命令。作者推荐使用conda安装科学计算和数据分析包,而pip则用于安装无法通过conda获取的包。
132 0
|
2月前
|
Java C语言 Python
解析Python中的全局解释器锁(GIL):影响、工作原理及解决方案
解析Python中的全局解释器锁(GIL):影响、工作原理及解决方案
48 0