Проверить существование каталога и создать, если не существует

388

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

mainDir <- "c:/path/to/main/dir"
subDir <- "outputDirectory"

if (file.exists(subDir)){
    setwd(file.path(mainDir, subDir))
} else {
    dir.create(file.path(mainDir, subDir))
    setwd(file.path(mainDir, subDir))

}
гнаться
источник
1
Я уверен, что видел функцию R, которая создает временный каталог со случайно сгенерированным именем и возвращает имя. Я думаю, что есть аналогичный файл, который создает временный файл. Я не могу найти их от случая к случаю, но в пакете Databel ( cran.r-project.org/web/packages/DatABEL/index.html ) есть функция get_teilitary_file_name.
PaulHurleyuk
42
Вы никогда не должны использовать setwd()в коде R - это в основном отрицает идею использования рабочего каталога, потому что вы больше не можете легко перемещать свой код между компьютерами.
Хэдли
6
Если бы у меня была интересная тема для размышления, я был бы признателен за ваши мысли о других методах с той же целью. На работе все компьютеры синхронизируются в одной сети, поэтому пути к файлам одинаковы. Если это не так, у нас есть более серьезные проблемы, чем переносимость скрипта. В этом конкретном примере я писал сценарий, который будет загружен на машину, которая будет перевозиться по нашим национальным паркам в течение 2 лет. Этот сценарий будет извлекать данные из локального экземпляра SQL, выполнять некоторую обработку и выдавать файл .csv. Конечным продуктом будет .batфайл, который конечному пользователю никогда не придется изменять.
Погоня
@Chase Но вам не нужно setwdработать с сетевыми путями. Вам просто нужно указать пути, чтобы сохранить результаты и по-прежнему работать с текущим путем (который устанавливается при запуске сеанса R). Или запустить R с желанием рабочего каталога.
Марек
5
Ага. Или параметризовать, out_dir <- "path/to/output/directory"а затем использовать write.table(file = file.path(out_dir,"table_1.csv"), ...). Или даже out_file <- function(fnm) file.path("path/to/output/directory", fnm)и тогда write.table(file = out_file("table_1.csv"), ...)(аналогичный метод я использую при работе с сетевыми дисками).
Марек

Ответы:

403

Используйте showWarnings = FALSE:

dir.create(file.path(mainDir, subDir), showWarnings = FALSE)
setwd(file.path(mainDir, subDir))

dir.create()не вылетает, если каталог уже существует, он просто выводит предупреждение. Так что если вы можете видеть предупреждения, нет проблем с этим:

dir.create(file.path(mainDir, subDir))
setwd(file.path(mainDir, subDir))
robbrit
источник
58
Имейте showWarnings = FALSEв виду, что при использовании этого также будут скрываться другие предупреждения, например, что каталог не может быть создан.
Zelanix
5
^ Есть ли способ подавить только одно конкретное предупреждение?
Bas
2
Привет, я хочу не создавать вложенный каталог, как если бы я был в папке test1, то внутри нее test2 внутри нее test3 ... но сейчас я столкнулся с проблемой. Есть ли способ, которым я могу создать 3 уровня каталога, даже если directory1 не выходит ??
Правин Кесани
10
@PraveenKesani ли это то , что вы ищете: dir.create("test1/test2/test3/", recursive=TRUE)?
Дин.
6
@Bas Действительно поздний ответ, но suppressWarnings(<statement>)будет подавлять предупреждения только для этого утверждения.
Ram RS
163

По состоянию на 16 апреля 2015 года, с выпуском R 3.2.0новой функции называется dir.exists(). Чтобы использовать эту функцию и создать каталог, если он не существует, вы можете использовать:

ifelse(!dir.exists(file.path(mainDir, subDir)), dir.create(file.path(mainDir, subDir)), FALSE)

Он вернется, FALSEесли каталог уже существует или TRUEего невозможно создать , и если он не существует, но был успешно создан.

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

dir.exists(file.path(mainDir, subDir))
Molx
источник
9
Просто заметьте, что не рекомендуется использовать ifelse()для не векторизованного ветвления.
Лайонел Генри
2
@ Бас, потому что ваш код неверно читается, как будто что-то векторизовано. Это как использовать векторизацию |вместо скаляра ||. Это работает, но это плохая практика.
Лайонел Генри
1
О блин, поэтому я тоже делал свои операторы if неправильно, используя |, является ли векторизация причиной, по которой ||иногда это не работает ? Я знаю, что это не по теме, но я очень хочу это выяснить. Я пойду дефо и пойду больше о векторизации. Спасибо
Bas
4
Так каков наилучший практический способ сделать это, если мы должны избегать ifelse?
KillerSnail
6
используя if и else;)
Лайонел Генри
17

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

mainDir <- "~"
subDir <- "outputDirectory"

