【问题标题】:Simultaneous read/write operations on file from different processes: working on Windows but not on Linux来自不同进程的文件同时读/写操作:在 Windows 上工作但在 Linux 上不工作
【发布时间】:2020-01-08 02:11:17
【问题描述】:

我正在使用 .log 文件中的数据创建一个 SQLite 数据库。机器每次执行操作时都会更新日志文件。我在 Windows 中所做的是监视这个文件的变化,一旦它被修改 - 机器通过串行通信在外部进行修改,但我通过在记事本中打开文件并添加新行来模拟这种行为 - 读取新行并将数据放入数据库。

问题是当我尝试对我的 Linux 机器做同样的事情时,因为显然 python 程序在文件中没有看到任何修改,即使在它被更新和保存之后。尽管如此,如果我在程序中关闭并重新打开文件以重新读取它,修改就在那里(但我仍然无法检测到新的修改)。

我实际上已经在运行在 Raspbian 上的 Raspberry Pi 3 B+ 上测试了相同的代码,它按预期工作:检测到修改toute de suite,并将数据添加到数据库中。

.log 文件的格式如下所示。

01  (PB)      008   Pa    6.01  bar         12/11/2055  07:25:17    
01  (PB)      008   Pa    6.01  bar         12/11/2055  07:26:39    
01  (PB)      009   Pa    6.00  bar         12/11/2055  07:29:45

这是我正在使用的代码:

import re
import sqlite3
import os, time

if __name__=='__main__':
    fd = open('putty.log', 'r')

    # Ici on crée la base de données qui gardera les résultats des tests

    connection = sqlite3.connect('banc_tests.db')
    cursor = connection.cursor()

    create_db = """
    CREATE TABLE tests (
        reference VARCHAR(15),
        num_serie VARCHAR(15),
        operation TINYINT,
        resultat CHAR(2),
        fuite SMALLINT,
        unite VARCHAR(5),
        pression FLOAT,
        pUnite VARCHAR(5),
        date DATE,
        time TIME
    );
    """

    cursor.execute(create_db)

    # Here we look for the testing bank frame's format with regular expressions
    # For now we are ignoring all information about low pressure, PE test, etc.

    while 1:
        where = fd.tell()  # Garde la position actuelle du curseur dans le fichier
        line = fd.readline() # On lit la ligne suivante
        if not line:
            time.sleep(1)
            fd.seek(where)  #Si il n'y a rien à lire on retourne à la position précédente.
        else: 
            # The ([\+\-]?\d*)\s*(\w*) segment in intended to ignore whitespaces in the faulty testing lines
            # as well as to grab the leak pressure for ordinary tests. To change it back to the original
            # version, it suffices to replace every * by a +.
            print(line)
            found = re.findall(r'^\s+(\d+)\s+\((\w+)\)\s+([\+\-]?\d*)\s*(\w*)\s+(\d+\.?\d+)\s+(\w+).*\s(\d+)/(\d+)/(\d+)\s+(\d+:\d+:\d+)', line)

            # La commande findall retourne une tuple dans une liste, donc ici j'enlève la tuple et laisse
            # juste la liste avec les éléments dedans.

            if found:
                temp = list(found[0])

                insert_values = """
                    INSERT INTO tests (reference, num_serie, operation, resultat, fuite, unite, pression, pUnite, date, time)
                    VALUES ("{ref}", "{num_ser}", "{op}", "{res}", "{fuite}", "{unt}", "{pression}", "{pUnt}", "{date}", "{time}"); """

                insert_command = insert_values.format(ref='', num_ser='', op=temp[0], res=temp[1], fuite=temp[2], unt=temp[3], pression=temp[4], pUnt=temp[5], date='{}-{}-{}'.format(temp[8], temp[7], temp[6]), time=temp[9])
                cursor.execute(insert_command)

                connection.commit()

我认为这是 open() 函数的阻塞行为的问题,所以我尝试将 os.O_NONBLOCK 标志与 fcntl 一起使用;不过没用。

有没有人知道是什么导致了 Linux(Debian 10 Bluster)和 Windows 之间的这种不同行为?又该如何解决Linux端的问题呢?

【问题讨论】:

  • 你在那个 Linux 上有什么类型的文件系统?分机4?
  • 取决于 FS,例如 NFS,不会更新那些时间。更好的是轮询文件并检查它的大小并从最后一个位置读取新内容
  • 我有一个 ext4 文件系统,@pako。那一定是这样的。我在我的 Windows 机器上进行了验证,它有一个 NTFS 文件系统。
  • 感谢您的建议,@Paul-AG。我将尝试这种方式,如果它按预期工作,我将在此处发布结果作为解决方案。
  • @EmanoelCosta 嗯,基本示例适用于我(我也有 ext4):pastebin.com/AbNhkSnA 它适用于您的 linux 系统吗?

标签: python linux windows file


【解决方案1】:

我做了一些研究,发现了问题。这是因为在编辑器中保存后文件的inode 发生了变化。

您可以使用vim 并设置set backupcopy=yes 来防止这种行为(http://vimdoc.sourceforge.net/htmldoc/options.html#'backupcopy'

要观察这种行为,您可以使用ls -li,它将向您显示文件的inode

pawel@pawel-XPS-15-9570:~/test$ ls -li putty.log 
10263515 -rw-r--r-- 1 pawel pawel 87 wrz  5 14:10 putty.log
pawel@pawel-XPS-15-9570:~/test$ echo 'test' >> putty.log 
pawel@pawel-XPS-15-9570:~/test$ ls -li putty.log 
10263515 -rw-r--r-- 1 pawel pawel 92 wrz  5 14:14 putty.log
pawel@pawel-XPS-15-9570:~/test$ vim putty.log 
pawel@pawel-XPS-15-9570:~/test$ ls -li putty.log 
10263513 -rw-r--r-- 1 pawel pawel 97 wrz  5 14:14 putty.log
pawel@pawel-XPS-15-9570:~/test$ 

第一列显示inode。正如您在 vim 编辑后看到的那样,它已更改,这就是您的脚本无法看到这些更改的原因(因为编辑器覆盖了此文件)。

【讨论】:

  • 非常感谢@pako!我在 Vim 中执行了 ":set backupcopy=yes",添加的行正如我预期的那样显示在我的程序中。
猜你喜欢
  • 1970-01-01
  • 2014-11-09
  • 2022-01-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-07-02
  • 1970-01-01
  • 2017-08-05
相关资源
最近更新 更多