Controlla l'esistenza della directory e crea se non esiste


388

Mi ritrovo spesso a scrivere script R che generano molto output. Trovo più pulito inserire questo output nella propria directory. Ciò che ho scritto di seguito verificherà l'esistenza di una directory e si sposterà in essa, oppure creerà la directory e quindi si sposterà in essa. C'è un modo migliore per affrontare questo?

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
Sono sicuro di aver visto una funzione R che crea una directory temporanea con un nome generato casualmente e restituisce il nome. Penso che ce ne sia uno simile che crea un file temporaneo. Non riesco a trovarli a mano, ma il pacchetto Databel ( cran.r-project.org/web/packages/DatABEL/index.html ) ha una funzione get_temporary_file_name.
PaulHurleyuk,

42
Non dovresti mai usarlo setwd()nel codice R - fondamentalmente sconfigge l'idea di usare una directory di lavoro perché non puoi più spostare facilmente il tuo codice tra i computer.
Hadley,

6
@hadley argomento interessante su cui riflettere, apprezzerei i tuoi pensieri su altri metodi allo stesso fine. Al lavoro, tutti i computer sono sincronizzati con la stessa rete in modo che i percorsi dei file siano coerenti. In caso contrario, abbiamo problemi più grandi da affrontare rispetto alla portabilità di uno script. In questo esempio particolare, stavo scrivendo una sceneggiatura che sarebbe stata caricata su una macchina che sarebbe stata portata in giro per i nostri parchi nazionali per 2 anni. Questo script prenderà i dati da un'istanza SQL locale, eseguirà alcune elaborazioni e sputerà un .csv. Il prodotto finale sarà un .batfile che l'utente finale non dovrà mai modificare.
Insegui il

@Chase Ma non è necessario setwdlavorare con i percorsi di rete. Devi solo fornire percorsi per salvare i risultati e continuare a lavorare con il percorso corrente (quello che viene stabilito all'avvio della sessione R). Oppure avvia R con la directory di lavoro desiderata.
Marek,

5
Sì. O parametrizzare out_dir <- "path/to/output/directory"e quindi utilizzare write.table(file = file.path(out_dir,"table_1.csv"), ...). O anche out_file <- function(fnm) file.path("path/to/output/directory", fnm)e poi write.table(file = out_file("table_1.csv"), ...)(metodo simile che uso quando lavoro con le unità di rete).
Marek,

Risposte:


403

Utilizzare showWarnings = FALSE:

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

dir.create()non si arresta in modo anomalo se la directory esiste già, stampa solo un avviso. Quindi, se riesci a convivere con la visualizzazione degli avvisi, non c'è alcun problema nel fare questo:

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

58
Prestare attenzione quando si utilizza showWarnings = FALSEche ciò nasconderà anche altri avvisi come la directory non è curabile.
zelanix,

5
^ C'è un modo per sopprimere solo un avviso specifico?
Bas

2
Salve, voglio creare una directory nidificata, come se fossi nella cartella test1, quindi al suo interno test2 al suo interno test3 ... ma in questo momento sto affrontando un problema. C'è un modo in cui posso creare 3 livelli di directory anche se directory1 non esce ??
Praveen Kesani,

10
@PraveenKesani E 'questo quello che stai cercando: dir.create("test1/test2/test3/", recursive=TRUE)?
decano.

6
@Bas Risposta davvero in ritardo, ma suppressWarnings(<statement>)sopprimerà gli avvisi solo per quell'affermazione.
Ram RS

163

A partire dal 16 aprile 2015, con il rilascio di R 3.2.0c'è una nuova funzione chiamata dir.exists(). Per utilizzare questa funzione e creare la directory se non esiste, è possibile utilizzare:

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

Ciò restituirà FALSEse la directory esiste già o è irraggiungibile e TRUEse non esiste ma è stata creata con successo.

Si noti che per controllare semplicemente se la directory esiste è possibile utilizzare

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

9
Solo per notare che non è buona pratica usare ifelse()per ramificazioni non vettorializzate.
Lionel Henry,

2
@Bas perché il tuo codice legge erroneamente come se stesse accadendo qualcosa di vettorializzato. È come usare vettorializzato |anziché scalare ||. Funziona ma è una cattiva pratica.
Lionel Henry,

1
Oh dannazione, quindi ho fatto le mie dichiarazioni if ​​anche sbagliate usando |, la vettorializzazione è il motivo per cui a ||volte non funziona ? So che questo è fuori tema, ma sono troppo ansioso di scoprirlo. Devo andare a leggere di più sulla vettorializzazione. Grazie
Bas

4
Quindi qual è il modo migliore per farlo se dovessimo evitare ifelse?
KillerSnail,

6
usando if e else;)
Lionel Henry,

