LINUX.ORG.RU

Удаление файлов стандартными утилями

 ,


0

1

Есть список файлов и каталогов. В списке не может быть каталога с именем '/','.','..' или пустым именем.

Разделителем в списке является символ '\0'.

Есть ли какая-нибудь стандартная (лучше гнутая) утиль которая сможет выполнить такую задачу?

Что-то мне не навится в конструкции xargs -0 rm -r <file_list. Или это предрассудки?

Это действие требуется для отката инкрементального бекапа.

Там всего 3 действия: удаление лишних файлов/каталогов из списка, распаковка (untar) измененных файлов и распаковка (untar) хитрого архива с временем модификации всех каталогов и symlink.

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

★★★★★

Что-то мне не навится в конструкции xargs -0 rm -r < file_list. Или это предрассудки?

Нормальная конструкция. Разве что своя утилитка будет шустрее, если это существенно.

pr849
()

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

Кем рассматривается?

А рассматриватель уже решил что делать, если файл в каталоге появился после проверки отсутствия в нём файлов?

debugger ★★★★★
()

Разделителем в списке является символ ‘\0’.

А можно не надо? Или всё же \n разделитель? Список я так понял блоб куда тупо загнаны строки файлов, а в конце его \0\0 идёт?

LINUX-ORG-RU ★★★★★
()
Последнее исправление: LINUX-ORG-RU (всего исправлений: 1)
Ответ на: комментарий от LINUX-ORG-RU

Как то так при условии \n:

input:

/home/dron/egnaroc/include/asset.h
/home/dron/egnaroc/include/net.h
/home/dron/egnaroc/include/event.h
/home/dron/egnaroc/include/ui.h
/home/dron/egnaroc/include/dir1
/home/dron/egnaroc/include/dir2
/home/dron/egnaroc/include/dir3
/home/dron/egnaroc/include/netu-faila.h
/home/dron/egnaroc/include/fake-file.h
/home/dron/egnaroc/include/data
/home/dron/egnaroc/include/SDL2
/home/dron/egnaroc/include/NETU-KATALOGA
/home/dron/egnaroc/include/FAKE-DIR

out:

dron@gnu:~$ gcc gg.c
dron@gnu:~$ ./a.out ./datafile.txt 
Удаляю файл: '/home/dron/egnaroc/include/asset.h'
Удаляю файл: '/home/dron/egnaroc/include/net.h'
Удаляю файл: '/home/dron/egnaroc/include/event.h'
Удаляю файл: '/home/dron/egnaroc/include/ui.h'
Удаляю каталог: '/home/dron/egnaroc/include/dir1'
Удаляю каталог: '/home/dron/egnaroc/include/dir2'
Удаляю каталог: '/home/dron/egnaroc/include/dir3'
Файла или каталога '/home/dron/egnaroc/include/netu-faila.h' не существует или нет доступа
Файла или каталога '/home/dron/egnaroc/include/fake-file.h' не существует или нет доступа
Каталог '/home/dron/egnaroc/include/data' не пуст и содержит  файл/каталог 'djb2_hash.h'
Каталог '/home/dron/egnaroc/include/data' не пуст и содержит  файл/каталог 'vertex_list.h'
Каталог '/home/dron/egnaroc/include/data' не пуст и содержит  файл/каталог 'list.h'
Каталог '/home/dron/egnaroc/include/data' не пуст и содержит  файл/каталог 'utf8.h'
Каталог '/home/dron/egnaroc/include/data' не пуст и содержит  файл/каталог 'vertex_hashtable.h'
Каталог '/home/dron/egnaroc/include/data' не пуст и содержит  файл/каталог 'randf.h'
Каталог '/home/dron/egnaroc/include/data' не пуст и содержит  файл/каталог 'trigger.h'
Каталог '/home/dron/egnaroc/include/data' не пуст и содержит  файл/каталог 'int_hashtable.h'
Каталог '/home/dron/egnaroc/include/data' не пуст и содержит  файл/каталог 'dict.h'
Каталог '/home/dron/egnaroc/include/data' не пуст и содержит  файл/каталог 'int_list.h'
Каталог '/home/dron/egnaroc/include/data' не пуст и содержит  файл/каталог 'spline.h'
Каталог '/home/dron/egnaroc/include/SDL2' не пуст и содержит  файл/каталог 'SDL_local_opengl.h'
Каталог '/home/dron/egnaroc/include/SDL2' не пуст и содержит  файл/каталог 'SDL_local.h'
Файла или каталога '/home/dron/egnaroc/include/NETU-KATALOGA' не существует или нет доступа
Файла или каталога '/home/dron/egnaroc/include/FAKE-DIR' не существует или нет доступа
dron@gnu:~$