if (file.exists(paste(mainDir, subDir, "/", sep = "/", collapse = "/"))) {
    cat("subDir exists in mainDir and is a directory")
} else if (file.exists(paste(mainDir, subDir, sep = "/", collapse = "/"))) {
    cat("subDir exists in mainDir but is a file")
    # you will probably want to handle this separately
} else {
    cat("subDir does not exist in mainDir - creating")
    dir.create(file.path(mainDir, subDir))
}

if (file.exists(paste(mainDir, subDir, "/", sep = "/", collapse = "/"))) {
    # By this point, the directory either existed or has been successfully created
    setwd(file.path(mainDir, subDir))
} else {
    cat("subDir does not exist")
    # Handle this error as appropriate
}

Также имейте в виду, что если ~/fooего не существует, вызов dir.create('~/foo/bar')не будет выполнен, если вы не укажете recursive = TRUE.

zelanix
источник
3
есть ли причина, по которой вы используете paste (...) vs file.path (mainDir, subDir). Также, если вы указали путь <- file.path (mainDir, subDir), вы можете использовать его 5 раз, делая операторы if более читабельными.
MikeF
15

Вот простая проверка , и создает каталог, если не существует:

## Provide the dir name(i.e sub dir) that you want to create under main dir:
output_dir <- file.path(main_dir, sub_dir)

if (!dir.exists(output_dir)){
dir.create(output_dir)
} else {
    print("Dir already exists!")
}
Surya
источник
9

Использование file.exists () для проверки существования каталога является проблемой в оригинальном посте. Если subDir содержит имя существующего файла (а не просто путь), file.exists () вернет TRUE, но вызов setwd () завершится неудачно, потому что вы не можете установить рабочий каталог так, чтобы он указывал на файл.

Я бы порекомендовал использовать file_test (op = "- d", subDir), который будет возвращать "TRUE", если subDir является существующим каталогом, и FALSE, если subDir является существующим файлом или несуществующим файлом или каталогом. Аналогично, проверка файла может быть выполнена с помощью op = "- f".

Кроме того, как описано в другом комментарии, рабочий каталог является частью среды R и должен контролироваться пользователем, а не сценарием. Скрипты в идеале не должны изменять среду R. Чтобы решить эту проблему, я мог бы использовать options () для хранения общедоступного каталога, в котором я хотел получить весь свой вывод.

Итак, рассмотрим следующее решение, где someUniqueTag - это определенный программистом префикс для имени опции, что делает маловероятным, чтобы опция с таким именем уже существовала. (Например, если вы разрабатывали пакет с именем «filer», вы можете использовать filer.mainDir и filer.subDir).

Следующий код будет использоваться для установки параметров, доступных позже для использования в других сценариях (таким образом, избегая использования setwd () в сценарии), и для создания папки при необходимости:

mainDir = "c:/path/to/main/dir"
subDir = "outputDirectory"

options(someUniqueTag.mainDir = mainDir)
options(someUniqueTag.subDir = "subDir")

if (!file_test("-d", file.path(mainDir, subDir)){
  if(file_test("-f", file.path(mainDir, subDir)) {
    stop("Path can't be created because a file with that name already exists.")
  } else {
    dir.create(file.path(mainDir, subDir))
  }
}

Затем в любом последующем скрипте, который должен был манипулировать файлом в subDir, вы можете использовать что-то вроде:

mainDir = getOption(someUniqueTag.mainDir)
subDir = getOption(someUniqueTag.subDir)
filename = "fileToBeCreated.txt"
file.create(file.path(mainDir, subDir, filename))

Это решение оставляет рабочий каталог под контролем пользователя.

G пул
источник
8

У меня была проблема с R 2.15.3, из-за которой при попытке рекурсивно создать древовидную структуру на общем сетевом диске я получил ошибку разрешения.

Чтобы обойти эту странность, я вручную создаю структуру;

mkdirs <- function(fp) {
    if(!file.exists(fp)) {
        mkdirs(dirname(fp))
        dir.create(fp)
    }
} 

mkdirs("H:/foo/bar")
user425678
источник
5

Один лайнер:

if (!dir.exists(output_dir)) {dir.create(output_dir)}

Пример:

dateDIR <- as.character(Sys.Date())
outputDIR <- file.path(outD, dateDIR)
if (!dir.exists(outputDIR)) {dir.create(outputDIR)}
den2042
источник
2

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

file.info(cacheDir)[1,"isdir"]

file.info не заботится о слэше на конце.

file.existsв Windows произойдет сбой каталога, если он завершится косой чертой и преуспеет без него. Так что это не может использоваться, чтобы определить, является ли путь каталогом.

file.exists("R:/data/CCAM/CCAMC160b_echam5_A2-ct-uf.-5t05N.190to240E_level1000/cache/")
[1] FALSE

file.exists("R:/data/CCAM/CCAMC160b_echam5_A2-ct-uf.-5t05N.190to240E_level1000/cache")
[1] TRUE

file.info(cacheDir)["isdir"]
user3807179
источник
Что не так в этом ответе (кроме dir.create()части, кроме )? Являются ли утверждения неправильными или просто считаются бесполезными для решения поставленного вопроса?
mschilli