这里写自定义目录标题
需求背景
一个简单的指定文件删除工具,程序运行流程如下:
- 输出欢迎信息
- 获取本机IP地址(取第一个)
- 读取同目录下的
files.txt文件(每行制表符分割的两列,第一列为IP、第二列为文件路径) - 匹配出对应 IP 的文件清单
- 控制台输出待删除的文件总数
- 用户敲回车后对文件清单进行遍历删除,并输出删除信息
- 作业完成后,显示成功删除的文件总数(部分可能不存在)及总耗时
技术选型
- 使用 Rust(edition=2024) 开发,纯控制台工具,在 windows 平台打包为
exe可执行文件。 - 控制台颜色字符使用colored库
- 应用图标使用
icon.ico(来自 iconfont) - 兼容 windows 11 及 windows 10 操作系统
核心代码
使用 Trae CN 搭建基础项目框架(用的免费模型),然后手工调整才出来最终的效果。
rust
use colored::*;
use local_ip_address::local_ip;
use std::fs::File;
use std::io::{self, BufRead, Read};
use std::path::Path;
use std::time::Instant;
#[cfg(windows)]
fn enable_ansi() {
let _ = colored::control::set_virtual_terminal(true);
}
#[cfg(not(windows))]
fn enable_ansi() {}
// 从 Cargo.toml 中获取版本号
const VERSION: &str = env!("CARGO_PKG_VERSION");
const AUTHOR: &str = env!("CARGO_PKG_AUTHORS");
/**
* 显示中灰色文字
* 这样在 Windows 10/11 的 PowerShell、Conhost 都能显示灰色,而不依赖 dimmed()
*/
const GRAY: (u8, u8, u8) = (128, 128, 128);
/// 显示欢迎信息
fn show_welcome() {
let line = "-------------------------------------------" .red();
let title = format!("欢迎使用文件批量删除工具(当前版本:{})", VERSION).red();
let copyright = format!("版权所有 © {}", AUTHOR) .red();
println!("{}", line);
println!("{}", title);
println!("{}", copyright);
println!("{}", line);
println!();
}
/// 获取本机IP地址
fn get_local_ip() -> String {
match local_ip() {
Ok(ip) => ip.to_string(),
Err(e) => {
eprintln!("获取IP地址失败: {}", e);
String::from("127.0.0.1")
}
}
}
/// 读取files.txt文件
fn read_files_file() -> io::Result<Vec<(String, String)>> {
let path = Path::new("files.txt");
let file = File::open(path)?;
let reader = io::BufReader::new(file);
let mut entries = Vec::new();
for line in reader.lines() {
let line = line?;
// 使用制表符分割,支持实际的TAB字符
let parts: Vec<&str> = line.split_terminator('\t').collect();
if parts.len() == 2 {
let ip = parts[0].trim();
let path = parts[1].trim();
if !ip.is_empty() && !path.is_empty() {
entries.push((ip.to_string(), path.to_string()));
}
}
}
Ok(entries)
}
/// 删除文件
fn delete_file(path: &str) -> bool {
match std::fs::remove_file(path) {
Ok(_) => {
println!("{} {}", "删除成功: ".bright_green(), path);
true
}
Err(e) => {
println!("{} {} {}", "删除失败: ".bright_red(), path, e.to_string().truecolor(GRAY.0, GRAY.1, GRAY.2));
false
}
}
}
#[tokio::main]
async fn main() {
enable_ansi();
// 显示欢迎信息
show_welcome();
// 获取本机IP
let local_ip = get_local_ip();
// 读取文件列表
let entries = match read_files_file() {
Ok(entries) => entries,
Err(e) => {
println!();
eprintln!("{} 读取 files.txt 失败: {}", "错误:".bright_red().bold(), e);
println!();
println!("{} 请检查以下问题:", "提示:".bright_yellow());
println!(" 1. 文件 files.txt 是否存在于程序同目录下");
println!(" 2. 文件格式是否为每行制表符分隔的IP和文件路径");
println!(" 3. 程序是否有读取该文件的权限");
println!();
println!("{} 按任意键退出...", "提示:".bright_cyan());
io::stdin().read_exact(&mut [0]).unwrap_or(());
return;
}
};
// 匹配对应IP的文件
let mut files_to_delete = Vec::new();
for (ip, path) in entries {
if ip == local_ip {
files_to_delete.push(path);
}
}
let total_files = files_to_delete.len();
// 等待用户确认
println!("{} 当前 IP 地址 {} 匹配到 {} 个文件,请回车执行删除操作",
"提示:".bright_cyan(),
local_ip.bright_blue(),
total_files.to_string().bright_white()
);
io::stdin().read_line(&mut String::new()).unwrap();
// 执行删除操作
let start_time = Instant::now();
let mut deleted_count = 0;
for path in files_to_delete {
if delete_file(&path) {
deleted_count += 1;
}
}
// 显示结果
let duration = start_time.elapsed();
let seconds = duration.as_secs_f32();
println!();
println!("{} 共处理 {} 个文件,删除 {} 个,耗时 {:.2} 秒",
"完成:".bright_green().bold(),
total_files.to_string().bright_blue(),
deleted_count.to_string().bright_green(),
seconds
);
// 程序结束后等待用户按任意键退出
println!();
println!("{} 按任意键退出...", "提示:".bright_cyan());
io::stdin().read_exact(&mut [0]).unwrap_or(());
}
效果图
