检查目录是否存在,如果不存在则创建


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。
PaulHurleyuk

42
您永远不应该setwd()在R代码中使用-它基本上使您无法使用工作目录,因为您再也无法在计算机之间轻松移动代码了。
hadley 2010年

6
@hadley有趣的话题值得深思,非常感谢您对其他方法的想法。在工作中,所有计算机都同步到同一网络,因此文件路径是一致的。如果不是,那么我们要处理的问题比脚本的可移植性还要大。在这个特定的示例中,我正在编写一个脚本,该脚本将被加载到将在我们国家公园周围携带2年的计算机上。该脚本将从本地SQL实例中获取数据,进行一些处理,然后吐出一个.csv。最终产品将是.bat最终用户永远不必修改的文件。
大通

@Chase但是您不需要setwd使用网络路径。您只需要提供路径即可保存结果并仍然使用当前路径(R会话启动时建立的路径)。或者以所需的工作目录开始R。
Marek

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"), ...)(使用网络驱动器时使用的类似方法)。
Marek

Answers:


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 2014年

5
^是否有办法只禁止一个特定警告?
2016年

2
嗨,我想创建一个嵌套目录,就像我在文件夹test1中,然后在test2中,在test3中一样,但是现在我面临问题。有没有一种方法可以创建3级目录,即使directory1没有退出?
Praveen Kesani

10
@PraveenKesani这是您在寻找什么dir.create("test1/test2/test3/", recursive=TRUE)吗?
院长

6
@Bas响应时间很晚,但是仅suppressWarnings(<statement>)会针对该语句取消警告。
Ram RS

163

自2015年4月16日起,随着的发布,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
@Bas,因为您的代码错误地读取了矢量化的内容。就像使用向量化|而不是标量||。这是可行的,但却是不好的做法。
莱昂内尔·亨利

1
噢,该死,所以我也一直在通过使用来处理if语句错误|,这是向量化||有时无法使用的原因吗?我知道这不是主题,但我太渴望找到答案。我将去了解更多关于矢量化的内容。谢谢
2016年

4
那么,如果应该避免的话,最佳做法是什么ifelse
KillerSnail16年

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)。另外,如果您执行path <-file.path(mainDir,subDir),则可以重用5次,从而使if语句更具可读性。
MikeF '18

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),如果subDir是现有目录,则返回“ TRUE”;如果subDir是现有文件或不存在的文件或目录,则返回FALSE。同样,可以使用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))

该解决方案将工作目录置于用户的控制之下。


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
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.