Как создать вложенные каталоги с помощью Mkdir в Golang?

97

Я пытаюсь создать набор вложенных каталогов из исполняемого файла Go, такого как dir1 / dir2 / dir3. Мне удалось создать единственный каталог с этой строкой:

os.Mkdir("." + string(filepath.Separator) + c.Args().First(),0777);

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

Томми Макдональд
источник

Ответы:

182

os.Mkdirиспользуется для создания единого каталога. Чтобы создать путь к папке, попробуйте использовать:

os.MkdirAll(folderPath, os.ModePerm)

Документация Go

func MkdirAll (строка пути, Perm FileMode) ошибка

MkdirAll создает каталог с именем path вместе со всеми необходимыми родительскими объектами и возвращает nil, иначе возвращает ошибку. Биты разрешения perm используются для всех каталогов, создаваемых MkdirAll. Если путь уже является каталогом, MkdirAll ничего не делает и возвращает nil.

Редактировать:

Обновлено для правильного использования os.ModePerm.
Для объединения путей к файлам используйте пакет, path/filepathкак описано в ответе @Chris.

Анисус
источник
@CodeWarrior: Спасибо за пинг. Я обновил свой ответ и проголосовал за Криса
Анисуса
Спасибо, @chris! :)
Thales P
1
Вы можете выбрать между 0755и os.ModePerm.
апдоглю
103

Таким образом, вам не нужно использовать магические числа:

os.MkdirAll(newPath, os.ModePerm)

Кроме того, вместо использования + для создания путей вы можете использовать:

import "path/filepath"
path := filepath.Join(someRootPath, someSubPath)

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

Крис
источник
3
Это правильный ответ. Гораздо проще и не зависит от платформы.
Дэн Эспарза
6

Если проблема заключается в создании всех необходимых родительских каталогов, вы можете рассмотреть возможность использования os.MkDirAll()

MkdirAll создает каталог с именем path вместе со всеми необходимыми родительскими объектами и возвращает nil, иначе возвращает ошибку.

Path_test.go является хорошей иллюстрацией того, как использовать его:

func TestMkdirAll(t *testing.T) {
    tmpDir := TempDir()
    path := tmpDir + "/_TestMkdirAll_/dir/./dir2"
    err := MkdirAll(path, 0777)
    if err != nil {
    t.Fatalf("MkdirAll %q: %s", path, err)
    }
    defer RemoveAll(tmpDir + "/_TestMkdirAll_")
...
}

(Обязательно укажите разумное значение разрешения, как указано в этом ответе )

VonC
источник
3

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

import (
  "os"
  "path/filepath"
  "log"
)

func ensureDir(fileName string) {
  dirName := filepath.Dir(fileName)
  if _, serr := os.Stat(dirName); serr != nil {
    merr := os.MkdirAll(dirName, os.ModePerm)
    if merr != nil {
        panic(merr)
    }
  }
}



func main() {
  _, cerr := os.Create("a/b/c/d.txt")
  if cerr != nil {
    log.Fatal("error creating a/b/c", cerr)
  }
  log.Println("created file in a sub-directory.")
}
скипи
источник
1

Это одна из альтернатив для достижения того же, но она позволяет избежать состояния гонки, вызванного наличием двух различных операций «проверка ... и ... создание».

package main

import (
    "fmt"
    "os"
)

func main()  {
    if err := ensureDir("/test-dir"); err != nil {
        fmt.Println("Directory creation failed with error: " + err.Error())
        os.Exit(1)
    }
    // Proceed forward
}

func ensureDir(dirName string) error {

    err := os.MkdirAll(dirName, os.ModeDir)

    if err == nil || os.IsExist(err) {
        return nil
    } else {
        return err
    }
}
пр-приятель
источник