Rの関数dirとfile.copyの引数recursiveの挙動を確認

Rの関数dirfile.copyの引数recursiveの挙動を確認します。

関数 dir

Rの dir 関数(および内部で呼び出されている list.files 関数)における recursive 引数について確認します。

引数 recursive「サブフォルダ(下層のディレクトリ)の中まで再帰的に探すかどうか」を指定する引数です。

基本的な動作の違い

  • recursive = FALSE (デフォルト)

    • 指定したパス(フォルダ)の直下にあるファイルやフォルダのみをリストアップします。サブフォルダの中身までは見に行きません。
  • recursive = TRUE

    • 指定したパスの中にあるサブフォルダ、さらにその中のサブフォルダ…と、階層を一番下まで掘り下げてすべてのファイルを探し出します。

具体例

以下のようなフォルダ構造が手元にあるとします。

my_folder/
 ├── file_A.txt
 └── sub_folder/
      ├── file_B.txt
      └── file_C.csv

この my_folder に対して dir を実行した場合の違いは以下のようになります。

recursive = FALSE の場合(デフォルト)
dir(path = "my_folder", recursive = FALSE)

出力:

[1] "file_A.txt"   "sub_folder"

直下にあるファイル名と、サブフォルダの「名前」だけが返ってきます。

recursive = TRUE の場合
dir(path = "my_folder", recursive = TRUE)

出力:

[1] "file_A.txt"            "sub_folder/file_B.txt" "sub_folder/file_C.csv"

サブフォルダの中身まで検索され、「サブフォルダ名/ファイル名」という形でリストアップされます。

なお、デフォルト設定(include.dirs = FALSE)では、空のフォルダそのものはリストに表示されず、ファイルのみが抽出されます。

パターン指定(特定の拡張子だけを探す)

プロジェクトフォルダ内の「すべての階層」から、.csv ファイルだけを抽出したい場合。

dir(path = ".", pattern = "\\.csv$", recursive = TRUE)
フルパスの取得

recursive = TRUE で見つけたファイルを read.csv() などで読み込みたい場合、full.names = TRUE を一緒に指定した方が確実です。

指定しませんと、作業ディレクトリ(カレントディレクトリ)からの正しいパスが取得できず、エラーになることがあります。

# 完全なファイルパスとして取得する
csv_files <- dir(path = "my_folder", pattern = "\\.csv$", 
                 recursive = TRUE, full.names = TRUE)

# 上記指定で lapply 等を使って一括読み込みができるようになります
# data_list <- lapply(csv_files, read.csv)

関数 file.copy

Rの file.copy 関数における recursive 引数について確認します。

関数 dir では「フォルダの中身を階層の底まで探すかどうか」でしたが、file.copy における recursive「フォルダ(ディレクトリ)ごと、その中身を丸ごとコピーするかどうか」を指定します。

基本的な動作の違い

  • recursive = FALSE (デフォルト)

    • 「ファイル」しかコピーできません。
    • もし from(コピー元)にフォルダを指定した場合、ソースコードの以下の部分が働き、「フォルダはスキップされました」という警告(warning)が出てコピーされません。
warning("directories are omitted unless 'recursive = TRUE'")
  • recursive = TRUE

    • 指定した「フォルダそのもの」と「その中に含まれるすべてのファイル・サブフォルダ」を丸ごとコピーします。

コピー先(to)は「既に存在する1つのフォルダ」である必要があります

以下は関数コードからの抜粋です。

else if (recursive) 
    warning("'recursive' will be ignored as 'to' is not a single existing directory")

recursive = TRUE を使ってフォルダごとコピーする場合、コピー先(to)はすでに作成されているフォルダ(ディレクトリ)を指定する必要があります。

存在しないフォルダ名や、複数の場所を指定すると、recursive は無視されてしまいます。(事前に 関数 dir.create 等でコピー先フォルダを作っておく必要があります)

overwrite(上書き)のデフォルト設定が変わります

以下は関数の引数定義です。

function (from, to, overwrite = recursive, recursive = FALSE, ...)

通常、file.copy は安全のため上書き保存しませんが、recursive = TRUE にすると、連動して overwrite = TRUE になります。

つまり、フォルダごとコピーした際、コピー先に同名のファイルがあった場合は問答無用で上書きされます。

上書きしたくない場合は、明示的に overwrite = FALSE を指定する必要があります。

自分自身の中へのコピー(無限ループ)の防止

以下は関数コードからの抜粋です。

if (recursive && to %in% from) 
    stop("attempt to copy a directory to itself")

フォルダを、そのフォルダ自身の中にコピーしようとすると無限ループになるため、エラー(stop)になるよう安全対策がされています。

具体例

以下のような状況を想定します。

  • コピー元:project_A というフォルダ(中に複数のファイルやサブフォルダがある)
  • コピー先:backup というフォルダ(既に作成済みとする)
失敗例(recursive = FALSE のまま)
file.copy(from = "project_A", to = "backup")

結果:

FALSE が返り、「directories are omitted unless ‘recursive = TRUE’」という警告が出ます。中身は何もコピーされません。

成功例(recursive = TRUE を指定)
file.copy(from = "project_A", to = "backup", recursive = TRUE)

結果:

backup フォルダの中に、project_A というフォルダが丸ごと(中身のファイル・サブフォルダごと)コピーされます。 (パスで言うと backup/project_A/元のファイルたち... という状態になります)

以上です。