Как создать папку в Go, если она не существует (mkdir if not exists)
Правильный способ: функция os.MkdirAll
В Go существует встроенная функция os.MkdirAll(), которая решает вашу задачу одной строкой. Она создаёт все необходимые родительские директории (аналог mkdir -p в Linux) и не возвращает ошибку, если директория уже существует.
Вот как выглядит ваш исправленный код:
package main
import (
"fmt"
"os"
)
func main() {
path := "./logs"
err := os.MkdirAll(path, 0755)
if err != nil {
fmt.Println("Ошибка создания папки:", err)
return
}
fmt.Println("Директория готова к использованию")
}
Почему это лучше вашего подхода?
- Thread-safe: os.MkdirAll атомарно выполняет проверку и создание, что исключает race condition.
- Вложенные директории: если в пути есть несуществующие родительские папки (например,
./logs/2024/01), они тоже будут созданы. - Код чище: не нужно писать лишние проверки и обрабатывать ошибки os.IsNotExist.
- Идиоматично: это стандартный способ в Go, используемый во всех крупных проектах.
Детали работы os.MkdirAll
Функция принимает два аргумента:
path— путь к директории (строка)perm— права доступа (типos.FileMode), обычно 0755 для папок
Важно: os.MkdirAll использует umask процесса, поэтому реальные права могут отличаться от указанных. Если вам нужны строго заданные права, сначала установите umask:
import "syscall"
func main() {
syscall.Umask(0) // отключаем umask
err := os.MkdirAll("./logs", 0755)
// ...
}
Обработка ошибок
Хотя функция не падает на существующей директории, она всё равно может вернуть ошибку в других случаях:
- Недостаточно прав на создание
- Файл с таким именем уже существует (не директория)
- Проблемы с файловой системой (диск переполнен, повреждена ФС)
Пример правильной обработки:
func EnsureDir(path string) error {
err := os.MkdirAll(path, 0755)
if err != nil {
return fmt.Errorf("не удалось создать директорию %s: %w", path, err)
}
return nil
}
// Использование:
if err := EnsureDir("./logs"); err != nil {
log.Fatal(err)
}
Когда всё-таки использовать os.Stat + os.Mkdir?
Единственный случай — если вам нужно создать только конечную директорию, без родительских, и вы хотите явно проверить, что она существует. Но даже тогда лучше использовать os.MkdirAll — он безопаснее.
Таким образом, запомните простое правило: нужно создать папку, если её нет? Используйте os.MkdirAll!
<<Альтернативный подход: проверка существования с обработкой ошибок
Иногда вам может понадобиться более детальный контроль — например, логировать, когда директория создаётся впервые, или выполнять разные действия в зависимости от того, существовала ли папка ранее. В таких случаях можно использовать комбинацию os.Stat и os.Mkdir, но с правильной синхронизацией.
package main
import (
"fmt"
"os"
)
func CreateDirIfNotExists(path string) (bool, error) {
_, err := os.Stat(path)
if err == nil {
// Директория уже существует
return false, nil
}
if !os.IsNotExist(err) {
// Произошла другая ошибка (например, нет прав на чтение)
return false, err
}
// Директория не существует — создаём
err = os.MkdirAll(path, 0755)
if err != nil {
return false, err
}
return true, nil // true — директория только что создана
}
func main() {
created, err := CreateDirIfNotExists("./logs")
if err != nil {
fmt.Println("Ошибка:", err)
return
}
if created {
fmt.Println("Директория создана впервые")
} else {
fmt.Println("Директория уже существовала")
}
}
Когда использовать этот подход?
- Вам нужно знать, была ли директория создана сейчас или уже существовала
- Вы пишете тесты и хотите проверить поведение для обоих случаев
- Требуется особая логика при первом создании (например, инициализация конфигурации)
Однако помните: этот код не является полностью потокобезопасным между проверкой os.Stat и созданием os.MkdirAll. В 99% случаев для однопоточных приложений это не проблема, но если у вас конкурентный доступ, лучше использовать os.MkdirAll с последующей проверкой на ошибку.