Как отслеживать изменения в файле, заполненном нулевыми байтами?

Нижеследующее является просто исправлением к ответу от @tripleeeтак что он удаляет любые кавычки из последнего поля так же, как и для всех других полей.

Чтобы показать, что исправлено, ниже приведен ответ tripee , а также небольшая модификация данных примера OP с добавлением кавычек вокруг последнего поля « Z ».

echo 'A,,C,"D,E,F","G",I,"K,L,M","Z"' |  sed -r -e 's/("([^"]*)")?,/\2\t/g'
A       C   D,E,F   G   I   K,L,M   "Z"

Вы можете видеть, что ' Z ' заключено в кавычки. Это отличается от того, как обрабатываются внутренние поля. Например, «G » не имеет кавычек.

Следующая команда использует вторую замену для очистки последнего столбца:

echo 'A,,C,"D,E,F","G",I,"K,L,M","Z"' |  sed -r -e 's/("([^"]*)")?,/\2\t/g' \
                                                -e 's/\t"([^"]*)"$/\t\1/'
A       C   D,E,F   G   I   K,L,M   Z

0
03.12.2019, 02:47
4 ответа

Это сценарий для Reader, который должен быть близок к тому, что вам нужно, чтобы подделать хвостовую команду для файла, заполненного NUL -. Он проверяет наличие изменений в файле (, сравнивая весь вывод 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
28.04.2021, 23:27

Я протестировал код для Writer (для создания теста )и Reader (для скрипта, который вы запросили ), и опубликую оба позже. Это 30 -секунд тестового прогона, чтобы доказать, что это работает. Устройство записи создает файл размером 10 МБ, когда начинает использовать dd, выгружает его с помощью 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
28.04.2021, 23:27

Это скрипт для 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
28.04.2021, 23:27

Проблемы со всей концепцией.

  1. Заменяет ли модуль записи только байты NUL другими строками или может записывать новые строки поверх старых строк, возможно, с неполным перекрытием? Всегда ли между строками будет хотя бы один разделитель NUL?

  2. Может ли он также перезаписывать строки новыми значениями NUL, чтобы стирать части файла?

  3. Действительно ли исходный файл имеет размер 10 МБ NUL или это изначально разреженный файл?

  4. Учитывая, что мы можем найти строки, только прочитав весь файл, как часто вы готовы это делать?

  5. Есть ли способ заблокировать файл во время записи, чтобы предотвратить состояние гонки?

  6. Может ли размер файла когда-либо изменяться в течение всей операции?

awk (по крайней мере, GNU/awk )может работать с символами NUL и длинными строками. Он может хранить список диапазонов, которые изначально были NUL (просто [0,10485760] ), и проверять наличие новой фрагментации в этих регионах. Однако это не обнаружит более -записей. Но он сможет сообщать обо всех добавлениях без каких-либо дополнительных процессов.

GNU/awk имеет встроенную -в patsplit ()функцию, которая разрезает строку в соответствии с разделителем RE, создавая массив полей и массив разделителей. Таким образом, RE /[\000]+/ должен поместить все строки в один массив, а все повторы NUL — в другой массив, и вы можете кумулятивно удлинить ()их все, чтобы найти общее смещение в файле для каждой строки.. Это выглядит как отличный кандидат для расследования.

Кстати, команда cat отображает символы NUL. Вы можете увидеть их в файле с помощью команды od. Причина, по которой они не отображаются в терминале, заключается в том, что драйвер терминала их игнорирует.

Как предполагает Ромео, сохранение контрольной суммы предыдущего файла позволит узнать, изменился ли он, но не где. Так что это может быть полезной оптимизацией, в зависимости от частоты обновлений.

1
28.04.2021, 23:27

Теги

Похожие вопросы