ディレクトリの走査
過去24時間以内に変更されたファイル名
現在の作業ディレクトリを取得し、過去24時間以内に変更されたファイル名を返します。
env::current_dir は現在の作業ディレクトリを取得し、WalkDir::new は現在のディレクトリに対する新しい WalkDir を作成します。
WalkDir::into_iter はイテレータを作成し、Iterator::filter_map は WalkDir::DirEntry に Result::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::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);
}
重複するファイル名を再帰的に検索する
現在のディレクトリ内で重複するファイル名を再帰的に検索し、 それぞれを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);
}
}
}
指定した述語に一致するすべてのファイルを再帰的に検索する
現在のディレクトリ内で、過去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(())
}
ドットファイルをスキップしながらディレクトリをたどる
filter_entry を使用して、is_not_hidden 述語を満たすエントリに対して再帰的に降下し、隠しファイルと隠しディレクトリをスキップします。
Iterator::filter_map は、親が隠しディレクトリであっても、各 WalkDir::DirEntry に is_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::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 ファイルを再帰的に検索する
現在のディレクトリ内にあるすべての PNG ファイルを再帰的に検索します。
この場合、** パターンは現在のディレクトリとそのすべてのサブディレクトリにマッチします。
パスの任意の部分で ** パターンを使用できます。たとえば、/media/**/*.png
は media とそのサブディレクトリ内のすべての PNG にマッチします。
use glob::glob;
use anyhow::Result;
fn main() -> Result<()> {
for entry in glob("**/*.png")? {
println!("{}", entry?.display());
}
Ok(())
}
ファイル名の大文字と小文字を無視して、指定したパターンに一致するすべてのファイルを検索する
/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(())
}