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

本文涉及的产品
实时计算 Flink 版,5000CU*H 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' 库现在能够更稳定地监控文件变化,即使在文件被清空或日志轮转的情况下也能继续工作。这使得它成为一个可靠的工具,可用于各种需要实时监控文件更新的场景

相关文章
|
7天前
|
Unix Shell Linux
nohup python -u ai_miniprogram_main.py > ../iwork.out 2>&1 & 这句命令是做什么的?
nohup python -u ai_miniprogram_main.py > ../iwork.out 2>&1 & 这句命令是做什么的?
9 1
|
25天前
|
设计模式 开发者 Python
Python中循环依赖问题及其解决方案
循环依赖是 Python 开发中需要特别注意的问题。通过重新设计模块结构、延迟导入、依赖注入、利用 Python 的动态特性以及代码重构等方法,可以有效地解决循环依赖问题。这些策略不仅有助于提高代码的可维护性和可读性,还能避免潜在的运行时错误。在实际开发中,开发者应该根据具体情况选择合适的解决方案。
|
26天前
|
算法 Unix 程序员
[oeasy]python031_[趣味拓展]unix起源_Ken_Tompson_Ritchie_multics
回顾了上次内容关于调试的基本概念后,本文介绍了Unix操作系统的起源。从早期计算机任务的独占执行方式讲起,到1960年代 Dartmouth 开发出分时算法,使得一台主机能够面对多个终端,轮流使用CPU时间片。随后,贝尔实验室、通用电气和MIT合作开展Multics项目,项目成员Kenneth Thompson与Dennis Ritchie在此期间开发了一款名为《Space Travel》的游戏。然而,因金融动荡等原因,Multics项目最终被裁撤。Thompson和Ritchie后来利用实验室闲置的一台PDP-7计算机仅用三周时间就开发出了Unix操作系统的第一版,初衷是为了能够继续玩游戏。
33 3
|
27天前
|
API 开发工具 网络架构
【Azure Developer】使用Python SDK去Azure Container Instance服务的Execute命令的疑问解释
【Azure Developer】使用Python SDK去Azure Container Instance服务的Execute命令的疑问解释
【Azure Developer】使用Python SDK去Azure Container Instance服务的Execute命令的疑问解释
|
1月前
|
Unix 数据处理 Perl
|
1月前
|
前端开发 计算机视觉
Building wheel for opencv-python (pyproject.toml) ,安装命令增加 --verbose 参数
Building wheel for opencv-python (pyproject.toml) ,安装命令增加 --verbose 参数
111 2
|
1月前
|
Linux 网络安全 Python
Linux离线安装Python时ssh和hashlib死活安装不上的解决方案
本文提供了Linux环境下离线安装Python时遇到的"ImportError: No module named _ssl"和"ERROR:root:code for hash md5|sha1|sha224|sha256|sha384|sha512 was not found"两个问题的解决方案,通过设置OpenSSL环境变量和编辑Python源码配置文件来解决。
18 1
|
1月前
|
API 开发工具 网络架构
【Azure Developer】使用Python SDK去Azure Container Instance服务的Execute命令的疑问解释
Azure 容器实例(Azure Container Instances,简称 ACI)是一个无服务器容器解决方案,允许用户在 Azure 云环境中运行 Docker 容器,而无需设置虚拟机、集群或编排器。 ACI 适用于任何可以在隔离容器中操作的场景,包括事件驱动的应用程序、从容器开发管道快速部署、数据处理和生成作业。
|
24天前
|
Linux Shell 数据库
python Django教程 之 安装、基本命令、视图与网站
python Django教程 之 安装、基本命令、视图与网站
|
28天前
|
Unix Python
如何使 Python 脚本在 Unix 上可执行
【8月更文挑战第24天】
14 0

热门文章

最新文章