ESPN DeportesMark Walter y Josh Kushner ultiman términos de venta de Lakers por $12,500 mdd, fuentesESPNWay-Too-Early 2027 MLB Power Rankings: Are the Brewers No. 1?InquirerLOOK: Iligan City’s holy, non-spooky HalloweenThe Jerusalem PostThe West Bank is the historic heartland of the Jewish people - opinion한겨레‘율리시스의 계약’은 항상 좋을까 [.txt]ZDF heuteEntdecken Sie das ZDF-NachrichtenstudioColliderMGM+'s Horror Thriller Officially Becomes the Biggest Show on Netflix Right NowVariety‘The Mosquito Bowl’: How a Parrot Proved to Be Peter Berg’s Biggest ChallengeBillboard‘NXT Halloween Havoc’ 2026: Here’s Where to Watch WWE Premium Live Event OnlineSDP EspectáculosConcierto de Dimitri Vegas & Like Mike en la Plaza de Toros: setlist y telonero para el 31 de octubreCBS NewsShooting at Spirit Halloween store in California kills 1, injures 6ANSA SportSerie A: Milan-Inter 1-0 DIRETTA
The Daily Newsstand · Free, Always
Saturday, October 31, 2026

Как я написал раскладчик файлов, который ничего не удаляет и всё возвращает обратно

Translate

Рабочий стол зарастает быстро: скриншоты, скачанные ролики, договоры, установщики, архивы «для друга». Разбирать это руками скучно, а программы-сортировщики пугают: непонятно, куда они денут файлы и как потом всё вернуть.

Готовые решения есть. На Маке давно существует Hazel, на Windows есть DropIt и File Juggler. Но они работают по правилам, которые сначала надо написать самому, и почти никогда не объясняют, почему файл попал именно сюда.

Я сделал «Порядок»: бесплатную программу для Мака и Windows с открытым кодом. Она сама предлагает раскладку, показывает план с причиной для каждого файла, ничего не удаляет и одной кнопкой возвращает всё как было.

Что умеет

  • Раскладывает Рабочий стол, Загрузки, Документы или любую папку, которую перетащили на окно.

  • Сначала показывает план: куда поедет каждая вещь и почему («в названии «договор»», «в тексте «акт выполненных работ»», «формат .pdf»).

  • Узнаёт слова с окончаниями и латиницей: «смета» найдёт «сметы», «договор» найдёт dogovor_arendy.pdf.

  • Если название ничего не говорит, читает начало текста в PDF, DOCX и RTF, а на картинках распознаёт текст встроенным в систему движком.

  • Скриншоты и фото с телефона раскладывает по месяцам, у «отчёт (1).pdf» убирает «(1)», папке «Новая папка» с фото даёт имя с датой.

  • Точные копии, пустые файлы и установщики складывает в папку «На удаление». Удаляете их вы сами.

  • Не трогает папки, в которых сейчас работает программа, папку скриншотов, папки, нужные скриптам, и проекты с кодом.

  • Возвращает всё обратно, даже если раскладку прервал сбой питания.

Цифры

Замерено 01.11.2026 на MacBook с Apple Silicon.

Что

Сколько

Размер программы для Мака

5,3 МБ

План для Рабочего стола, Загрузок и Документов, только по названиям

0,15 с

То же с чтением текста файлов

8,0 с (из них 5 с ушло на ожидание системного распознавания, см. грабли №4)

Чтение текста из PDF на 4 страницы

97 мс

Тестов ядра

43, плюс сверка с ручной разборкой

Случайных рабочих столов в проверке «разложить + вернуть»

150

Грабли №1. Защита не узнавала папки с русскими именами

Симптом. Программа должна оставлять в покое папку, в которой сейчас открыт терминал или редактор. Папку tekst она узнавала, а папку «Чисто» нет.

Причина. Кто где работает, я узнаю у системы командой lsof -a -d cwd -Fcn: она выводит рабочие папки всех процессов. Всё, что не ASCII, lsof печатает кодами байтов:

n/Users/me/Desktop/\xd0\xa7\xd0\xb8\xd1\x81\xd1\x82\xd0\xbe/tekst

