Periksa keberadaan direktori dan buat jika tidak ada

388

Saya sering menemukan diri saya menulis skrip R yang menghasilkan banyak output. Saya merasa lebih bersih untuk menempatkan output ini ke dalam direktori itu sendiri. Apa yang saya tulis di bawah ini akan memeriksa keberadaan direktori dan pindah ke dalamnya, atau membuat direktori dan kemudian pindah ke dalamnya. Apakah ada cara yang lebih baik untuk mendekati ini?

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

}
Mengejar
sumber
1
Saya yakin saya telah melihat fungsi R yang membuat direktori sementara dengan nama yang dihasilkan secara acak dan mengembalikan namanya. Saya pikir ada yang serupa yang membuat file temp. Saya tidak dapat menemukannya secara langsung, tetapi paket Databel ( cran.r-project.org/web/packages/DatABEL/index.html ) memiliki fungsi get_tentar_file_name.
PaulHurleyuk
42
Anda seharusnya tidak pernah menggunakan setwd()kode R - itu pada dasarnya mengalahkan ide untuk menggunakan direktori yang berfungsi karena Anda tidak lagi dapat dengan mudah memindahkan kode di antara komputer.
Hadley
6
@adley topik menarik untuk direnungkan, saya menghargai pemikiran Anda tentang metode lain untuk tujuan yang sama. Di tempat kerja, semua komputer disinkronkan ke jaringan yang sama sehingga jalur file konsisten. Jika tidak, kami memiliki masalah yang lebih besar untuk ditangani daripada portabilitas sebuah skrip. Dalam contoh khusus ini, saya sedang menulis sebuah skrip yang akan dimuat pada mesin yang akan dibawa di sekitar taman nasional kami selama 2 tahun. Script ini akan mengambil data dari instance SQL lokal, melakukan beberapa pemrosesan, dan meludahkan .csv. Produk akhir akan menjadi .batfile yang tidak akan pernah diubah oleh pengguna akhir.
Mengejar
@Chase Tapi Anda tidak perlu setwdbekerja dengan jalur jaringan. Anda hanya perlu memberikan jalur untuk menyimpan hasil dan masih bekerja dengan jalur saat ini (yang ditetapkan saat sesi R dimulai). Atau mulai R dengan direktori kerja keinginan.
Marek
5
Ya. Atau parametrize out_dir <- "path/to/output/directory"lalu gunakan write.table(file = file.path(out_dir,"table_1.csv"), ...). Atau bahkan out_file <- function(fnm) file.path("path/to/output/directory", fnm)kemudian write.table(file = out_file("table_1.csv"), ...)(metode yang sama saya gunakan ketika bekerja dengan drive jaringan).
Marek

Jawaban:

403

Gunakan showWarnings = FALSE:

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

dir.create()tidak macet jika direktori sudah ada, itu hanya mencetak peringatan. Jadi jika Anda dapat hidup dengan melihat peringatan, tidak ada masalah dengan hanya melakukan ini:

dir.create(file.path(mainDir, subDir))
setwd(file.path(mainDir, subDir))
merampok
sumber
58
Berhati-hatilah saat menggunakannya showWarnings = FALSEbahwa ini juga akan menyembunyikan peringatan lain seperti direktori tidak dapat dibuat.
zelanix
5
^ Apakah ada cara untuk hanya menekan satu peringatan tertentu?
Bas
2
Hai, saya ingin atau membuat direktori bersarang, seperti jika saya di folder test1 lalu di dalamnya test2 di dalamnya test3 ... tapi sekarang saya sedang menghadapi masalah. Apakah ada cara agar saya dapat membuat 3 level direktori walaupun direktori1 tidak keluar ??
Praveen Kesani
10
@PraveenKesani Ini adalah apa yang Anda cari: dir.create("test1/test2/test3/", recursive=TRUE)?
Dekan.
6
@Bas Tanggapan benar-benar terlambat tetapi suppressWarnings(<statement>)akan menekan peringatan hanya untuk pernyataan itu.
Ram RS
163

Pada 16 April 2015, dengan dirilisnya R 3.2.0ada fungsi baru yang disebut dir.exists(). Untuk menggunakan fungsi ini dan membuat direktori jika tidak ada, Anda dapat menggunakan:

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

Ini akan kembali FALSEjika direktori sudah ada atau tidak dapat dikelola, dan TRUEjika tidak ada tetapi berhasil dibuat.

Perhatikan bahwa untuk cukup memeriksa apakah direktori tersebut ada, Anda dapat menggunakan

