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

На прошлой неделе я начал играться с GoLang, это C-подобный компилируемый язык, созданный Google, очень легкий и быстрый, на самом деле он часто находится в топ-листе тестов Techempower, хотя и очень C- как и синтаксис, он использует GC для очистки памяти, поэтому он идеально подходит для простых приложений.

Go настолько знаком и прост в освоении, что позволил мне выучить язык с нуля и разработать простое CLI-приложение за 6 дней. уверенно начать разработку чего-либо в Go.

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

Задание

По совпадению, на неделе, когда я изучал Go в свободное время, у меня была необычная задача на работе, версия TL; DR такова, что есть каталог, который содержит другие подкаталоги и несколько файлов, некоторые из этих файлов не имеют расширение, а некоторые из них имеют некоторые пользовательские расширения (например, .whatever), для этих случаев мне нужно будет изменить расширение файла на .txt или другой известный формат текстового файла.

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

Тогда зачем делать специальный инструмент для этого? Я обнаружил, что попытка решить реальную проблему на языке — это прекрасная возможность закрепить полученные знания, и это позволяет выйти даже за рамки ваших потребностей, в моем случае я мог бы сделать что-то, что можно было бы вызвать в CLI с пользовательскими параметрами, печатать цветной текст, показывать измененные файлы в реальном времени и т. д.

Реализация

Каждый проект Go начинается с папки со сценарием main.go и файлом go.mod, чтобы убедиться, что вы можете распечатать Hello World в сценарии main.go.

После этого основной причиной существования этого инструмента является изменение расширений файлов, поэтому мне потребовался всего 1 поиск в Google, чтобы найти пакет ОС, в Go, как и в большинстве языков, вы можете импортировать внешние библиотеки, чтобы помочь вашей разработке , некоторые из этих библиотек являются стандартной библиотекой, причудливым термином для общих встроенных функций.

В частности, модуль ОС — это то, что вы будете использовать для любого взаимодействия с операционной системой, в случае создания файлов, их изменения, выполнения процессов и т. д.

Пакет os обеспечивает независимый от платформы интерфейс к функциям операционной системы.

Из доступных методов в модуле ОС выделялся один, и это было именно то, что мне было нужно, что-то для изменения имени файлов, так как расширение прикрепляется в конце имени, я мог просто изменить эту часть.

func Rename(oldpath, newpath string) error

Из аргументов для вызова этого метода требуется исходный путь с именем файла и расширением (пример: /Users/Me/TestFile.img) и та же строка только с измененным именем файла.

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

func WalkDir(root string, fn fs.WalkDirFunc) error

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

Вот что я придумал

if info.IsDir() == false {
   applyExtensionChange(path, newExt, whitelist, info)
}

info — это информация о текущем узле, мы проверяем, является ли это каталогом с помощью IsDir(), false означает, что это файл, поэтому мы применяем логику изменения расширения, как указано выше.

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

type Whitelist map[string]bool

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

if whitelist[filepath.Ext(path)] == false {
   return
}
//Proceed with renaming logic

Некоторый рефакторинг

type ExtChangeJob struct {
   Path             string
   NewExtensionType string
   Whitelist        Whitelist
}

Была сделана самая простая реализация, что-то, что проходит по пути и изменяет типы файлов в соответствии с белым списком.

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

Разбор аргумента

Модуль Flag — это еще один модуль стандартной библиотеки для работы с аргументами командной строки. Было бы неплохо, чтобы этот инструмент можно было повторно использовать в других экземплярах, поэтому мне нужен способ динамически указывать собственный путь, белый список и тип расширения.

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

file-extension-changer -path=User/Me/TestDir

func flag.String(name string, value string, usage string) *string

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

func flag.Parse()

Бонус: цветной текст

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

Еще одна интересная особенность GO заключается в том, как легко добавить внешний код в проект. Беглым взглядом на некоторые популярные репозитории GitHub я нашел этот, который позволяет мне изменить цвет печатного текста, чтобы импортировать его в проект. просто добавьте строку в файл мода проекта

require github.com/daviddengcn/go-colortext v1.0.0

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

import "github.com/daviddengcn/go-colortext"

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

func ChangeColor(fg Color, fgBright bool, bg Color, bgBright bool)

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

Вы можете проверить полное решение на моей странице репозитория github:



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

Спасибо за прочтение! Надеюсь, что это было так же полезно, как и для меня, чтобы поделиться им.