Строка \xd0\xa7… никогда не совпадёт с путём «/Users/me/Desktop/Чисто».

Решение. Собираю байты обратно и только потом читаю строку как UTF-8:

fn unescape_lsof(s: &str) -> String {
    let b = s.as_bytes();
    let mut out = Vec::with_capacity(b.len());
    let mut i = 0;
    while i < b.len() {
        if b[i] == b'\\' && i + 3 < b.len() && b[i + 1] == b'x' {
            if let Ok(byte) = u8::from_str_radix(&s[i + 2..i + 4], 16) {
                out.push(byte);
                i += 4;
                continue;
            }
        }
        out.push(b[i]);
        i += 1;
    }
    String::from_utf8_lossy(&out).into_owned()
}

Грабли №2. iCloud скачивает файлы, если на них просто посмотреть

Симптом. Если включены «Рабочий стол и Документы в iCloud», часть файлов лежит только в облаке: на диске есть имя и размер, а содержимого нет. Стоит программе прочитать хотя бы 12 байт (я так отличаю ярлык Finder от обычного файла), и система скачивает файл целиком.

Причина. Такие файлы помечены флагом SF_DATALESS. Чтение поднимает их из облака прозрачно для программы.

Решение. Флаг проверяется до любого чтения. Облачные файлы не читаются никогда, исключение сделано только для крошечных скриптов (до 64 КБ), чтобы найти в них пути:

const SF_DATALESS: u32 = 0x4000_0000;
meta.st_flags() & SF_DATALESS != 0

Отдельно проверил перенос с помощью ls -lO. Файл, переехавший со стола в Документы, так и остался dataless: обе папки синхронизирует iCloud, и это просто переименование. Файл, переехавший в Фильмы, потерял флаг, то есть скачался. Поэтому план считает, сколько придётся скачать, только для вещей, которые уезжают за пределы облачных папок, и пишет это заранее.

Грабли №3. Перенос и запись в журнал — это два действия

Симптом. Откат работает по журналу. Если писать в журнал после переноса и выключить компьютер между этими шагами, файл переедет, а журнал о нём не узнает.

Решение. Сделал журнал так, как это устроено в базах данных. Сначала пишется намерение, потом выполняется перенос, потом ставится отметка, и каждая строка сразу сбрасывается на диск:

{"op":"intent","id":3,"src":"/Users/me/Desktop/фото.jpg","dst":"/Users/me/Pictures/Картинки/фото.jpg"}
{"op":"done","id":3}

При откате намерение без отметки проверяется по диску: если на новом месте файл есть, а на старом нет, значит, переезд успел случиться, и его нужно вернуть. Оборванная последняя строка просто пропускается. Тест имитирует такой сбой: записывает два намерения, переносит один файл и обрывает журнал на середине строки.

Остальные правила простые. Используется только rename, никаких «скопировать и удалить». Занятое имя не перезаписывается: перед переносом имя проверяется ещё раз, потому что его могли занять, пока человек смотрел на план.

Грабли №4. Системное распознавание текста зависло на 25–50 секунд

Симптом. Текст на картинках я распознаю тем, что уже встроено в систему: Vision на Маке и Windows.Media.Ocr на Windows. Ставить ничего не нужно, и всё работает без сети. На моём Маке распознавание одной картинки занимало от 25 до 50 секунд и заканчивалось ошибкой e5rt_execution_stream_operation_create_precompiled_compute_operation_with_options call failed.

Причина. Ошибка в самом системном движке: маленькая проверочная программа на Swift падает точно так же. Быстрый режим работает, но русский язык он не поддерживает.

Решение. Предохранитель. Распознаванию даётся не больше 5 секунд. Если система не ответила, распознавание выключается до конца работы программы, и план строится по названиям:

match rx.recv_timeout(PATIENCE) {
    Ok(text) => text,
    Err(_) => {
        BROKEN.store(true, Ordering::Relaxed);
        None
    }
}

Грабли №5. Пустая папка, которая весит 519 МБ

