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
    • 最新
    • 标签
主页 / unix / 问题 / 554061
Accepted
Gryu
Gryu
Asked: 2019-11-26 06:32:21 +0800 CST2019-11-26 06:32:21 +0800 CST 2019-11-26 06:32:21 +0800 CST

如何监控由空字节填充的文件的变化?

  • 772

我有一个由空字节填充的 10Mb 文件。程序正在访问它并将零更改为特定字符串,直到文件末尾。

我尝试使用tail -F | grep wanted_text | grep -v "unwanted_text",但它不监视更改。它仅适用于通常的文本文件,但不适用于由零填充的文件。

所有空字节都被替换为由换行符分隔的行,直到文件末尾。文件填满后,它被重命名,而是创建新文件。

那么如何通过过滤输出来监视由空字节填充的文件的变化呢?

linux files
  • 5 5 个回答
  • 290 Views

5 个回答

  • Voted
  1. Paul_Pedant
    2019-11-26T17:17:54+08:002019-11-26T17:17:54+08:00

    整个概念的问题。

    1. 作者是否只用其他字符串替换 NUL 字节,或者它可以在旧字符串上写入新字符串,可能有不完整的重叠?字符串之间是否总是至少有一个 NUL 分隔符?

    2. 它也可以用新的 NUL 覆盖字符串以擦除文件的某些部分吗?

    3. 原始文件真的是 10MB 的 NUL,还是最初是稀疏文件?

    4. 鉴于我们只能通过读取整个文件来查找字符串,您准备多久执行一次?

    5. 有没有办法在写入文件时锁定文件,以防止出现竞争条件?

    6. 在整个操作过程中文件大小可以改变吗?

    awk(至少,GNU/awk)可以处理 NUL 字符和长行。它可以保留一个 NUL 范围列表(最初只是 [0,10485760]),并检查这些区域中的新碎片。不过,这不会检测到覆盖。但它无需任何额外流程即可报告所有添加内容。

    GNU/awk 有一个内置的 patsplit() 函数,它根据 RE 分隔符分割字符串,生成字段数组和分隔符数组。所以 RE /[\000]+/ 应该将所有字符串放在一个数组中,所有 NUL 都在另一个数组中重复,您可以将它们全部累加长度()以找到每个字符串在文件中的总偏移量。这看起来是一个很好的调查对象。

    顺便说一句,cat 命令确实显示 NUL 字符。您可以使用 od 命令在文件中查看它们。它们没有出现在终端中的原因是终端驱动程序忽略了它们。

    正如罗密欧建议的那样,保留前一个文件的 cksum 会告诉您它是否已更改,但不会告诉您更改的位置。因此,这可能是一个有用的优化,具体取决于更新的频率。

    • 1
  2. Paul_Pedant
    2019-11-27T16:44:08+08:002019-11-27T16:44:08+08:00

    我已经做了足够多的工作来验证我将 GNU/awk 与 patsplit() 结合使用的概念是否可行。设置一个假 Writer 大约需要 70% 的开发时间。我找到了一组 dd 选项,可以让我设置一个 10MB 的文件,然后定期在其中的随机位置写入字符串。

    我有一个阅读器,它将整个东西作为一个长字符串拖到内存中,并将空值分隔到一个数组中,将字符串分隔到另一个数组中。读取 10MB 需要 0.044 秒,将字符串拆分为数组需要 0.989 秒,报告我放置的 20 个字符串的开始、长度和内容需要 0.138 秒。所以大约需要 1.2 秒来做一个文件快照。

    所有计时都是在我使用了 8 年的便宜笔记本电脑上完成的。我认为,因为无论如何它都必须解析整个 10MB,所以有更多的字符串不会对性能造成那么严重的影响。下一步是确认这一点。

    我相信保留字符串的新旧哈希表并查找更改将简单有效。

    对这里的数据添加字符串有更多了解吗?如果它总是与以前的数据连续,那么通过查看前一个字符串就可以很容易地模拟 tail。如果它不频繁,我们可以在读取文件之前检查时间戳。如果它正在将索引写入文件的第一部分,那么我们可以先检查一下。这个文件的整个概念使得很难看出它对系统的其余部分有什么用——它只是一种使用存储的敌对方式。

    这个问题还有兴趣吗?我没有看到 OP 对我之前的问题做出任何回应,但在我看来,字符串重叠等只​​会显示为更新和长度变化。

    • 1
  3. Paul_Pedant
    2019-12-03T13:55:19+08:002019-12-03T13:55:19+08:00

    我已经测试了 Writer(以生成测试)和 Reader(您要求的脚本)的代码,并将在稍后发布。这是一个 30 秒的测试运行,以证明这是有效的。Writer 在开始使用 dd 时创建一个 10MB 的文件,并使用 od 转储它,并将每个输出报告为 Write Pos。数据是脚本文件中的随机行。Reader 只显示长度和内容(每个长度短 1,因为写入包含 NL,读取丢弃 NL)。当您决定如何以及何时发生这种情况时,我们需要修复文件之间的转换。

    Paul--) ~/wdog -w 1m; ./Reader & sleep 3; ./Writer & jobs; wait
    [1] 25030
    21:40:00.032622643: READER Begins
    21:40:00.059864870: No file: ./myNullFile
    [2] 25066
    [1]-  Running                 ./Reader &
    [2]+  Running                 ./Writer &
    1+0 records in
    1+0 records out
    10485760 bytes (10 MB, 10 MiB) copied, 0.0421849 s, 249 MB/s
    0000000  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00
            nul nul nul nul nul nul nul nul nul nul nul nul nul nul nul nul
    *
    10485760
    21:40:03.155837791: WRITER Begins
    21:40:03.166173417: Reading: ./myNullFile
    Initial NUL is at 0
    21:40:04.053667668: Write Pos          0 Lth  32 Txt :print Tx[Row] > Wk; close (Wk);:
    21:40:04.221015418  31 :print Tx[Row] > Wk; close (Wk);:
    21:40:07.453403168: Write Pos         32 Lth  26 Txt :Row = int (nTx * rand());:
    21:40:08.320950327: Write Pos         58 Lth  26 Txt :Row = int (nTx * rand());:
    21:40:08.331308212  25 :Row = int (nTx * rand());:
    21:40:08.331308212  25 :Row = int (nTx * rand());:
    21:40:11.526810010: Write Pos         84 Lth  42 Txt :n = patsplit (buf, Str, /[^\000]+/, Sep);:
    21:40:12.444730213  41 :n = patsplit (buf, Str, /[^\000]+/, Sep);:
    21:40:12.998876406: Write Pos        126 Lth  51 Txt :function Monitor (Local, rs, tk, Buf, Now, P, Q) {:
    21:40:13.474633987  50 :function Monitor (Local, rs, tk, Buf, Now, P, Q) {:
    21:40:14.604161200: Write Pos        177 Lth  43 Txt :Parser( Q, Buf); Differ( Q, P); Now = "P";:
    21:40:15.529375510  42 :Parser( Q, Buf); Differ( Q, P); Now = "P";:
    21:40:18.381688129: Write Pos        220 Lth  44 Txt :cmd | getline ts; close (cmd); return (ts);:
    21:40:18.611992430  43 :cmd | getline ts; close (cmd); return (ts);:
    21:40:19.583131365: Write Pos        264 Lth  19 Txt :split ("", X, FS);:
    21:40:19.642066181  18 :split ("", X, FS);:
    21:40:20.670745471: Write Pos        283 Lth   2 Txt :}:
    21:40:21.701154684   1 :}:
    21:40:23.060019472: Write Pos        285 Lth   5 Txt :done:
    21:40:23.749500189   4 :done:
    21:40:25.956196125: Write Pos        290 Lth   2 Txt :}:
    21:40:26.832889643   1 :}:
    21:40:27.098544055: Write Pos        292 Lth  19 Txt :split ("", X, FS);:
    21:40:27.861066600  18 :split ("", X, FS);:
    21:40:30.707892127: Write Pos        311 Lth  32 Txt :print Tx[Row] > Wk; close (Wk);:
    21:40:30.939979744  31 :print Tx[Row] > Wk; close (Wk);:
    21:40:33.705042808: Write Pos        343 Lth  15 Txt :Reader 30 10 &:
    21:40:34.010378554  14 :Reader 30 10 &:
    21:40:35.103897039: Write Pos        358 Lth   2 Txt :}:
    21:40:36.063585547   1 :}:
    21:40:38.661280841: Write Pos        360 Lth  49 Txt :printf ("%s: Write Pos %10d Lth %3d Txt :%s:\n",:
    21:40:38.674634657: WRITER Exits
    21:40:39.144828936  48 :printf ("%s: Write Pos %10d Lth %3d Txt :%s:\n",:
    21:40:41.188952035: READER Exits
    [1]-  Done                    ./Reader
    [2]+  Done                    ./Writer
    
    • 1
  4. Paul_Pedant
    2019-12-03T14:16:54+08:002019-12-03T14:16:54+08:00

    这是 Writer 的脚本。它使用 dd 命令一次性创建初始文件(如果它不存在),然后使用 dd 将脚本文件中的随机行填充到文件中。它曾经在随机位置执行此操作,但现在它将每个位置都放在前一个位置之后。它以围绕给定参数平均的随机间隔添加行(此版本中为 2 秒)。它在特定时间限制后或文件已满时退出。

    #! /bin/bash
    
    #.. Declare the shared file.
    FILE="./myNullFile"
    SIZE=$(( 10 * 1024 * 1024 ))
    
    #.. Using script as source of the strings.
    TEXT="./isNulls"
    WORK="./myLine"
    
    #### Simulate the file writer defined in the question.
    
    function Writer {
    
        local RUN="${1:-60}" SLEEP="${2:-5}"
    
        local AWK='''
    BEGIN { printf ("%s: WRITER Begins\n", TS( )); }
    function Begin (Local) {
        Pos = getNull( );
        printf ("Initial NUL is at %d\n", Pos);
        if (Pos < 0) exit;
    }
    function TS (Local, cmd, ts) {
        cmd = "date \047+%H:%M:%S.%N\047";
        cmd | getline ts; close (cmd); return (ts);
    }
    function Wait (secs) {
        system (sprintf ("sleep %s", secs));
    }
    function getNull (Local, rs, Buf) {
        rs = RS; RS = "^$";
        getline Buf < Fn; close (Fn);
        RS = rs;
        return (-1 + index (Buf, "\000"));
    }
    function Str (Local, Row, Lth) {
        Row = int (nTx * rand());
        Lth = length (Tx[Row]);
        if (Pos + Lth >= Sz) {
            printf ("%s is full: Pos %d, Lth %d, Sz %d\n", Fn, Pos, Lth, Sz);
            exit;
        }
        printf ("%s: Write Pos %10d Lth %3d Txt :%s:\n",
            TS( ), Pos, 1 + Lth, Tx[Row]);
        print Tx[Row] "\n" > Wk; close (Wk);
        system (sprintf (Fmt, Pos, 1 + Lth, Wk, Fn, Wk));
        Pos += 1 + Lth;
    }
    NR == 1 { Fmt = $0; srand (); next; }
    NR == 2 { Fn = $0; next; }
    NR == 3 { Sz = $0; next; }
    NR == 4 { Wk = $0; Begin( ); next; }
    NF { sub (/^[ \011]+/, ""); Tx[nTx++] = $0; next; }
    { Str( ); }
    END { printf ("%s: WRITER Exits\n", TS( )); }
    '''
        local EXPIRED=$(( SECONDS + RUN ))
        local AWK_WT='BEGIN { srand(); } { print 0.1 + 2 * $1 * rand(); }'
        {
            DD_OPT='status=none conv=notrunc bs=1 seek="%s" count="%s"'
            DD_FNS='if="%s" of="%s" && rm -f "%s"'
    
            echo "dd ${DD_OPT} ${DD_FNS}"
            echo "${FILE}"; echo "${SIZE}"; echo "${WORK}"
            awk NF "${TEXT}"
            while (( SECONDS <= EXPIRED )); do
                sleep "$( echo "${SLEEP}" | awk "${AWK_WT}" )"
                echo ''
            done
        } | awk -f <( echo "${AWK}" )
    }
    
    #### Script Body Starts Here.
    
        [[ -r "${FILE}" ]] || {
            dd count=1 bs="${SIZE}" if="/dev/zero" of="${FILE}"
            od -A d -t x1a "${FILE}"
        }
    
        Writer 32 2
    
    • 1
  5. Best Answer
    Paul_Pedant
    2019-12-03T14:39:45+08:002019-12-03T14:39:45+08:00

    这是 Reader 的脚本,它应该接近于为 NUL 填充文件伪造 tail 命令所需的内容。它检查文件中的更改(通过比较整个 ls -l 输出,其中包括低至纳秒的时间戳),并报告批处理中的任何添加。它在启动时不报告文件中已经存在的行,仅在运行时添加。

    它以两种速度运行,以避免浪费检查。如果它检测到任何添加,它会在 1.0 秒后再次尝试。如果一个循环没有看到任何添加,它会在 5 秒后再次尝试(这 5 是该过程的参数)。

    #! /bin/bash
    #: Reader: tail -f a file which is pre-formatted with many trailing NUL characters.
    
    #### Implement the User Requirement.
    
    function Reader {
    
        local RUN="${1:-60}" SLEEP="${2:-5}" FILE="${3:-/dev/null}"
    
        local AWK='''
    BEGIN { NUL = "\000"; }
    function Tick (Local, cmd, ts) {
        cmd = "date \047+%s\047";
        cmd | getline ts; close (cmd); return (ts);
    }
    function TS (Local, cmd, ts) {
        cmd = "date \047+%H:%M:%S.%N\047";
        cmd | getline ts; close (cmd); return (ts);
    }
    function Wait (secs) {
        system (sprintf ("sleep %s", secs));
    }
    function isChange (Local, cmd, tx) {
        cmd = sprintf ("ls 2>&1 -l --full-time \047%s\047", Fn);
        cmd | getline tx; close (cmd);
        if (tsFile == tx) return (0);
        tsFile = tx;
        if (index (tx, "\047")) {
            if (fSt != "B") { fSt = "B"; printf ("%s: No file: %s\n", TS( ), Fn); }
        } else {
            if (fSt != "G") { fSt = "G"; printf ("%s: Reading: %s\n", TS( ), Fn); }
        }
        return (1);
    }
    function atNul (buf, Local, j) {
        j = index (buf, NUL);
        return ((j > 0) ? j : 1 + length (buf)); 
    }
    function List (tx, Local, ts, X, j) {
        sub ("\012$", "", tx); split (tx, X, "\012");
        ts = TS( );
        for (j = 1; j in X; ++j) {
            printf ("%s %3d :%s:\n", ts, length (X[j]), X[j]);
        }
    }
    function Monitor (Local, rs, tk, Buf, Now, End) {
        printf ("%s: READER Begins\n", TS( ));
        tk = Tick( ); Expired = tk + Run;
        Now = -1;
        while (Tick( ) <= Expired) {
            if (! isChange( )) { Wait( Sleep); continue; }
            rs = RS; RS = "\000";
            Buf = ""; getline Buf < Fn; close (Fn);
            RS = rs;
            if (Now < 0) Now = atNul( Buf);
            End = atNul( Buf);
            List( substr (Buf, Now, End - Now));
            Now = End;
            Wait( 1.0);
        }
        printf ("%s: READER Exits\n", TS( ));
    }
    NR == 1 { Run = $0; next; }
    NR == 2 { Sleep = $0; next; }
    NR == 3 { Fn = $0; }
    END { Monitor( Fn); }
    '''
        {
            echo "${RUN}";
            echo "${SLEEP}";
            echo "${FILE}";
    
        } | awk -f <( echo "${AWK}" )
    }
    
    #### Script Body Starts Here.
    
        Reader 40 5 "./myNullFile"
    
    • 1

相关问题

  • 使用键盘快捷键启动/停止 systemd 服务 [关闭]

  • du/df 和 ls 报告不同的磁盘使用情况

  • 需要一些系统调用

  • astyle 不会更改源文件格式

  • 通过标签将根文件系统传递给linux内核

Sidebar

Stats

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

    模块 i915 可能缺少固件 /lib/firmware/i915/*

    • 3 个回答
  • Marko Smith

    无法获取 jessie backports 存储库

    • 4 个回答
  • Marko Smith

    如何将 GPG 私钥和公钥导出到文件

    • 4 个回答
  • Marko Smith

    我们如何运行存储在变量中的命令?

    • 5 个回答
  • Marko Smith

    如何配置 systemd-resolved 和 systemd-networkd 以使用本地 DNS 服务器来解析本地域和远程 DNS 服务器来解析远程域?

    • 3 个回答
  • Marko Smith

    dist-upgrade 后 Kali Linux 中的 apt-get update 错误 [重复]

    • 2 个回答
  • Marko Smith

    如何从 systemctl 服务日志中查看最新的 x 行

    • 5 个回答
  • Marko Smith

    Nano - 跳转到文件末尾

    • 8 个回答
  • Marko Smith

    grub 错误:你需要先加载内核

    • 4 个回答
  • Marko Smith

    如何下载软件包而不是使用 apt-get 命令安装它?

    • 7 个回答
  • Martin Hope
    user12345 无法获取 jessie backports 存储库 2019-03-27 04:39:28 +0800 CST
  • Martin Hope
    Carl 为什么大多数 systemd 示例都包含 WantedBy=multi-user.target? 2019-03-15 11:49:25 +0800 CST
  • Martin Hope
    rocky 如何将 GPG 私钥和公钥导出到文件 2018-11-16 05:36:15 +0800 CST
  • Martin Hope
    Evan Carroll systemctl 状态显示:“状态:降级” 2018-06-03 18:48:17 +0800 CST
  • Martin Hope
    Tim 我们如何运行存储在变量中的命令? 2018-05-21 04:46:29 +0800 CST
  • Martin Hope
    Ankur S 为什么 /dev/null 是一个文件?为什么它的功能不作为一个简单的程序来实现? 2018-04-17 07:28:04 +0800 CST
  • Martin Hope
    user3191334 如何从 systemctl 服务日志中查看最新的 x 行 2018-02-07 00:14:16 +0800 CST
  • Martin Hope
    Marko Pacak Nano - 跳转到文件末尾 2018-02-01 01:53:03 +0800 CST
  • Martin Hope
    Kidburla 为什么真假这么大? 2018-01-26 12:14:47 +0800 CST
  • Martin Hope
    Christos Baziotis 在一个巨大的(70GB)、一行、文本文件中替换字符串 2017-12-30 06:58:33 +0800 CST

热门标签

linux bash debian shell-script text-processing ubuntu centos shell awk ssh

Explore

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

Footer

AskOverflow.Dev

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve