Можно ли обрезать файл (на месте, тот же индекс) в начале?

У меня это работало на Centos7:

$ rm /tmp/.X11-unix/*
4
30.11.2019, 19:34
8 ответов

Это можно сделать с помощью spongeиз moreutils. Вот пример использования tailдля удаления первых 3 байтов

tail -c +4 file | sponge file
1
27.01.2020, 20:48

Сksh93:

tail -c+2 < file 1<>; file

(где <>;— специфичный для ksh93 вариант стандартного оператора <>, который усекает файл в конце, если перенаправляемая команда была успешной ).

Удалил бы первый байт (, записав остальную часть файла поверх себя и обрезав в конце ).

То же самое можно сделать с shс:

{
  tail -c+2 < file &&
    perl -e 'truncate STDOUT, tell STDOUT'
} 1<> file

Обратите внимание, что это разрежет разреженные файлы, (вы все равно сможете -копать ямы после fallocate -d, хотя ).

При ошибках чтения/записи tail, скорее всего, выйдет из строя, оставив файл частично перезаписанным (, поэтому, например, abcdefghможет закончиться как bcddefgh, если произойдет сбой после перезаписиbcd). Вы можете адаптировать приведенное выше так, чтобы оно сообщало о смещении записи при ошибке, чтобы вы знали, как восстановить данные. Еще сksh93:

unset -v offset
{ tail -c+2 < file || false >#((offset=CUR)); } 1<>; file

После чего, если установлено $offset, оно содержит количество данных, которые были успешно записаны.

В Linux (начиная с версии 3.15 )и в файловых системах ext4 или xfs можно свернуть диапазоны или байты размера и смещения, кратные размеру блока файловой системы, с fallocate()системный вызов или утилита fallocate.

Так, например

fallocate -c -l 8192 file

Удалить первые 8192 байта файла (, предполагая, что FS с размером блока является делителем 8192 ), без необходимости перезаписи остальной части файла. Но это бесполезно, если вы хотите удалить раздел, который не кратен размеру блока FS.

4
27.01.2020, 20:48

Это довольно просто сделать в C, использует тот же индексный дескриптор, не использует рабочий файл. Просто нужна осторожность, чтобы сделать это правильно. Вероятно, вам понадобится предварительный запрос, чтобы найти размер блока устройства (пример 4096 ), хотя может быть достаточно некоторой произвольной степени 2 (пример 64K ).

Визуализируйте поток данных в виде гусеницы, :тянущейся вперед, когда данные продвигаются к новому местоположению.

Откройте файл для чтения/записи и выполните все системные вызовы для чтения/записи, чтобы избежать возможных проблем с буферизацией в подпрограммах FILE *.

Счетчик байтов, подлежащих удалению из файла в начале (N ), представляет собой количество полных блоков, а некоторые резервные байты (одного или обоих этих компонентов могут быть равны нулю ).

Поиск A, чтение X *4096 байт, где X выбран большим (для эффективности ), но не глупым -большим. Возможно, 4-мегабайтный буфер был бы оптимальным решением.

Поиск 0, запись этого буфера в полное число блоков, которые он должен занимать. Насколько я вижу на бумаге, это никогда не может быть перенесено на себя --следующий непрочитанный байт не может находиться в более раннем блоке.

Промойте и повторяйте (, увеличивая на 4 МБ при обоих поисках ), пока не закончится файл. Работайте с любым коротким блоком правильно.

Это оставляет вам дополнительную копию последних N байтов, которую вы можете обрезать с помощью системного вызова.

Производительность должна быть хорошей. Записи выравниваются по блокам -. Теоретически каждое чтение требует двух обращений к блоку из-за перекрытия, но непрерывное чтение позволяет избежать этого (, например. 4MB читает 1025 блоков вместо 1024).

Я думал, что это можно сделать в сценарии с помощью команды dd, но параметр размера блока -в dd применяется к поиску и чтению, поэтому он становится крайне неэффективным.

Метод тестирования :Получите файл размером 100 МБ со случайными данными, подсчитайте его. Затем добавьте его в меньший файл размером N байт. Запустите код, cksum и убедитесь, что файл теперь идентичен тому, что вы добавили. Время это. Тест с различными значениями N, включая 0, < 1 блок, точное количество блоков, несколько блоков + бит и весь файл.

Нужно ли мне писать и тестировать код, чтобы получить награду?

1
27.01.2020, 20:48

Вы можете сделать это с помощью printfи sed, хотя очень опасно редактировать файлы на лету без использования временной копии и я не могу рекомендовать вам достаточно читать эту замечательную статью , прежде чем делать это.

Кроме того, имейте в виду, что это может привести к сбою, если файл слишком большой и у вас недостаточно памяти .

printf '%s\n' "$(sed '1d' test.txt)" > test.txt

Тем не менее, это должно работать и изменять файл на лету без изменения индекса.

0
27.01.2020, 20:48

Кодекс. Он функционально завершен, хотя сообщения об ошибках могли бы быть более понятными. Опытным путем я установил, что буфер в 64 МБ не имеет преимуществ перед буфером в 1 МБ.

//
// xFront.c: Remove Leading bytes from a large file efficiently.

#include <sys/types.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <sys/stat.h>
#include <fcntl.h>

#define Hint(TX) fprintf (stderr, "%s\n%s\n", Usage, TX)

static char *Usage =

  "\nUsage: xFront size filename"
  "\n   size      number of bytes to be removed from the beginning of the file"
  "\n   filename  name of the file to be modified.";

// #### Structure definition for file management.

#define MAX_BUF (1 * 1024 * 1024)

typedef struct {

    size_t  szBuf;
    void *  buf;
    char *  fn;
    int     status;
    int     fd;
    int     cycles;
    off_t   r_offset;
    off_t   w_offset;
} FileManager_t;

static FileManager_t fixFM = {

    MAX_BUF,
    NULL,
    NULL,
    0,
};

static FileManager_t *pFM = & fixFM;


// #### File copying function.

void xFront (FileManager_t *pFM)

{
off_t r_offs; ssize_t r_size;
off_t w_offs; ssize_t w_size;

    while (1) {
        r_offs = lseek (pFM->fd, pFM->r_offset, SEEK_SET);
        if (r_offs < 0) { pFM->status = 1; return; }
        r_size = read (pFM->fd, pFM->buf, pFM->szBuf);
        if (r_size < 0) { pFM->status = 2; return; }
        if (r_size == 0) { pFM->status = 0; return; }
        pFM->r_offset += r_size;

        w_offs = lseek (pFM->fd, pFM->w_offset, SEEK_SET);
        if (w_offs < 0) { pFM->status = 3; return; }
        w_size = write (pFM->fd, pFM->buf, r_size);
        if (w_size != r_size) { pFM->status = 4; return; }
        pFM->w_offset += w_size;

        pFM->cycles++;
    }
    return;
}

// #### Entry function.

int main (int argc, char **argv, char **envp)

{
long int xSize;
off_t skEnd;
char *e;

    if (argc != 3) { Hint ("Missing args"); return (2); }

    // Validate the count to be removed from the front of the file.
    xSize = strtol (*(argv + 1), &e, 10);
    if (*e != '\0' || xSize < 0) { Hint ("Bad size"); return (2); }

    // Open the file for update, and check.
    pFM->fn = *(argv + 2);
    pFM->fd = open (pFM->fn, O_RDWR);
    if (pFM->fd < 0) { perror (pFM->fn); return (2); }

    // Find the file length, and maybe use a short buffer.
    skEnd = lseek (pFM->fd, (off_t) 0, SEEK_END);
    fprintf (stderr, "skEnd = %ld\n", skEnd);
    if (xSize >= skEnd) { close (pFM->fd); Hint ("Size too big"); return (2); }
    if (pFM->szBuf > skEnd) pFM->szBuf = skEnd;

    pFM->buf = malloc (pFM->szBuf);
    if (pFM->buf == NULL) {
        fprintf (stderr, "Cannot allocate %ld for file buffer\n", pFM->szBuf);
        return (1);
    }
    // Update the file in-situ.
    pFM->cycles = 0;
    pFM->r_offset = xSize;
    pFM->w_offset = 0L;
    xFront (pFM);
    fprintf (stderr, "Used %d cycles of %ld buffer.\n", pFM->cycles, pFM->szBuf);

    // We have scuffed up the file somehow.
    if (pFM->status != 0) {
        fprintf (stderr, "Copying failed with status %d\n", pFM->status);
    }

    // Trim the duplicate data from the end of the file.
    if (pFM->status == 0) {
        pFM->status = ftruncate (pFM->fd, (off_t) (skEnd - xSize));
        if (pFM->status < 0)  perror (pFM->fn);
    }

    // Tidy up.
    if (pFM->buf != NULL) free (pFM->buf);
    pFM->status = close (pFM->fd);
    if (pFM->status < 0) { perror (pFM->fn); return (2); }

    return (0);
}
0
27.01.2020, 20:48

Например, чтобы обрезать первые 9 байтов в начале файла , для того же индекса вы можете сделать:

dd if=file of=file bs=1 skip=9 conv=notrunc

Вы можете отрегулировать bsи skip, чтобы процесс был более эффективным, в зависимости от фактических значений.

Он будет пропускать некоторые блоки(bsбайтов )на входе, а затем начнет копирование в файл на себя, блок за блоком.

1
27.01.2020, 20:48

Зависит от того, что вы подразумеваете под «очень большими файлами». Каков ваш предел?

Вы можете прочитать все это в память (как строку awk )и записать подстроку в исходный файл. На каком-то этапе awk будет содержать оригинал и substr одновременно, но это жизнеспособное решение, возможно, для 0,5 ГБ. awk будет делать около 80 МБ в секунду на моем ноутбуке.

Легко в C, потому что вы можете просто переместить указатель начала записи.

1
27.01.2020, 20:48

Вот некоторые идеи (в основном теория ), и они не касаются i -узловой части (простой части )вопроса.

Я думаю, что можно удалить целые блоки с самого начала (, но я не знаю инструментов для этого ). Однако было бы необычно -удалять целое число блоков, что не имело бы смысла реализовывать (если только не было -убедительного случая использования ).

Тогда в файловой -системе мог бы быть способ сделать начальную часть файла -проходной через блок. (Меня интересует слияние хвостов, интересно, можно ли его использовать, но это усложнит файловую систему,и потребуется файл -system hack ). Альтернативой может быть предохранитель над системой -lay file -для игнорирования первой части файла. Это не удалит начало, но создаст видимость его удаления. Объединив это с идеей из пункта 1 (, если это можно заставить работать ), можно вернуть почти все хранилище.

Вызов ядра для удаления блоков из начала/середины/конца файла:fallocate. Но это реализовано только для ядра Linux 3.15+ в ext4 (только для файлов на основе экстента -)и XFS в системах Linux. Он также может пробивать отверстия, чтобы сделать файл разреженным (без ограничений по блокам, но сообщаемый размер не меняется ). --спасибо @isaac за это.

Существует также инструмент (, который можно использовать из оболочки)fallocate

1
27.01.2020, 20:48

Теги

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