Симптом. Пустые папки программа отправляет в «На удаление». На проверке она собралась отправить туда папку резервной копии весом 519 МБ.

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

Решение. Скрытые файлы теперь считаются отдельно (кроме служебных .DS_Store). Папка, в которой есть только скрытое, остаётся на месте с пояснением «похоже на данные программы». Эту ошибку нашла сверка с ручной разборкой, о ней ниже.

Как проверить, что раскладка разумная

Тестами на «правильно и неправильно» здесь не обойтись: правильно — это как разложил бы человек. Поэтому я разобрал настоящий Рабочий стол руками и записал каждое решение: чем смотрел, что заметил, куда положил и почему. Получилось 41 запись (имена личных проектов и клиентов заменены). Тест воссоздаёт этот стол в песочнице и сверяет решения программы с ручными.

Программа совпала со мной во всех 25 общих случаях. Ещё 16 решений опирались на то, что знает только хозяин компьютера: «этот архив относится к такому-то проекту», «это личные документы». Для них в программе есть свои правила, rules.json.

Вторая проверка — свойство «ничего не случилось». Тест 150 раз собирает случайный Рабочий стол с одинаковыми именами, копиями и пустыми файлами, раскладывает его и откатывает. После раскладки ни один файл не должен пропасть или оказаться задавленным, а после отката дерево папок должно совпасть с исходным байт в байт.

Окно-визитка

Окно сделано в пропорциях визитки 85 × 55 мм. Его ширина составляет малую долю золотого сечения экрана (38 %, но не меньше 540 и не больше 760 точек). По вертикали окно стоит чуть выше середины: свободное место делится тем же сечением. Кегль текста равен ширине окна, делённой на 36, поэтому на самом маленьком окне это 15 пикселей.

Шрифт EB Garamond. Курсив в первой версии плохо читался, и от него пришлось отказаться. Цифры в плане ровные, по линии строки. Кнопки сделаны текстом с подчёркиванием, рамок и плашек нет.

Что хранится

  • Журналы переносов: ~/Library/Application Support/poryadok/journal на Маке, %APPDATA%\poryadok\journal на Windows. В них только пути.

  • Свои правила, если вы их завели: rules.json там же.

  • Больше ничего. Программа не ходит в сеть, аккаунтов и аналитики нет. Текст файлов читается только для выбора полки и нигде не сохраняется.

Чем отличается

Цены проверены на сайтах 01.11.2026.

Порядок

Hazel 6

DropIt

File Juggler

Цена

бесплатно

$42, на 5 Маков $65

бесплатно

платно после 30 дней пробы

Системы

Мак, Windows

Мак

Windows

Windows

Открытый код

да, MIT

нет

да, GPLv2

нет

С чего начинается работа

сразу предлагает раскладку

правила пишете сами

правила пишете сами

правила пишете сами

Эти программы умеют больше меня в одном: они следят за папками в фоне и срабатывают сами. Я сделал другое. Программа без настройки предлагает раскладку целиком, для каждого файла объясняет, почему он едет именно сюда, защищает папки, нужные другим программам, и одной кнопкой возвращает всё, даже после сбоя.

Чего не умеет

  • Не понимает смысла так, как человек. «Договор с клиентом» и «договор аренды квартиры» для неё одно и то же, пока вы не заведёте своё правило.

  • Не переносит проекты с кодом. В них бывают абсолютные пути, и после переноса проект может сломаться.

  • Не переносит файлы между дисками: она только переименовывает и никогда не копирует.

  • Не следит за папками в фоне. Раскладка запускается, когда вы сами нажмёте кнопку.

  • Программа не подписана платными сертификатами Apple и Microsoft, поэтому при первом запуске система предупредит. Как открыть, написано в README.

Ссылки

Будет ещё.

Раньше в серии: Качалка — видео без водяных знаков, «Чисто» — без рекламы, Кружок — кружок из любого видео, Толково — голосовой ввод без интернета, Мимо — пропуск рекламных вставок в TikTok.

View the original on Хабр →

KioskNews shows a cleaned-up reading view extracted from the publisher’s page — the original always lives on their site, not ours.