gg.c:

#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#include <dirent.h>
#include <string.h>
int main(int argc, char *argv[])
{
    if(argc < 2)
    {
        printf("use: appname datafile\n");
        exit(1);
    }else{

        FILE * input = fopen(argv[1],"r");
        if(!input)
        {
            printf("filed to open -> '%s'\n",argv[1]);
            exit(2);
        }else{
            char path[PATH_MAX];

            while( fscanf(input,"%s\n", path) == 1)
            { 
                DIR * dir = NULL;
                FILE * file = NULL;
                if((dir = opendir(path)))
                {
                    struct dirent * ent = NULL;
                    int num_items = 0;
                    while((ent = readdir(dir)))
                    {
                        if(strcmp(ent->d_name,".") != 0 && strcmp(ent->d_name,"..") != 0)
                        {
                            printf("Каталог '%s' не пуст и содержит  файл/каталог '%s'\n",path,ent->d_name);
                            num_items++;
                        }
                    }
                    if(num_items == 0)
                    {
                        /*код удаления каталога*/
                        printf("Удаляю каталог: '%s'\n",path);
                    }

                    closedir(dir);
                }else if((file = fopen(path,"r")))
                {
                    /*код удаления Файла*/
                    printf("Удаляю файл: '%s'\n",path);
                    fclose(file);
                }else{
                    printf("Файла или каталога '%s' не существует или нет доступа\n",path);
                }
            }
        }

    fclose(input);
    }

    return 0;
}

Ответственности за рм рф всех твоих файлов не несу :D

LINUX-ORG-RU ★★★★★
()
Последнее исправление: LINUX-ORG-RU (всего исправлений: 1)
Ответ на: комментарий от LINUX-ORG-RU

Или всё же \n разделитель?

\n — допустимый байт в именах файлов, а \0 — нет. Так что если использовать \n в качестве разделителя, и не экранировать его в именах файлов, могут быть неприятные сюрпризы.

i-rinat ★★★★★
()

которая сначала проверяет … что при удалении каталога в нем не будет файлов

как-то так (не проверял)

mydelete.sh

#!/bin/sh
rm "$1" 2>/dev/null || rmdir "$1" 2>/dev/null || echo "Can not delete $1"
xargs -0 -L1 -I{} ./mydelete.sh "{}" <file_list
futurama ★★★★★
()
Ответ на: комментарий от i-rinat

Да, тупанул. Ладно. Но тогда не сказано как оканчивается список, двумя нулями или ничем или ещё чем или надо высчитывать его конец в виде полный размер минут то что прочитали уже. Ну и по остальному, что с пробелами там. Точки вон сказано что отфильтрованы например.

LINUX-ORG-RU ★★★★★
()
Последнее исправление: LINUX-ORG-RU (всего исправлений: 1)
Ответ на: комментарий от LINUX-ORG-RU

fscanf(input,"%s\n", path)

