Verifique la existencia del directorio y cree si no existe

388

A menudo me encuentro escribiendo scripts R que generan una gran cantidad de resultados. Me parece más limpio poner esta salida en su propio directorio (s). Lo que he escrito a continuación verificará la existencia de un directorio y se moverá a él, o creará el directorio y luego se moverá a él. ¿Hay una mejor manera de abordar esto?

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

}
Persecución
fuente
1
Estoy seguro de que he visto una función R que crea un directorio temporal con un nombre generado aleatoriamente y devuelve el nombre. Creo que hay uno similar que crea un archivo temporal. No puedo encontrarlos de forma informal, pero el paquete Databel ( cran.r-project.org/web/packages/DatABEL/index.html ) tiene una función get_temporary_file_name.
PaulHurleyuk
42
Nunca debe usar setwd()en el código R, básicamente elimina la idea de usar un directorio de trabajo porque ya no puede mover fácilmente su código entre computadoras.
hadley
66
@hadley tema interesante para reflexionar, agradecería sus pensamientos sobre otros métodos para el mismo fin. En el trabajo, todas las computadoras se sincronizan con la misma red, por lo que las rutas de los archivos son consistentes. Si no lo son, tenemos problemas más importantes que tratar que la portabilidad de un script. En este ejemplo en particular, estaba escribiendo un guión que se cargaría en una máquina que se transportará por nuestros parques nacionales durante 2 años. Este script tomará datos de una instancia local de SQL, procesará un poco y escupirá un .csv. El producto final será un .batarchivo que el usuario final nunca tendrá que modificar.
Chase
@Chase Pero no necesita setwdtrabajar con rutas de red. Solo necesita proporcionar rutas para guardar resultados y seguir trabajando con la ruta actual (aquella que se estableció cuando se inició la sesión R). O comience R con el directorio de trabajo deseado.
Marek
55
Sí. O parametrizar out_dir <- "path/to/output/directory"y luego usar write.table(file = file.path(out_dir,"table_1.csv"), ...). O incluso out_file <- function(fnm) file.path("path/to/output/directory", fnm)y luego write.table(file = out_file("table_1.csv"), ...)(método similar que uso cuando trabajo con unidades de red).
Marek

Respuestas:

403

Uso showWarnings = FALSE:

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

dir.create()no se bloquea si el directorio ya existe, solo imprime una advertencia. Entonces, si puede vivir viendo advertencias, no hay problema con solo hacer esto:

dir.create(file.path(mainDir, subDir))
setwd(file.path(mainDir, subDir))
robbrit
fuente
58
Tenga en cuenta al usar showWarnings = FALSEque esto también ocultará otras advertencias, como que el directorio no se pueda crear.
zelanix
55
^ ¿Hay alguna manera de suprimir solo una advertencia específica?
Bas
2
Hola, no quiero crear un directorio anidado, como si estuviera en la carpeta prueba1 y luego dentro de la prueba2 dentro de la prueba3 ... pero ahora estoy enfrentando un problema. ¿Hay alguna manera de que pueda crear 3 niveles de directorio incluso si el directorio1 no sale?
Praveen Kesani
10
@PraveenKesani ¿Es esto lo que estás buscando dir.create("test1/test2/test3/", recursive=TRUE)?
decano.
66
@Bas Respuesta realmente tardía pero suppressWarnings(<statement>)suprimirá las advertencias solo por esa declaración.
Ram RS
163

A partir del 16 de abril de 2015, con el lanzamiento de R 3.2.0hay una nueva función llamada dir.exists(). Para usar esta función y crear el directorio si no existe, puede usar:

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

Esto devolverá FALSEsi el directorio ya existe o no es creíble, y TRUEsi no existía pero se creó con éxito.

Tenga en cuenta que para verificar simplemente si el directorio existe, puede usar

