目录穿梭

过去 24 小时内,修改过的文件名

std-badge cat-filesystem-badge

通过调用env::current_dir,获得当前工作目录,然后针对fs::read_dir中的每个条目,提取DirEntry::path,并通过fs::Metadata得到元信息。 这个Metadata::modified返回上次修改后的SystemTime::elapsed时间。Duration::as_secs会将时间转换为秒,并与 24 小时(24 _ 60 _ 60 秒)比较。Metadata::is_file则是筛选出目录。

# #[macro_use]
# extern crate error_chain;
#
use std::{env, fs};

# error_chain! {
#     foreign_links {
#         Io(std::io::Error);
#         SystemTimeError(std::time::SystemTimeError);
#     }
# }
#
fn run() -> Result<()> {
    let current_dir = env::current_dir()?;
    println!(
        "Entries modified in the last 24 hours in {:?}:",
        current_dir
    );

    for entry in fs::read_dir(current_dir)? {
        let entry = entry?;
        let path = entry.path();

        let metadata = fs::metadata(&path)?;
        let last_modified = metadata.modified()?.elapsed()?.as_secs();

        if last_modified < 24 * 3600 && metadata.is_file() {
            println!(
                "Last modified: {:?} seconds, is read only: {:?}, size: {:?} bytes, filename: {:?}",
                last_modified,
                metadata.permissions().readonly(),
                metadata.len(),
                path.file_name().ok_or("No filename")?
            );
        }
    }

    Ok(())
}
#
# quick_main!(run);

查找给定路径的循环

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

/tmp/foo/bar/baz/qux == /tmp/foo/ => /tmp/foo/bar/baz => /tmp/foo/bar/baz/qux (重复循环)

下面将断言,存在一个循环。

extern crate same_file;

use std::io;
use std::path::{Path, PathBuf};
use same_file::is_same_file;

fn contains_loop<P: AsRef<Path>>(path: P) -> io::Result<Option<(PathBuf, PathBuf)>> {
    let path = path.as_ref();
    let mut path_buf = path.to_path_buf();
    while path_buf.pop() {
        if is_same_file(&path_buf, path)? {
            return Ok(Some((path_buf, path.to_path_buf())));
        } else if let Some(looped_paths) = contains_loop(&path_buf)? {
            return Ok(Some(looped_paths));
        }
    }
    return Ok(None);
}

fn main() {
    assert_eq!(
        contains_loop("/tmp/foo/bar/baz/qux/bar/baz").unwrap(),
        Some((
            PathBuf::from("/tmp/foo"),
            PathBuf::from("/tmp/foo/bar/baz/qux")
        ))
    );
}

递归查找,重复的文件名

walkdir-badge cat-filesystem-badge

在当前目录中,递归查找重复的文件名,只打印一次。

extern crate walkdir;

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

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

    for entry in WalkDir::new(".")
            .into_iter()
            .filter_map(Result::ok)
            .filter(|e| !e.file_type().is_dir()) {
        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

在当前目录中查找,在前一天内有修改的 JSON 文件。使用follow_links确保,符号链接像普通目录和文件一样被遵循。

# #[macro_use]
# extern crate error_chain;
extern crate walkdir;

use walkdir::WalkDir;
#
# error_chain! {
#     foreign_links {
#         WalkDir(walkdir::Error);
#         Io(std::io::Error);
#         SystemTime(std::time::SystemTimeError);
#     }
# }

fn run() -> 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!("{}", f_name);
        }
    }

    Ok(())
}
#
# quick_main!(run);

跳过点(隐藏)文件时,遍历目录

walkdir-badge cat-filesystem-badge

使用filter_entry深度递归,传递的是is_not_hidden断言,因此跳过隐藏的文件和目录。Iterator::filter应用到每个WalkDir::DirEntry,即使父目录是隐藏目录。

根目录"."的结果输出,是通过is_not_hidden断言中WalkDir::depth的使用。

extern crate walkdir;

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::min_depth & WalkDir::max_depth方法。 3 个子文件夹深度的所有文件,其总和大小的计算,忽略根文件夹中的文件。

extern crate walkdir;

use walkdir::WalkDir;

fn main() {
    let total_size = WalkDir::new(".")
        .min_depth(1)
        .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/**/*.png,会匹配media下的所有 PNG,且是子目录。

# #[macro_use]
# extern crate error_chain;
extern crate glob;

use glob::glob;
#
# error_chain! {
#     foreign_links {
#         Glob(glob::GlobError);
#         Pattern(glob::PatternError);
#     }
# }

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

    Ok(())
}
#
# quick_main!(run);

查找具有给定模式的所有文件,忽略文件名大小写。

glob-badge cat-filesystem-badge

/media/目录中查找所有图像文件,要匹配img_[0-9]*.png模式。

一个自定义的MatchOptions结构,传递给glob_with函数,可以使全局模式不区分大小写,同时保留其他选项的默认Default

# #[macro_use]
# extern crate error_chain;
extern crate glob;

use glob::{glob_with, MatchOptions};
#
# error_chain! {
#     foreign_links {
#         Glob(glob::GlobError);
#         Pattern(glob::PatternError);
#     }
# }

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

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

    Ok(())
}
#
# quick_main!(run);