Kiểm tra sự tồn tại của thư mục và tạo nếu không tồn tại


388

Tôi thường thấy mình viết các tập lệnh R tạo ra nhiều đầu ra. Tôi thấy nó sạch hơn để đưa đầu ra này vào (các) thư mục riêng của nó. Những gì tôi đã viết dưới đây sẽ kiểm tra sự tồn tại của một thư mục và di chuyển vào đó, hoặc tạo thư mục và sau đó di chuyển vào đó. Có cách nào tốt hơn để tiếp cận điều này?

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
Tôi chắc chắn tôi đã thấy một hàm R tạo một thư mục tạm thời với một tên được tạo ngẫu nhiên và trả về tên đó. Tôi nghĩ rằng có một cái tương tự tạo ra một tập tin tạm thời. Tôi không thể tìm thấy chúng một cách trực tiếp, nhưng gói Databel ( cran.r-project.org/web/packages/DatABEL/index.html ) có chức năng get_t tạm_file_name.
PaulHurleyuk

42
Bạn không bao giờ nên sử dụng setwd()mã R - về cơ bản nó đánh bại ý tưởng sử dụng thư mục làm việc vì bạn không còn có thể dễ dàng di chuyển mã giữa các máy tính.
hadley

6
@hadley chủ đề thú vị để suy ngẫm, tôi đánh giá cao suy nghĩ của bạn về các phương pháp khác đến cùng. Tại nơi làm việc, tất cả các máy tính được đồng bộ hóa với cùng một mạng để đường dẫn tệp nhất quán. Nếu họ không, chúng tôi có vấn đề lớn hơn để giải quyết hơn là tính di động của tập lệnh. Trong ví dụ cụ thể này, tôi đã viết một kịch bản sẽ được tải trên một máy sẽ được mang theo xung quanh các công viên quốc gia của chúng tôi trong 2 năm. Tập lệnh này sẽ lấy dữ liệu từ một cá thể SQL cục bộ, thực hiện một số xử lý và nhổ ra .csv. Sản phẩm cuối sẽ là một .battệp mà người dùng cuối sẽ không bao giờ phải sửa đổi.
Đuổi theo

@Chase Nhưng bạn không cần phải setwdlàm việc với các đường dẫn mạng. Bạn chỉ cần cung cấp các đường dẫn để lưu kết quả và vẫn hoạt động với đường dẫn hiện tại (đường dẫn đó được thiết lập khi phiên R bắt đầu). Hoặc bắt đầu R với thư mục làm việc mong muốn.
Marek

5
Vâng. Hoặc parametrize out_dir <- "path/to/output/directory"và sau đó sử dụng write.table(file = file.path(out_dir,"table_1.csv"), ...). Hoặc thậm chí out_file <- function(fnm) file.path("path/to/output/directory", fnm)và sau đó write.table(file = out_file("table_1.csv"), ...)(phương pháp tương tự tôi sử dụng khi làm việc với các ổ đĩa mạng).
Marek

Câu trả lời:


403

Sử dụng showWarnings = FALSE:

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

dir.create()không sập nếu thư mục đã tồn tại, nó chỉ in ra một cảnh báo. Vì vậy, nếu bạn có thể sống với việc nhìn thấy các cảnh báo, không có vấn đề gì khi chỉ làm điều này:

dir.create(file.path(mainDir, subDir))
setwd(file.path(mainDir, subDir))

58
Lưu ý khi sử dụng showWarnings = FALSE, điều này cũng sẽ ẩn các cảnh báo khác như thư mục không thể điều trị được.
zelanix

5
^ Có cách nào để chỉ triệt tiêu một cảnh báo cụ thể không?
Bas

2
Xin chào, tôi muốn ot tạo thư mục lồng nhau, giống như nếu tôi đang ở trong thư mục test1 thì bên trong nó test2 bên trong nó test3 ... nhưng ngay bây giờ tôi đang gặp vấn đề. Có cách nào để tôi có thể tạo 3 cấp thư mục ngay cả khi thư mục1 không thoát không ??
Praveen Kesani

10
@PraveenKesani Đây có phải là những gì bạn đang tìm kiếm : dir.create("test1/test2/test3/", recursive=TRUE)?
trưởng khoa.

6
@Bas Phản hồi thực sự muộn nhưng suppressWarnings(<statement>)sẽ ngăn chặn cảnh báo cho câu nói đó.
Ram RS

163

Kể từ ngày 16 tháng 4 năm 2015, với việc phát hành R 3.2.0một chức năng mới được gọi là dir.exists(). Để sử dụng chức năng này và tạo thư mục nếu nó không tồn tại, bạn có thể sử dụng:

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

Điều này sẽ trở lại FALSEnếu thư mục đã tồn tại hoặc không thể điều trị được và TRUEnếu nó không tồn tại nhưng đã được tạo thành công.

Lưu ý rằng để kiểm tra xem thư mục có tồn tại không, bạn có thể sử dụng

dir.exists(file.path(mainDir, subDir))

9
Chỉ cần lưu ý rằng nó không phải là thực hành tốt để sử dụng ifelse()cho phân nhánh không véc tơ.
Lionel Henry