dir.exists(file.path(mainDir, subDir))
Molx
fuente
99
Solo para tener en cuenta que no es una buena práctica usar ifelse()para ramificaciones no vectorizadas.
Lionel Henry el
2
@Bas porque su código lee falsamente como si algo vectorizado estuviera sucediendo. Es como usar vectorizado en |lugar de escalar ||. Funciona pero es una mala práctica.
Lionel Henry
1
Oh, maldición, así que he estado haciendo mis declaraciones if mal también al usarlas |, ¿es la vectorización la razón por la que a veces no funciona ||? Sé que esto está fuera de tema, pero estoy demasiado ansioso por descubrirlo. Definitivamente iré y leeré más sobre vectorización. Gracias
Bas
44
Entonces, ¿cuál es la mejor forma de hacer esto si debemos evitarlo ifelse?
KillerSnail
66
usando if y else;)
Lionel Henry
17

En términos de arquitectura general, recomendaría la siguiente estructura con respecto a la creación de directorios. Esto cubrirá la mayoría de los problemas potenciales y la dir.createllamada detectará cualquier otro problema con la creación del directorio .

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
}

También tenga en cuenta que si ~/foono existe, la llamada a dir.create('~/foo/bar')fallará a menos que usted especifique recursive = TRUE.

zelanix
fuente
3
¿hay alguna razón por la que use pegar (...) vs file.path (mainDir, subDir)? Además, si hiciste una ruta <- file.path (mainDir, subDir), podrías reutilizarla 5 veces para que las declaraciones if sean más legibles.
MikeF
14

Aquí está la verificación simple , y crea el directorio si no existe:

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

El uso de file.exists () para probar la existencia del directorio es un problema en la publicación original. Si subDir incluye el nombre de un archivo existente (en lugar de solo una ruta), file.exists () devolvería VERDADERO, pero la llamada a setwd () fallaría porque no puede configurar el directorio de trabajo para que apunte a un archivo.

Recomendaría el uso de file_test (op = "- d", subDir), que devolverá "TRUE" si subDir es un directorio existente, pero FALSO si subDir es un archivo existente o un archivo o directorio inexistente. Del mismo modo, la comprobación de un archivo se puede lograr con op = "- f".

Además, como se describe en otro comentario, el directorio de trabajo es parte del entorno R y debe ser controlado por el usuario, no por un script. Los scripts deberían, idealmente, no cambiar el entorno R. Para solucionar este problema, podría usar options () para almacenar un directorio disponible globalmente donde quisiera toda mi salida.

Entonces, considere la siguiente solución, donde someUniqueTag es solo un prefijo definido por el programador para el nombre de la opción, lo que hace improbable que ya exista una opción con el mismo nombre. (Por ejemplo, si estaba desarrollando un paquete llamado "filer", podría usar filer.mainDir y filer.subDir).

El siguiente código se usaría para establecer opciones que están disponibles para su uso posterior en otros scripts (evitando así el uso de setwd () en un script), y para crear la carpeta si es necesario:

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

Luego, en cualquier script posterior que necesite manipular un archivo en subDir, puede usar algo como:

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

Esta solución deja el directorio de trabajo bajo el control del usuario.

G Poole
fuente
8

Tuve un problema con R 2.15.3 por el cual al intentar crear una estructura de árbol de forma recursiva en una unidad de red compartida recibía un error de permiso.

Para evitar esta rareza, creo manualmente la estructura;

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

mkdirs("H:/foo/bar")
usuario425678
fuente
5

Un trazador de líneas:

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

Ejemplo:

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

Para averiguar si una ruta es un directorio válido, intente:

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

file.info no le importa una barra al final.

file.existsen Windows fallará para un directorio si termina en una barra diagonal y tiene éxito sin él. Por lo tanto, esto no se puede usar para determinar si una ruta es un directorio.

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"]
usuario3807179
fuente
¿Qué tiene de malo esta respuesta (aparte de no incluir la dir.create()parte)? ¿Las afirmaciones son incorrectas o simplemente no se consideran útiles para resolver la pregunta en cuestión?
mschilli