А ты уверен, что оно пробелы считывает? Мне кажется, что %s читает до первого пробельного символа.

    if (...)
    {
        ...
        exit(1);
    }else{
        ...

Оп-па, Javascript-стайл!

Какой смысл отодвигать код на один отступ в false-ветке, если true-ветке if'а уже есть exit, и которого исполнение уже не возвращается?

char path[PATH_MAX];

Пути могут быть длиннее PATH_MAX.

i-rinat ★★★★★
()
Последнее исправление: i-rinat (всего исправлений: 1)
Ответ на: комментарий от i-rinat

А ты уверен, что оно пробелы считывает? Мне кажется, что %s читает до первого пробельного символа.

С пробелами не ясно, есть ли там они или нет. Допустил что нет. Но да не прав, исходить всё же стоит из «наихудшего».
Там надо было вписать что-то типа "%[^ ]",path вроде, не помню как точно

Какой смысл отодвигать код на один отступ в false-ветке

Автоформатирование вроде, хотя глаз намазолило так что я уже и сам так пишу.

если true-ветке if’а уже есть exit

Там первый else вообще не нужен. Мне даже ответить нечего. Просто на автопилоте ifelse, а дальше как бы проставить коды выхода ну и проставил. А уже на то что всё находится в скопе else который стал не нужен я просто внимания не обратил.

Спасибо за замечания. Лабу я на троечку сдал? :D

Я из расчёта, «ну типа как-то так, надо будет ТС сам допилит» хихи

LINUX-ORG-RU ★★★★★
()
Последнее исправление: LINUX-ORG-RU (всего исправлений: 2)
Ответ на: комментарий от LINUX-ORG-RU

Тогда вот так тогда для блоба со строками разделёными \0 максимальный размер строки в блобе задаётся во втором параметре, в случае если он задан меньше чем размер строк то выполнение будет прервано. Найти ошибки и допилить до желаемого самостоятельно я рукожопый :D

генерация тестовых данных

#include <stdio.h>
#include <string.h>
int main(int argc, char *argv[])
{
    FILE * dfile = fopen("datafile","wb");

    char * arr[] = {
    "/home/dron/egnaroc/include/asset.h",
    "/home/dron/egnaroc/include/net.h",
    "/home/dron/egnaroc/include/event.h",
    "/home/dron/egnaroc/include/ui.h",
    "/home/dron/egnaroc/include/dir 1",
    "/home/dron/egnaroc/include/dir2",
    "/home/dron/egnaroc/include/file 3 .txt",
    "/home/dron/egnaroc/include/netu-faila.h",
    "/home/dron/egnaroc/include/fake-file.h",
    "/home/dron/egnaroc/include/data",
    "/home/dron/egnaroc/include/SDL2",
    "/home/dron/egnaroc/include/NETU-KATALOGA",
    "/home/dron/egnaroc/include/FAKE-DIR",
    NULL,
    };

    for (int i = 0; arr[i] != NULL; ++i)
    {
        fwrite(arr[i],sizeof(char),strlen(arr[i])+1,dfile);
    }

    fclose(dfile);
    return 0;
}

Новый говнокод

#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#include <dirent.h>
#include <string.h>
int main(int argc, char *argv[])
{
    if(argc < 3)
    {
        printf("use: appname datafile path_max\n");
        exit(1);
    }

    unsigned long path_len = 0;
    sscanf(argv[2],"%ld",&path_len);
    if(path_len <= 0)
    {
        printf("Bad path_max -> '%ld'\n",path_len);
        exit(2);
    }

    FILE * input = fopen(argv[1],"rb");

    if(!input)
    {
        printf("filed to open -> '%s'\n",argv[1]);
        exit(3);
    }

    fseek(input,0,SEEK_END);
    long input_size = ftell(input);
    fseek(input,0,SEEK_SET);

    if(input_size == 0)
    {
        printf("File '%s' is empty",argv[1]);
        fclose(input);
        exit(4);
    }

    char * data = malloc(input_size);
    if(!data)
    {
        printf("Filed allocate memory\n");
        exit(5);
    }
    unsigned long read_result = fread(data,sizeof(char),input_size,input);
    fclose(input);

    if(read_result != input_size)
    {
        printf("Error to read file '%s'\n",argv[1]);
        free(data);
        exit(6);
    }

    char * path = malloc(path_len);

    if(!path)
    {
        printf("Filed allocate memory\n");
        exit(7);
    }

    char * work_data = data;

    while(input_size != 0)
    {
        memset(path,'\0',path_len);
        unsigned long work_data_path_len = strlen(work_data);
        if(work_data_path_len > path_len)
        {
            free(data);
            free(path);
            printf("Error path len in datafile overflow max path len in paramentr\n");
            exit(8);
        }

        for (int i = 0; *work_data != '\0' && i < path_len-1; ++i)
        {
            path[i] = *work_data++;
            input_size--;
        }

        work_data++;
        input_size--;

        DIR * dir = NULL;
        FILE * file = NULL;
        if((dir = opendir(path)))
        {
            struct dirent * ent = NULL;
            int num_items = 0;
            while((ent = readdir(dir)))
            {
                if(strcmp(ent->d_name,".") != 0 && strcmp(ent->d_name,"..") != 0)
                {
                    printf("Каталог '%s' не пуст и содержит  файл/каталог '%s'\n",path,ent->d_name);
                    num_items++;
                }
            }
            if(num_items == 0)
            {
                /*код удаления каталога*/
                printf("Удаляю каталог: '%s'\n",path);
            }

            closedir(dir);
        }else if((file = fopen(path,"r")))
        {
            /*код удаления Файла*/
            printf("Удаляю файл: '%s'\n",path);
            fclose(file);
        }else{
            printf("Файла или каталога '%s' не существует или нет доступа\n",path);
        }
     }

     free(path);
     free(data);

    return 0;
}

dron@gnu:~$ gcc datafile.c && ./a.out && gcc gg.c && ./a.out datafile 4096
Удаляю файл: '/home/dron/egnaroc/include/asset.h'
Удаляю файл: '/home/dron/egnaroc/include/net.h'
Удаляю файл: '/home/dron/egnaroc/include/event.h'
Удаляю файл: '/home/dron/egnaroc/include/ui.h'
Удаляю каталог: '/home/dron/egnaroc/include/dir 1'
Удаляю каталог: '/home/dron/egnaroc/include/dir2'
Удаляю файл: '/home/dron/egnaroc/include/file 3 .txt'
Файла или каталога '/home/dron/egnaroc/include/netu-faila.h' не существует или нет доступа
Файла или каталога '/home/dron/egnaroc/include/fake-file.h' не существует или нет доступа
Каталог '/home/dron/egnaroc/include/data' не пуст и содержит  файл/каталог 'djb2_hash.h'
Каталог '/home/dron/egnaroc/include/data' не пуст и содержит  файл/каталог 'vertex_list.h'
Каталог '/home/dron/egnaroc/include/data' не пуст и содержит  файл/каталог 'list.h'
Каталог '/home/dron/egnaroc/include/data' не пуст и содержит  файл/каталог 'utf8.h'
Каталог '/home/dron/egnaroc/include/data' не пуст и содержит  файл/каталог 'vertex_hashtable.h'
Каталог '/home/dron/egnaroc/include/data' не пуст и содержит  файл/каталог 'randf.h'
Каталог '/home/dron/egnaroc/include/data' не пуст и содержит  файл/каталог 'trigger.h'
Каталог '/home/dron/egnaroc/include/data' не пуст и содержит  файл/каталог 'int_hashtable.h'
Каталог '/home/dron/egnaroc/include/data' не пуст и содержит  файл/каталог 'dict.h'
Каталог '/home/dron/egnaroc/include/data' не пуст и содержит  файл/каталог 'int_list.h'
Каталог '/home/dron/egnaroc/include/data' не пуст и содержит  файл/каталог 'spline.h'
Каталог '/home/dron/egnaroc/include/SDL2' не пуст и содержит  файл/каталог 'SDL_local_opengl.h'
Каталог '/home/dron/egnaroc/include/SDL2' не пуст и содержит  файл/каталог 'SDL_local.h'
Файла или каталога '/home/dron/egnaroc/include/NETU-KATALOGA' не существует или нет доступа
Файла или каталога '/home/dron/egnaroc/include/FAKE-DIR' не существует или нет доступа
dron@gnu:~$ 
LINUX-ORG-RU ★★★★★
()
Ответ на: комментарий от LINUX-ORG-RU

Размер полного пути меня не волнует вообще.

Я хочу избежать явных ошибок (человеческий фактор).

Это особенно полезно при отладке моего велосипеда.

vel ★★★★★
() автор топика
Ответ на: комментарий от vel

Размер полного пути меня не волнует вообще.

Тебя нет, а программу да, надо какую-то память то выделять, ну можно убрать проверки и выделить массив размером 100500 и норм =)