2
@Bas bởi vì mã của bạn đọc sai như thể một cái gì đó véc tơ đang xảy ra. Nó giống như sử dụng véc tơ |thay vì vô hướng ||. Nó hoạt động nhưng là thực hành xấu.
Lionel Henry

1
Ôi chết tiệt, vì vậy tôi đã làm sai nếu phát biểu sai bằng cách sử dụng |, liệu vector hóa có phải là lý do ||đôi khi nó không hoạt động không? Tôi biết điều này không đúng chủ đề nhưng tôi quá háo hức để tìm hiểu. Tôi sẽ đi và đọc thêm về vector hóa. Cảm ơn
Bas

4
Vì vậy, cách thực hành tốt nhất để làm điều này là gì nếu chúng ta nên tránh ifelse?
KillerSnail

6
sử dụng if và khác;)
Lionel Henry

17

Về mặt kiến ​​trúc chung, tôi muốn giới thiệu cấu trúc sau đây liên quan đến việc tạo thư mục. Điều này sẽ bao gồm hầu hết các vấn đề tiềm ẩn và bất kỳ vấn đề nào khác với việc tạo thư mục sẽ được phát hiện bởi dir.createcuộc gọi.

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
}

Ngoài ra, hãy lưu ý rằng nếu ~/fookhông tồn tại thì cuộc gọi đến dir.create('~/foo/bar')sẽ thất bại trừ khi bạn chỉ định recursive = TRUE.


3
có lý do gì bạn sử dụng paste (...) so với file.path (mainDir, subDir). Ngoài ra nếu bạn đã thực hiện một đường dẫn <- file.path (mainDir, subDir), bạn có thể sử dụng lại 5 lần để làm cho các câu lệnh if dễ đọc hơn.
MikeF

14

Dưới đây là những kiểm tra đơn giản , và tạo ra các thư mục nếu không tồn tại:

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

Việc sử dụng file.exists () để kiểm tra sự tồn tại của thư mục là một vấn đề trong bài viết gốc. Nếu subDir bao gồm tên của một tệp hiện có (chứ không chỉ là một đường dẫn), file.exists () sẽ trả về TRUE, nhưng lệnh gọi đến setwd () sẽ thất bại vì bạn không thể đặt thư mục làm việc trỏ vào một tệp.

Tôi sẽ khuyên bạn nên sử dụng file_test (op = "- d", subDir), sẽ trả về "TRUE" nếu subDir là một thư mục hiện có, nhưng FALSE nếu subDir là tệp hiện có hoặc tệp hoặc thư mục không tồn tại. Tương tự, việc kiểm tra tệp có thể được thực hiện bằng op = "- f".

Ngoài ra, như được mô tả trong một nhận xét khác, thư mục làm việc là một phần của môi trường R và phải được kiểm soát bởi người dùng, không phải là tập lệnh. Các kịch bản nên, lý tưởng nhất, không thay đổi môi trường R. Để giải quyết vấn đề này, tôi có thể sử dụng các tùy chọn () để lưu trữ một thư mục có sẵn trên toàn cầu nơi tôi muốn tất cả đầu ra của mình.

Vì vậy, hãy xem xét giải pháp sau đây, trong đó someUniqueTag chỉ là tiền tố do lập trình viên xác định cho tên tùy chọn, điều này khiến cho một tùy chọn có cùng tên đã tồn tại. (Ví dụ: nếu bạn đang phát triển gói gọi là "filer", bạn có thể sử dụng filer.mainDir và filer.subDir).

Đoạn mã sau sẽ được sử dụng để đặt các tùy chọn có sẵn để sử dụng sau này trong các tập lệnh khác (do đó tránh sử dụng setwd () trong tập lệnh) và để tạo thư mục nếu cần:

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

Sau đó, trong bất kỳ tập lệnh tiếp theo nào cần thao tác với tệp trong subDir, bạn có thể sử dụng một cái gì đó như:

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

Giải pháp này để lại thư mục làm việc dưới sự kiểm soát của người dùng.


8

Tôi đã gặp sự cố với R 2.15.3, trong khi cố gắng tạo cấu trúc cây theo cách đệ quy trên ổ đĩa mạng chung, tôi sẽ gặp lỗi về quyền.

Để giải quyết vấn đề kỳ quặc này, tôi tự tạo cấu trúc;

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

mkdirs("H:/foo/bar")

5

Lót:

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

Thí dụ:

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

2

Để tìm hiểu xem một đường dẫn là một thư mục hợp lệ hãy thử:

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

file.info không quan tâm đến một dấu gạch chéo vào cuối.

file.existstrên Windows sẽ thất bại cho một thư mục nếu nó kết thúc bằng dấu gạch chéo và thành công mà không có nó. Vì vậy, điều này không thể được sử dụng để xác định nếu một đường dẫn là một thư mục.

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

Điều gì sai về câu trả lời này (bao gồm không bao gồm dir.create()phần)? Là những tuyên bố sai hoặc chỉ được coi là không hữu ích để giải quyết câu hỏi trong tầm tay?
mschilli
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.