Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

ディレクトリの走査

過去24時間以内に変更されたファイル名

walkdir-badge cat-filesystem-badge

現在の作業ディレクトリを取得し、過去24時間以内に変更されたファイル名を返します。 env::current_dir は現在の作業ディレクトリを取得し、WalkDir::new は現在のディレクトリに対する新しい WalkDir を作成します。 WalkDir::into_iter はイテレータを作成し、Iterator::filter_mapWalkDir::DirEntryResult::ok を適用してディレクトリを除外します。

std::fs::Metadata::modified は、最後の変更からの SystemTime::elapsed 時間を返します。 Duration::as_secs はその時間を秒に変換し、24時間(24 * 60 * 60 秒)と比較します。 Iterator::for_each はファイル名を出力します。

use walkdir::WalkDir;
use anyhow::Result;
use std::env;

fn main() -> Result<()> {
    let current_dir = env::current_dir()?;
    println!("Entries modified in the last 24 hours in {:?}:", current_dir);

    for entry in WalkDir::new(current_dir)
            .into_iter()
            .filter_map(|e| e.ok())
            .filter(|e| e.metadata().unwrap().is_file()) {
        let path = entry.path();
        let metadata = entry.metadata()?;
        let modified = metadata.modified()?.elapsed()?.as_secs();
        if modified < 24 * 3600 {
            println!("{}", path.display());
        }
    }

    Ok(())
}

指定されたパスのループを見つける

same_file-badge walkdir-badge cat-filesystem-badge

指定されたパスのループを検出するには same_file::is_same_file を使用します。 たとえば、Unix システムではシンボリックリンクによってループが作成されます:

mkdir -p /tmp/foo/bar/baz
ln -s /tmp/foo/  /tmp/foo/bar/baz/qux

次のコードは、ループが存在することを確認します。

use walkdir::WalkDir;
use same_file::is_same_file;

fn main() {
    let mut loop_found = false;
    for entry in WalkDir::new(".")
        .follow_links(true)
        .into_iter()
        .filter_map(|e| e.ok()) {
        let ancestor = entry.path()
            .ancestors()
            .skip(1)
            .find(|ancestor| is_same_file(ancestor, entry.path()).is_ok());

        if ancestor.is_some() {
            loop_found = true;
        }
    }
    // 注: このテストは、実際にシンボリックリンクのループがある場合にのみ成功します
    // println!("Loop found: {}", loop_found);
}

重複するファイル名を再帰的に検索する

walkdir-badge cat-filesystem-badge

現在のディレクトリ内で重複するファイル名を再帰的に検索し、 それぞれを1回だけ出力します。

use walkdir::WalkDir;
use std::collections::HashMap;

fn main() {
    let mut filenames = HashMap::new();

    for entry in WalkDir::new(".")
                         .into_iter()
                         .filter_map(Result::ok)
                         .filter(|e| e.file_type().is_file()) {

        let f_name = String::from(entry.file_name().to_string_lossy());
        let counter = filenames.entry(f_name.clone()).or_insert(0);
        *counter += 1;

        if *counter == 2 {
            println!("{}", f_name);
        }
    }
}

指定した述語に一致するすべてのファイルを再帰的に検索する

walkdir-badge cat-filesystem-badge

現在のディレクトリ内で、過去1日以内に変更された JSON ファイルを見つけます。 follow_links を使用すると、シンボリックリンクが通常のディレクトリやファイルであるかのように たどられることが保証されます。

use walkdir::WalkDir;
use anyhow::Result;

fn main() -> Result<()> {
    for entry in WalkDir::new(".")
        .follow_links(true)
        .into_iter()
        .filter_map(|e| e.ok()) {
        let f_name = entry.file_name().to_string_lossy();
        let sec = entry.metadata()?.modified()?;

        if f_name.ends_with(".json") && sec.elapsed()?.as_secs() < 86400 {
            println!("{}", entry.path().display());
        }
    }
    Ok(())
}

ドットファイルをスキップしながらディレクトリをたどる

walkdir-badge cat-filesystem-badge

filter_entry を使用して、is_not_hidden 述語を満たすエントリに対して再帰的に降下し、隠しファイルと隠しディレクトリをスキップします。 Iterator::filter_map は、親が隠しディレクトリであっても、各 WalkDir::DirEntryis_not_hidden を適用します。

ルートディレクトリ "." は、is_not_hidden 述語で WalkDir::depth を使用しているため、対象に含まれます。

use walkdir::{DirEntry, WalkDir};

fn is_not_hidden(entry: &DirEntry) -> bool {
    entry
         .file_name()
         .to_str()
         .map(|s| entry.depth() == 0 || !s.starts_with("."))
         .unwrap_or(false)
}

fn main() {
    WalkDir::new(".")
        .into_iter()
        .filter_entry(|e| is_not_hidden(e))
        .filter_map(|v| v.ok())
        .for_each(|x| println!("{}", x.path().display()));
}

指定した深さでファイルサイズを再帰的に計算する

walkdir-badge cat-filesystem-badge

再帰の深さは WalkDir::max_depth で柔軟に設定できます。ルートディレクトリ内のファイルを無視し、3 レベル下のサブディレクトリまでにあるすべてのファイルサイズの合計を計算します。

use walkdir::WalkDir;

fn main() {
    let total_size = WalkDir::new(".")
        .max_depth(3)
        .into_iter()
        .filter_map(|entry| entry.ok())
        .filter_map(|entry| entry.metadata().ok())
        .filter(|metadata| metadata.is_file())
        .fold(0, |acc, m| acc + m.len());

    println!("Total size: {} bytes.", total_size);
}

すべての PNG ファイルを再帰的に検索する

glob-badge cat-filesystem-badge

現在のディレクトリ内にあるすべての PNG ファイルを再帰的に検索します。 この場合、** パターンは現在のディレクトリとそのすべてのサブディレクトリにマッチします。

パスの任意の部分で ** パターンを使用できます。たとえば、/media/**/*.pngmedia とそのサブディレクトリ内のすべての PNG にマッチします。

use glob::glob;
use anyhow::Result;

fn main() -> Result<()> {
    for entry in glob("**/*.png")? {
        println!("{}", entry?.display());
    }
    Ok(())
}

ファイル名の大文字と小文字を無視して、指定したパターンに一致するすべてのファイルを検索する

walkdir-badge glob-badge cat-filesystem-badge

/media/ ディレクトリ内で、img_[0-9]*.png パターンに一致するすべての画像ファイルを検索します。

他のオプションは Default のままにしつつ、glob パターンで大文字と小文字を区別しないようにするため、glob の代わりにカスタムの MatchOptions 構造体を glob_with に渡します。

use walkdir::WalkDir;
use anyhow::Result;
use glob::{glob_with, MatchOptions};

fn main() -> Result<()> {
    let options = MatchOptions {
        case_sensitive: false,
        ..Default::default()
    };

    for entry in glob_with("/media/img_[0-9]*.png", options)? {
        println!("{}", entry?.display());
    }

    Ok(())
}