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

23 ГБ бесплатно из 500 ГБ. Я бы не заметил этого, если бы не отсутствующие документы и значок загрузки рядом с каждым файлом в папке «Документы» на моем Mac. Когда вы снова и снова работаете с техническими стеками, такими как Node.js, Next.js, React, Arduino на PlatformIO, неудивительно, что объем свободного места на диске быстро уменьшается.

Инструмент под названием ncdu подсказал мне, какой проект занимает больше ста мегабайт места. Это также позволяет мне удалять каталоги node_modules или .pio один за другим.

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

Как инженер-программист, желание автоматизировать этот долгий и утомительный процесс смехотворно велико. Но подождите, пока у вас не возникнет желание заново изобрести инструмент, который на самом деле был там в течение самого долгого времени, только для того, чтобы позже осознать это и спросить себя: «Почему я не использовал это???».

Да, я написал инструмент CLI только для удаления каждого каталога, имя которого соответствует значению флага CLI в текущем рабочем каталоге.

Я не использовал Go какое-то время. Я даже не могу вспомнить, когда в последний раз использовал Go для проекта. Итак, я пошел (или я должен сказать «Да») с этим и создал проект с помощью команды go mod github.com/alwint3r/janithor.

Да, вы правильно прочитали, и, пожалуйста, пожалуйста, не спрашивайте меня, почему я выбрал это имя.

Во-первых, мне нужен способ:

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

Чтобы получить текущий рабочий каталог, я могу использовать функцию Getcwd из пакета os.

cwd, err := os.Getcwd()

Затем я использую эту строку, чтобы получить имя каталога, который я хочу удалить:

var folderName = flag.String("dir", "", "The folder that will be removed");

Я определил функцию, которую я могу использовать для поиска абсолютного пути каждого каталога, имя которого хранится в переменной folderName.

func walk(base, target string, entry fs.DirEntry, output *[]string) error {
// ...
}

Функция принимает base в качестве текущего рабочего каталога или любого каталога внутри текущего рабочего каталога. target хранит имя каталога, который я хочу удалить, а параметр entry содержит информацию о текущем рабочем каталоге. Наконец, параметр output является указателем на фрагмент массива строк, чтобы я мог добавить каждый абсолютный путь к каталогу, который я хотел удалить. Затем функция вернет nil, если все в порядке, в противном случае она вернет любую ошибку, возвращенную функциями стандартных пакетов библиотек, которые я использую.

var fullPath string
if entry == nil {
    fullPath = base
} else {
    fullPath = path.Join(base, entry.Name())
}

Затем я проверил, является ли запись нулевой или нет. Если он равен нулю, то я предполагаю, что базой является текущий рабочий каталог или cwd. В противном случае fullPath будет установлен на абсолютный путь любого entry. Это может быть как файл, так и каталог.

if entry != nil && entry.IsDir() && entry.Name() == target {
    *output = append(*output, fullPath)
    return nil
}

Затем я проверил, является ли entry каталогом, имя которого совпадает с именем каталога, который я хочу удалить, или нет. Если true, добавьте абсолютный путь к выходному фрагменту и верните nil, нарушив рекурсию.

if entry == nil || entry.IsDir() {
    dirEntry, err := os.ReadDir(fullpath)
    if err != nil {
        return err
    }
    for _, dir := range dirEntry {
        walk(fullPath, target, dir, output)
    }
}

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

Следующие строки находятся внутри основной функции

deleteList := make([]string, 0)
walk(cwd, *folderName, nil, &deleteList);
for _, dir := range deleteList {
    err := os.RemoveAll(file)
    // .. error handling
}

Мне нужно вызвать функцию RemoveAll из пакета os, чтобы удалить каталог и его содержимое.

Вот и все!

Полный код здесь не приводится для краткости. Вы можете проверить полный исходный код из репозитория GitHub позже, если вам интересно.

Это работает как задумано, я удалил десятки каталогов .pio и node_modules на своем Mac и сумел восстановить более 10 ГБ свободного места.

Да, да, это работает, но знал ли я, что могу добиться того же, запустив эту единственную строку команды в окне терминала?

find . -type d -name .pio -exec rm -rf {} \;

Я сделал.

Думал ли я об этом, прежде чем потратить менее 30 минут на написание собственного инструмента? Нет, я этого не делал. Так происходит все время.

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

Пожалел ли я, что сделал это? Абсолютно нет.

Мне удалось заново научиться программировать на языке программирования Go и использовать его богатые стандартные библиотеки. А ты, случайный и замечательный человек интернета, знаешь, когда я в последний раз пользовался рекурсией?

Нет? И я нет.

Спасибо за прочтение! Пожалуйста, не стесняйтесь присылать мне свои отзывы, особенно о моем ломаном английском. Это многое значит для меня.