LINUX.ORG.RU

Замена последовательности символов на другую в бинарных файлах

 , , , ,


0

1

Решение: Замена последовательности символов на другую в бинарных файлах (комментарий)

Братья и сёстры линуксоиды, подсобите советом!

Чем в большом множестве файлов заменить одну последовательность символов на другую? Есть около 1.7kk файлов (в сумме 43 GiB, размер самих файлов очень разный), как текстовых, так и совсем бинарных. Во многих из них встречается строка «ABcd123», надо заменить на «CDef123». Или вроде того. Важно, что длина строк одинаковая и размеры файлов должны остаться такими же, как были до правок, чтобы там никакие бинарные структуры не «поплыли», вне зависимости от того, что это за файл, хоть исполняемый. Поскольку файлов много, то желательно это сделать прямо на месте, без временного копирования. И хорошо бы ещё удостовериться, что всё прошло хорошо, ничего не изменилось кроме указанной последовательности.

Кроме того, есть ещё второй смежный вопрос. Чем ту же последовательность заменить в target'ах символьных ссылок?

Суть в том, что эта последовательность - имя директории, где всё это лежит и она много где «осела» и в ссылках и в самих данных.

Думается, sed для такого не очень подходит.

Благодарствую!

★★★★★

Последнее исправление: ls-h (всего исправлений: 1)

есть такой язык - Си. с его помощью можно открыть файл, mmapнуть его в память, и там искать. Если строка короче 32байт, можно на avx заоптимизировать до усрачки.

salozar
()
Ответ на: комментарий от salozar

я подозреваю, что весь этот тред - это вопрос «не хочу пилить велосипед, хочу готовое решение». И, учитывая что запрошен не какой-то космический корабль, а «in-place binary sed», лично я вот не могу сказать что правильный ответ - пилить свой велосипед

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

ну возможно велосипед можно запилить без особой боли. если строка размером до 16 байт например.

salozar
()

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

salozar
()
Ответ на: комментарий от Pinkbyte

Тут прогу писать быстрее чем искать готовое. Только автор решил не сам искать а у других спросить.

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

Какая разница какая там длина строки, ему нужно на один запуск, уверен можно оставить на ночь. Делаешь чтение большого буфера + memcmp в цикле с побайтовым сдвигом и всё.

А чтение с диска (внутри mmap тоже будет чтение, хоть проге об этом не скажут) всё равно будет медленнее чем побайтовый перебор, так что можно тем более не париться.

firkax ★★★★★
()
Последнее исправление: firkax (всего исправлений: 2)

Думается, sed для такого не очень подходит.

Отлично подходит.

zemidius
()

Чем ту же последовательность заменить в target’ах символьных ссылок?

patchelf + elfutils

anonymous
()

Остановился на небольшом скрипте на Python'е.

def is_target(path: str) -> bool:
    extension = path.split('.')[-1].lower()
    if extension in EXTS_TO_IGNORE:
        return False
    elif os.path.islink(path):
        return False
    elif not os.access(path, os.W_OK):
        return False
    elif os.stat(path).st_size == 0:
        return False
    return True


def look_inside(path: str) -> None:
    print(f'{path}: ', end='')
    total = 0
    with open(path, 'r+b') as file:
        with mmap.mmap(file.fileno(), 0) as mm:
            mm: mmap.mmap
            mm.seek(0)
            pos = 0
            while (pos := mm.find(STR_TO_FIND, pos)) >= 0:
                print(f'{pos}', end=' ')
                mm[pos:pos+len(STR_TO_FIND)] = STR_TO_PASTE[0:len(STR_TO_FIND)]
                total += 1
    if total == 0:
        print(f'{STR_TO_FIND} not found.')
    else:
        print(f': {total}')


def find_and_replace(
        root_dir: str,
        saved: str
):
    if os.path.exists(saved):
        with open(saved, 'rb') as pfile:
            processed_files = pickle.load(pfile)
    else:
        processed_files = set()
    total_files_processed = 0
    all_files = 0
    save_every = 100
    for root, dir_names, file_names in os.walk(root_dir, followlinks=False):
        for file_name in file_names:
            all_files += 1
            path_to_file = os.path.join(root, file_name)
            if not is_target(path_to_file):
                continue
            if path_to_file in processed_files:
                print(f'{path_to_file} was processed.')
                continue
            look_inside(path_to_file)
            processed_files.add(path_to_file)
            total_files_processed += 1
            if total_files_processed % save_every == 0:
                with open(saved, 'wb') as pfile:
                    pickle.dump(processed_files, pfile)
    print()
    print(f'Total files found: {all_files}')
    print(f'Total files processed in this session: {total_files_processed}')

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

Довольно опасную вещь Вы затеяли, должен заметить. И вопрос даже не в конкретном решении (коих миллион различной степени извращённости), а в постановке. У Вас есть гарантия что не будет false positive matches на «ABcd123»? Я бы предложил соломки подстелить и бекапить в процессе все файлы которые меняются. Или вести подробный лог что именно Вы поменяли (name + offset) чтобы откатить можно было в случае чего. Мои 2 копейки.

bugfixer ★★★★★
()
Последнее исправление: bugfixer (всего исправлений: 2)
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.