Я хочу избежать явных ошибок (человеческий фактор).

Ну тогда напиши под себя утилиту по аналогии что выше которая сверяет соответствуют ли файлы в списке на удаление содержимому дереву файлов на диске к которым хотят применить действия и в случае чего пусть орёт АХТУНГ =)))

Ну вот код что выше скомпиляй и дай ему блоб со списком файлов и запусти, если всё норм то оно напишет только

Удаляю файл: такойто
Удаляю каталог: такойто

Во все остальные принты можно вписать просто АХТУНГ ВСЁ СЛОМАЛОСЬ =) Оно же у меня ничего не удаляет, просто бубнит в консоль и всё. Ну или вместо этого скрипт какой, тебе же для отладки, нафигачил на ифах лапшу и норм =)

Ну я чем смог, дальше тебе виднее ^.^

LINUX-ORG-RU ★★★★★
()
Последнее исправление: LINUX-ORG-RU (всего исправлений: 1)
Ответ на: комментарий от LINUX-ORG-RU

Я под кат только сейчас заглянул :) И не лень же тебе было писать...

opendir() - каменный век :)

nwft() - наше все :)

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

Есть три замечательные утили find, tar и rsync. Замечательно в них то, что они стандартные.

«rsync --backup» умеет при синхронизации складывать все измененные файлы в указаный каталог.

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