dir.exists(file.path(mainDir, subDir))
Molx
sumber
9
Hanya untuk dicatat itu bukan praktik yang baik untuk digunakan ifelse()untuk percabangan non-vektor.
Lionel Henry
2
@ Menjadi karena kode Anda salah membaca seolah-olah sesuatu vectorised sedang terjadi. Ini seperti menggunakan vektor |bukan skalar ||. Ini bekerja tetapi merupakan praktik buruk.
Lionel Henry
1
Oh sial, jadi saya sudah melakukan pernyataan if saya salah dengan menggunakan |, apakah vektorisasi itu alasannya ||kadang tidak bekerja ? Saya tahu ini di luar topik, tetapi saya terlalu bersemangat untuk mencari tahu. Saya akan defo pergi dan membaca lebih lanjut tentang vektorisasi. Terima kasih
Bas
4
Jadi apa cara praktik terbaik untuk melakukan ini jika kita harus menghindari ifelse?
KillerSnail
6
menggunakan if and else;)
Lionel Henry
17

Dalam hal arsitektur umum saya akan merekomendasikan struktur berikut berkaitan dengan pembuatan direktori. Ini akan mencakup sebagian besar masalah potensial dan masalah lain dengan pembuatan direktori akan terdeteksi oleh dir.createpanggilan.

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
}

Perlu diketahui juga bahwa jika ~/footidak ada maka panggilan ke dir.create('~/foo/bar')akan gagal kecuali Anda tentukan recursive = TRUE.

zelanix
sumber
3
apakah ada alasan Anda menggunakan paste (...) vs file.path (mainDir, subDir). Juga jika Anda melakukan path <- file.path (mainDir, subDir) Anda dapat menggunakannya kembali 5 kali sehingga pernyataan if lebih mudah dibaca.
MikeF
14

Ini cek sederhana , dan buat dir jika tidak ada:

## 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
sumber
9

Penggunaan file.exists () untuk menguji keberadaan direktori adalah masalah dalam posting asli. Jika subDir menyertakan nama file yang ada (bukan hanya path), file.exists () akan mengembalikan TRUE, tetapi panggilan ke setwd () akan gagal karena Anda tidak dapat mengatur direktori kerja untuk menunjuk pada file.

Saya akan merekomendasikan penggunaan file_test (op = "- d", subDir), yang akan mengembalikan "TRUE" jika subDir adalah direktori yang sudah ada, tetapi FALSE jika subDir adalah file yang ada atau file atau direktori yang tidak ada. Demikian pula, memeriksa file dapat dilakukan dengan op = "- f".

Selain itu, seperti dijelaskan dalam komentar lain, direktori kerja adalah bagian dari lingkungan R dan harus dikontrol oleh pengguna, bukan skrip. Skrip seharusnya, idealnya, tidak mengubah lingkungan R. Untuk mengatasi masalah ini, saya mungkin menggunakan opsi () untuk menyimpan direktori yang tersedia secara global di mana saya ingin semua output saya.

Jadi, pertimbangkan solusi berikut, di mana someUniqueTag hanyalah awalan yang ditentukan programmer untuk nama opsi, yang membuatnya tidak mungkin bahwa opsi dengan nama yang sama sudah ada. (Misalnya, jika Anda mengembangkan paket yang disebut "filer", Anda dapat menggunakan filer.mainDir dan filer.subDir).

Kode berikut akan digunakan untuk mengatur opsi yang tersedia untuk digunakan nanti di skrip lain (sehingga menghindari penggunaan setwd () dalam skrip), dan untuk membuat folder jika perlu:

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

Kemudian, dalam skrip selanjutnya yang perlu memanipulasi file di subDir, Anda dapat menggunakan sesuatu seperti:

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

Solusi ini meninggalkan direktori kerja di bawah kendali pengguna.

G Poole
sumber
8

Saya punya masalah dengan R 2.15.3 di mana ketika mencoba membuat struktur pohon secara rekursif pada drive jaringan bersama saya akan mendapatkan kesalahan izin.

Untuk menyiasati keanehan ini saya secara manual membuat struktur;

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

mkdirs("H:/foo/bar")
pengguna425678
sumber
5

Satu-liner:

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

Contoh:

dateDIR <- as.character(Sys.Date())
outputDIR <- file.path(outD, dateDIR)
if (!dir.exists(outputDIR)) {dir.create(outputDIR)}
den2042
sumber
2

Untuk mengetahui apakah jalur adalah direktori yang valid coba:

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

file.info tidak peduli tentang garis miring pada akhirnya.

file.existspada Windows akan gagal untuk direktori jika berakhir dengan garis miring, dan berhasil tanpanya. Jadi ini tidak dapat digunakan untuk menentukan apakah path adalah direktori.

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"]
pengguna3807179
sumber
Apa yang salah dengan jawaban ini (selain tidak termasuk dir.create()bagian)? Apakah pernyataan itu salah atau hanya dianggap tidak membantu untuk menyelesaikan pertanyaan?
mschilli