17

In termini di architettura generale, consiglierei la seguente struttura per quanto riguarda la creazione di directory. Questo coprirà la maggior parte dei potenziali problemi e qualsiasi altro problema relativo alla creazione della directory verrà rilevato dalla dir.createchiamata.

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
}

Inoltre, tieni presente che se ~/foonon esiste, la chiamata a dir.create('~/foo/bar')fallirà se non specificato recursive = TRUE.


3
c'è un motivo per cui si utilizza paste (...) vs file.path (mainDir, subDir). Inoltre, se hai eseguito un percorso <- file.path (mainDir, subDir) potresti riutilizzarlo 5 volte rendendo le istruzioni if ​​più leggibili.
MikeF,

14

Ecco il semplice controllo , e crea la directory se non esiste:

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

L'uso di file.exists () per verificare l'esistenza della directory è un problema nel post originale. Se subDir includesse il nome di un file esistente (anziché solo un percorso), file.exists () restituirà VERO, ma la chiamata a setwd () fallirebbe perché non è possibile impostare la directory di lavoro in modo che punti a un file.

Consiglierei l'uso di file_test (op = "- d", subDir), che restituirà "TRUE" se subDir è una directory esistente, ma FALSE se subDir è un file esistente o un file o una directory inesistente. Allo stesso modo, il controllo di un file può essere realizzato con op = "- f".

Inoltre, come descritto in un altro commento, la directory di lavoro fa parte dell'ambiente R e deve essere controllata dall'utente, non da uno script. Idealmente, gli script non dovrebbero modificare l'ambiente R. Per risolvere questo problema, potrei usare options () per memorizzare una directory disponibile a livello globale dove volevo tutto il mio output.

Quindi, considera la seguente soluzione, in cui someUniqueTag è solo un prefisso definito dal programmatore per il nome dell'opzione, il che rende improbabile che esista già un'opzione con lo stesso nome. (Ad esempio, se stavi sviluppando un pacchetto chiamato "filer", potresti usare filer.mainDir e filer.subDir).

Il seguente codice verrebbe utilizzato per impostare le opzioni disponibili per l'uso in seguito in altri script (evitando così l'uso di setwd () in uno script) e per creare la cartella se necessario:

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

Quindi, in qualsiasi script successivo che necessitava di manipolare un file in subDir, potresti usare qualcosa come:

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

Questa soluzione lascia la directory di lavoro sotto il controllo dell'utente.


8

Ho avuto un problema con R 2.15.3 per cui durante il tentativo di creare una struttura ad albero in modo ricorsivo su un'unità di rete condivisa avrei ricevuto un errore di autorizzazione.

Per aggirare questa stranezza creo manualmente la struttura;

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

mkdirs("H:/foo/bar")

5

One-liner:

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

Esempio:

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

2

Per scoprire se un percorso è una directory valida prova:

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

file.info non importa di una barra alla fine.

file.existssu Windows non riuscirà per una directory se termina in una barra e ha esito positivo senza di essa. Quindi questo non può essere usato per determinare se un percorso è una directory.

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

Cosa c'è di sbagliato in questa risposta (assediata non includendo la dir.create()parte)? Le dichiarazioni sono sbagliate o semplicemente considerate non utili per risolvere la domanda?
mschilli,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.