Как выяснилось, rsync не сохраняет время модификации измененных каталогов и symlink-ов. Но эту проблему легко решает find + tar, который умеет сохранять каталоги без рекурсии

find -type d -o -type l -print0 | tar --no-recursion --null -T - -cf dir.tar

Это делается перед запуском rsync --backup.

Далее пакуем каталог с изменениями и к нему дописываем архив dir.tar, сжимая все это на ходу. tar c ключиком "-i" замечательно распаковывает несколько архивов которые идут в файле друг за другом.

Таким образом, для отката на предыдущее состояние у нас получается 2 файла: список удаляемых объектов и архив. В реальности есть еще один файл - контрольный файл с основными атрибутами всех объектов.

Что еще есть интересного в rsync - это rsync-демон, со своим файлом конфигурации.

В конфигурации модуля есть замечательная опция «early exec» которая запускает скрипт перед тем как начать процесс синхронизации. В моем случае там делается снапшот средствами lvm и его монтирование в каталог из которого происходит синхронизация.

В «post-xfer exec» указан скрипт который размонтирует каталог и удаляет снапшот.

Потребовалось написать простой перловый скрипт который автоматизирует все эти действия и утиль для удаления лишних объектов.

Получился значительно упрощенный вариант rdiff-backup.

vel ★★★★★
() автор топика
Последнее исправление: vel (всего исправлений: 1)

Что-то мне не навится в конструкции xargs -0 rm -r <file_list. Или это предрассудки?

Предрассудки. Но всегда эту конструкцию можно запаковать в скрипт и засунуть его в $PATH

kirill_rrr ★★★★★
()