Перевірте існування каталогу та створіть, якщо його немає


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_temporary_file_name.
Павло Гурлейюк

42
Ніколи не слід використовувати setwd()R-код - це в основному перешкоджає ідеї використання робочого каталогу, оскільки ви більше не можете легко переміщувати свій код між комп'ютерами.
хадлі

6
@hadley цікаву тему для роздумів, я вдячний вашим думкам щодо інших методів до того ж кінця. На роботі всі комп’ютери синхронізуються в одній мережі, тому шляхи до файлів послідовні. Якщо їх немає, у нас виникають більші проблеми, ніж переносимість сценарію. У цьому конкретному прикладі я писав сценарій, який буде завантажений на машину, яка буде перевозитися навколо наших національних парків протягом 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))

58
Будьте в курсі, використовуючи, showWarnings = FALSEщо це також приховуватиме інші попередження, наприклад, те, що каталог не може бути створений.
zelanix

5
^ Чи існує спосіб придушити лише одне конкретне попередження?
Бас

2
Привіт, я хочу створити вкладений каталог, як, наприклад, якщо я в тесті теки1, то всередині нього test2 всередині нього test3 ... але зараз я стикаюся з проблемою. Чи є спосіб я створити каталог 3 рівня, навіть якщо каталог1 не виходить ??
Praveen Kesani

10
@PraveenKesani Це те, що ти шукаєш dir.create("test1/test2/test3/", recursive=TRUE):?
декан.

6
@Bas Дійсно запізніла відповідь, але suppressWarnings(<statement>)придушить попередження лише для цього твердження.
Рам 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))

9
Зауважимо, що ifelse()для невекторизованого розгалуження це не дуже корисна практика .
Ліонель Генрі

2
@Бай тому, що ваш код помилково читає, ніби щось векторизовано відбувається. Це як використовувати векторизований |замість скалярного ||. Це працює, але це погана практика.
Ліонель Генрі

1
О, чорт, я робив свою справу, якщо твердження неправильно, також використовуючи |, чи векторизація є причиною, з якою ||іноді не працює ? Я знаю, що це поза темою, але я занадто нетерплячий, щоб дізнатися це. Я відмовчуся і прочитаю докладніше про векторизацію. Спасибі
Бас

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.


3
Чи є причина, коли ви використовуєте paste (...) vs file.path (mainDir, subDir). Також якщо ви зробили шлях <- file.path (mainDir, subDir), ви можете його повторно використати 5 разів, зробивши оператори if більш читабельними.
MikeF

14

Ось проста перевірка , і створюється редактор, якщо його немає:

## 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!")
}

9

Використання file.exists () для перевірки на наявність каталогу є проблемою в початковій публікації. Якщо subDir включив ім'я існуючого файлу (а не просто шлях), file.exists () поверне TRUE, але виклик setwd () не вдасться, оскільки ви не можете встановити робочий каталог, який би вказував на файл.

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

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

Отже, розглянемо наступне рішення, де 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))

Це рішення залишає робочий каталог під контролем користувача.


8

У мене виникла проблема з R 2.15.3, внаслідок чого під час спроби створити структуру дерева рекурсивно на спільному мережному диску я отримав помилку дозволу.

Щоб обійти цю дивацтво, я вручну створюю структуру;

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

mkdirs("H:/foo/bar")

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)}

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"]

Що не так у цій відповіді (крім того, що не включати dir.create()частину)? Чи є твердження помилковими чи просто вважаються не корисними для вирішення питання?
mschilli
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.