Rust has consistently ranked as the "most loved programming language" in Stack Overflow developer surveys for multiple years, with its core advantages being:

  • Zero-cost abstractions: High-level language features incur no runtime performance overhead
  • Memory safety: Compile-time detection of data races and memory leaks
  • Cross-platform compilation: Single source code compiles to native binaries for Linux, macOS, Windows
  • Excellent CLI ecosystem: Crates like clap, tokio, serde make CLI development twice as effective with half the effort

This article guides you from scratch to build a Linux server management tool named sysmgr, covering the following functional modules:

  1. System information collection (CPU, memory, disk, network)
  2. Process management (listing, termination, monitoring)
  3. Log analysis (filtering, statistics, alerting)
  4. Automation operations (scheduled tasks, batch operations)

1. Project Initialization and Dependency Configuration

1.1 Creating the Project

BASH
cargo new sysmgr
cd sysmgr

1.2 Configuring Cargo.toml

TOML
[package]
name = "sysmgr"
version = "0.1.0"
edition = "2021"

[dependencies]
# CLI framework
clap = { version = "4.5", features = ["derive"] }
# Asynchronous runtime
tokio = { version = "1.40", features = ["full"] }
# System information
sysinfo = "0.31"
# Serialization
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
# Terminal UI
crossterm = "0.28"
# Regular expressions
regex = "1.10"
# Time handling
chrono = "0.4"
# Logging
tracing = "0.1"
tracing-subscriber = "0.3"
# Error handling
anyhow = "1.0"
thiserror = "1.0"

1.3 Project Structure

sysmgr/
├── Cargo.toml
├── src/
│   ├── main.rs          # Entry point and command routing
│   ├── cli.rs           # CLI argument definitions
│   ├── commands/
│   │   ├── mod.rs
│   │   ├── info.rs      # System information
│   │   ├── process.rs   # Process management
│   │   ├── log.rs       # Log analysis
│   │   └── cron.rs      # Automation tasks
│   └── utils/
│       ├── mod.rs
│       └── output.rs    # Output formatting

2. CLI Framework Construction

2.1 Defining Command Structure

Using clap's derive macros to define commands:

RUST
// src/cli.rs
use clap::{Parser, Subcommand};

#[derive(Parser)]
#[command(name = "sysmgr")]
#[command(about = "Linux server management tool", version)]
pub struct Cli {
    /// Enable verbose output
    #[arg(short, long, global = true)]
    pub verbose: bool,

    /// Output format
    #[arg(short, long, default_value = "table", global = true)]
    pub format: OutputFormat,

    #[command(subcommand)]
    pub command: Commands,
}

#[derive(Subcommand)]
pub enum Commands {
    /// Display system information
    Info {
        /// Specify information type
        #[arg(short, long)]
        section: Option<InfoSection>,
    },
    /// Process management
    Proc {
        #[command(subcommand)]
        action: ProcAction,
    },
    /// Log analysis
    Log {
        /// Log file path
        #[arg(short, long)]
        file: String,
        /// Filter keyword
        #[arg(short, long)]
        keyword: Option<String>,
        /// Calculate error statistics
        #[arg(short, long)]
        stats: bool,
    },
    /// Automation tasks
    Cron {
        #[command(subcommand)]
        action: CronAction,
    },
}

#[derive(Clone, Debug, clap::ValueEnum)]
pub enum InfoSection {
    Cpu,
    Memory,
    Disk,
    Network,
    All,
}

#[derive(Clone, Debug, clap::ValueEnum)]
pub enum OutputFormat {
    Table,
    Json,
    Csv,
}

#[derive(Subcommand)]
pub enum ProcAction {
    /// List processes
    List {
        /// Filter by name
        #[arg(short, long)]
        name: Option<String>,
        /// Sort by CPU usage
        #[arg(long)]
        sort_by_cpu: bool,
    },
    /// Terminate process
    Kill {
        /// Process ID
        pid: u32,
        /// Force termination
        #[arg(short, long)]
        force: bool,
    },
    /// Monitor process
    Watch {
        /// Process name
        name: String,
        /// Refresh interval (seconds)
        #[arg(short, long, default_value = "2")]
        interval: u64,
    },
}

#[derive(Subcommand)]
pub enum CronAction {
    /// List scheduled tasks
    List,
    /// Add scheduled task
    Add {
        /// Cron expression
        schedule: String,
        /// Command to execute
        command: String,
    },
    /// Remove scheduled task
    Remove {
        /// Task ID
        id: u32,
    },
}

2.2 Main Entry Point

RUST
// src/main.rs
mod cli;
mod commands;
mod utils;

use anyhow::Result;
use clap::Parser;
use cli::{Cli, Commands};

#[tokio::main]
async fn main() -> Result<()> {
    let cli = Cli::parse();

    // Initialize logging
    if cli.verbose {
        tracing_subscriber::fmt()
            .with_max_level(tracing::Level::DEBUG)
            .init();
    }

    match cli.command {
        Commands::Info { section } => {
            commands::info::run(section, &cli.format).await?;
        }
        Commands::Proc { action } => {
            commands::process::run(action, &cli.format).await?;
        }
        Commands::Log { file, keyword, stats } => {
            commands::log::run(&file, keyword.as_deref(), stats, &cli.format).await?;
        }
        Commands::Cron { action } => {
            commands::cron::run(action, &cli.format).await?;
        }
    }

    Ok(())
}

3. System Information Module

3.1 Implementing System Information Collection

RUST
// src/commands/info.rs
use crate::cli::{InfoSection, OutputFormat};
use anyhow::Result;
use sysinfo::System;
use serde::Serialize;

#[derive(Serialize)]
struct SystemInfo {
    hostname: String,
    os: String,
    kernel: String,
    uptime: u64,
    cpu_count: usize,
    cpu_usage: f32,
    total_memory: u64,
    used_memory: u64,
    total_swap: u64,
    used_swap: u64,
}

pub async fn run(section: Option<InfoSection>, format: &OutputFormat) -> Result<()> {
    let mut sys = System::new_all();
    sys.refresh_all();

    let section = section.unwrap_or(InfoSection::All);

    match section {
        InfoSection::Cpu => print_cpu_info(&sys, format),
        InfoSection::Memory => print_memory_info(&sys, format),
        InfoSection::Disk => print_disk_info(format),
        InfoSection::Network => print_network_info(format),
        InfoSection::All => {
            print_cpu_info(&sys, format);
            println!();
            print_memory_info(&sys, format);
            println!();
            print_disk_info(format);
            println!();
            print_network_info(format);
        }
    }

    Ok(())
}

fn print_cpu_info(sys: &System, format: &OutputFormat) {
    let cpu_count = sys.cpus().len();
    let cpu_usage: f32 = sys.cpus().iter().map(|c| c.cpu_usage()).sum::<f32>() / cpu_count as f32;

    match format {
        OutputFormat::Json => {
            let data = serde_json::json!({
                "cpu_count": cpu_count,
                "cpu_usage_percent": cpu_usage,
                "cpu_brand": sys.cpus().first().map(|c| c.brand()).unwrap_or("Unknown"),
            });
            println!("{}", serde_json::to_string_pretty(&data).unwrap());
        }
        _ => {
            println!("=== CPU Information ===");
            println!("Core count: {}", cpu_count);
            println!("Usage: {:.1}%", cpu_usage);
            if let Some(cpu) = sys.cpus().first() {
                println!("Model: {}", cpu.brand());
            }
        }
    }
}

fn print_memory_info(sys: &System, format: &OutputFormat) {
    let total = sys.total_memory();
    let used = sys.used_memory();
    let total_swap = sys.total_swap();
    let used_swap = sys.used_swap();

    match format {
        OutputFormat::Json => {
            let data = serde_json::json!({
                "total_memory_mb": total / 1024 / 1024,
                "used_memory_mb": used / 1024 / 1024,
                "memory_usage_percent": (used as f64 / total as f64) * 100.0,
                "total_swap_mb": total_swap / 1024 / 1024,
                "used_swap_mb": used_swap / 1024 / 1024,
            });
            println!("{}", serde_json::to_string_pretty(&data).unwrap());
        }
        _ => {
            println!("=== Memory Information ===");
            println!("Total memory: {} MB", total / 1024 / 1024);
            println!("Used: {} MB ({:.1}%)", used / 1024 / 1024, (used as f64 / total as f64) * 100.0);
            println!("Total swap: {} MB", total_swap / 1024 / 1024);
            println!("Used swap: {} MB", used_swap / 1024 / 1024);
        }
    }
}

fn print_disk_info(format: &OutputFormat) {
    use sysinfo::Disks;

    let disks = Disks::new_with_refreshed_list();

    match format {
        OutputFormat::Json => {
            let data: Vec<_> = disks.iter().map(|d| {
                serde_json::json!({
                    "mount_point": d.mount_point().to_string_lossy(),
                    "filesystem": d.file_system().to_string_lossy(),
                    "total_gb": d.total_space() / 1024 / 1024 / 1024,
                    "available_gb": d.available_space() / 1024 / 1024 / 1024,
                })
            }).collect();
            println!("{}", serde_json::to_string_pretty(&data).unwrap());
        }
        _ => {
            println!("=== Disk Information ===");
            println!("{:<20} {:<10} {:<10} {:<10}", "Mount Point", "Filesystem", "Total", "Available");
            for disk in disks.iter() {
                println!("{:<20} {:<10} {:<8} GB  {:<6} GB",
                    disk.mount_point().to_string_lossy(),
                    disk.file_system().to_string_lossy(),
                    disk.total_space() / 1024 / 1024 / 1024,
                    disk.available_space() / 1024 / 1024 / 1024);
            }
        }
    }
}

fn print_network_info(format: &OutputFormat) {
    use sysinfo::Networks;

    let networks = Networks::new_with_refreshed_list();

    match format {
        OutputFormat::Json => {
            let data: Vec<_> = networks.iter().map(|(name, data)| {
                serde_json::json!({
                    "interface": name,
                    "received_mb": data.total_received() / 1024 / 1024,
                    "transmitted_mb": data.total_transmitted() / 1024 / 1024,
                })
            }).collect();
            println!("{}", serde_json::to_string_pretty(&data).unwrap());
        }
        _ => {
            println!("=== Network Information ===");
            println!("{:<15} {:<15} {:<15}", "Interface", "Received", "Transmitted");
            for (name, data) in networks.iter() {
                println!("{:<15} {:<10} MB  {:<10} MB",
                    name,
                    data.total_received() / 1024 / 1024,
                    data.total_transmitted() / 1024 / 1024);
            }
        }
    }
}

3.2 Testing the System Information Module

BASH
# Compile
cargo build --release

# View all system information
./target/release/sysmgr info

# View only CPU information
./target/release/sysmgr info --section cpu

# JSON format output
./target/release/sysmgr info --format json

# View memory information (JSON)
./target/release/sysmgr info --section memory --format json

4. Process Management Module

4.1 Implementing Process Listing

RUST
// src/commands/process.rs
use crate::cli::{ProcAction, OutputFormat};
use anyhow::Result;
use sysinfo::{System, Process, Pid};
use serde::Serialize;

#[derive(Serialize)]
struct ProcessInfo {
    pid: u32,
    name: String,
    cpu_usage: f32,
    memory_mb: u64,
    status: String,
    user: String,
}

pub async fn run(action: ProcAction, format: &OutputFormat) -> Result<()> {
    match action {
        ProcAction::List { name, sort_by_cpu } => {
            list_processes(name, sort_by_cpu, format).await?;
        }
        ProcAction::Kill { pid, force } => {
            kill_process(pid, force).await?;
        }
        ProcAction::Watch { name, interval } => {
            watch_process(name, interval).await?;
        }
    }
    Ok(())
}

async fn list_processes(
    name_filter: Option<String>,
    sort_by_cpu: bool,
    format: &OutputFormat,
) -> Result<()> {
    let mut sys = System::new_all();
    sys.refresh_all();

    let mut processes: Vec<ProcessInfo> = sys.processes().iter().map(|(pid, proc_)| {
        ProcessInfo {
            pid: pid.as_u32(),
            name: proc_.name().to_string_lossy().to_string(),
            cpu_usage: proc_.cpu_usage(),
            memory_mb: proc_.memory() / 1024 / 1024,
            status: format!("{:?}", proc_.status()),
            user: proc_.user_id().map(|u| u.to_string()).unwrap_or_default(),
        }
    }).collect();

    // Filter
    if let Some(name) = name_filter {
        processes.retain(|p| p.name.to_lowercase().contains(&name.to_lowercase()));
    }

    // Sort
    if sort_by_cpu {
        processes.sort_by(|a, b| b.cpu_usage.partial_cmp(&a.cpu_usage).unwrap());
    } else {
        processes.sort_by_key(|p| p.pid);
    }

    // Output
    match format {
        OutputFormat::Json => {
            println!("{}", serde_json::to_string_pretty(&processes)?);
        }
        OutputFormat::Csv => {
            println!("PID,NAME,CPU%,MEMORY_MB,STATUS,USER");
            for p in &processes {
                println!("{},{},{:.1},{},{},{}", p.pid, p.name, p.cpu_usage, p.memory_mb, p.status, p.user);
            }
        }
        OutputFormat::Table => {
            println!("{:<8} {:<25} {:<8} {:<10} {:<10} {}", "PID", "Name", "CPU%", "Memory(MB)", "Status", "User");
            println!("{}", "-".repeat(80));
            for p in processes.iter().take(50) {
                println!("{:<8} {:<25} {:<8.1} {:<10} {:<10} {}",
                    p.pid, truncate(&p.name, 24), p.cpu_usage, p.memory_mb, p.status, p.user);
            }
            println!("\nTotal {} processes", processes.len());
        }
    }

    Ok(())
}

fn truncate(s: &str, max_len: usize) -> String {
    if s.len() > max_len {
        format!("{}...", &s[..max_len - 3])
    } else {
        s.to_string()
    }
}

async fn kill_process(pid: u32, force: bool) -> Result<()> {
    let mut sys = System::new();
    sys.refresh_processes(sysinfo::ProcessesToUpdate::All, true);

    let pid = Pid::from(pid as usize);

    if let Some(process) = sys.process(pid) {
        if force {
            process.kill();
            println!("Forcefully terminated process {} ({})", pid, process.name().to_string_lossy());
        } else {
            // Send SIGTERM
            #[cfg(unix)]
            {
                use std::process::Command;
                Command::new("kill").arg(pid.to_string()).output()?;
                println!("Sent TERM signal to process {} ({})", pid, process.name().to_string_lossy());
            }
        }
    } else {
        println!("Process {} not found", pid);
    }

    Ok(())
}

async fn watch_process(name: String, interval: u64) -> Result<()> {
    use std::time::Duration;

    println!("Monitoring process: {} (refresh every {} seconds, press Ctrl+C to stop)", name, interval);

    loop {
        let mut sys = System::new();
        sys.refresh_processes(sysinfo::ProcessesToUpdate::All, true);

        let matched: Vec<_> = sys.processes().iter()
            .filter(|(_, p)| p.name().to_string_lossy().to_lowercase().contains(&name.to_lowercase()))
            .collect();

        // Clear screen
        print!("\x1B[2J\x1B[1;1H");
        println!("=== Process Monitor: {} ===", name);
        println!("Time: {}", chrono::Local::now().format("%Y-%m-%d %H:%M:%S"));
        println!();

        if matched.is_empty() {
            println!("No processes matching '{}' found", name);
        } else {
            println!("{:<8} {:<25} {:<8} {:<10}", "PID", "Name", "CPU%", "Memory(MB)");
            println!("{}", "-".repeat(55));
            for (pid, proc_) in &matched {
                println!("{:<8} {:<25} {:<8.1} {:<10}",
                    pid, proc_.name().to_string_lossy(),
                    proc_.cpu_usage(),
                    proc_.memory() / 1024 / 1024);
            }
        }

        tokio::time::sleep(Duration::from_secs(interval)).await;
    }
}

4.2 Testing Process Management

BASH
# List all processes
./target/release/sysmgr proc list

# Sort by CPU usage
./target/release/sysmgr proc list --sort-by-cpu

# Filter specific process
./target/release/sysmgr proc list --name nginx

# JSON format output
./target/release/sysmgr proc list --format json

# Terminate process
./target/release/sysmgr proc kill 1234

# Force terminate
./target/release/sysmgr proc kill 1234 --force

# Monitor process
./target/release/sysmgr proc watch nginx --interval 5

5. Log Analysis Module

5.1 Implementing Log Analysis

RUST
// src/commands/log.rs
use crate::cli::OutputFormat;
use anyhow::Result;
use regex::Regex;
use serde::Serialize;
use std::collections::HashMap;
use std::fs::File;
use std::io::{BufRead, BufReader};

#[derive(Serialize)]
struct LogStats {
    total_lines: usize,
    error_count: usize,
    warn_count: usize,
    info_count: usize,
    debug_count: usize,
    errors_per_hour: HashMap<String, usize>,
}

pub async fn run(
    file: &str,
    keyword: Option<&str>,
    stats: bool,
    format: &OutputFormat,
) -> Result<()> {
    let file = File::open(file)?;
    let reader = BufReader::new(file);

    if stats {
        analyze_stats(reader, format)?;
    } else if let Some(kw) = keyword {
        filter_lines(reader, kw, format)?;
    } else {
        println!("Please specify --keyword or --stats argument");
    }

    Ok(())
}

fn analyze_stats(reader: BufReader<File>, format: &OutputFormat) -> Result<()> {
    let mut stats = LogStats {
        total_lines: 0,
        error_count: 0,
        warn_count: 0,
        info_count: 0,
        debug_count: 0,
        errors_per_hour: HashMap::new(),
    };

    // Regex: match timestamp and log level
    let re = Regex::new(r"(?P<timestamp>\d{4}-\d{2}-\d{2}[T ]\d{2}:\d{2}:\d{2}).*(?P<level>ERROR|WARN|INFO|DEBUG)")?;

    for line in reader.lines() {
        let line = line?;
        stats.total_lines += 1;

        if let Some(caps) = re.captures(&line) {
            let level = caps.name("level").unwrap().as_str();
            let timestamp = caps.name("timestamp").unwrap().as_str();

            match level {
                "ERROR" => {
                    stats.error_count += 1;
                    // Extract hour
                    if let Some(hour) = timestamp.get(11..13) {
                        *stats.errors_per_hour.entry(hour.to_string()).or_insert(0) += 1;
                    }
                }
                "WARN" => stats.warn_count += 1,
                "INFO" => stats.info_count += 1,
                "DEBUG" => stats.debug_count += 1,
                _ => {}
            }
        }
    }

    match format {
        OutputFormat::Json => {
            println!("{}", serde_json::to_string_pretty(&stats)?);
        }
        _ => {
            println!("=== Log Statistics ===");
            println!("Total lines: {}", stats.total_lines);
            println!("ERROR: {}", stats.error_count);
            println!("WARN: {}", stats.warn_count);
            println!("INFO: {}", stats.info_count);
            println!("DEBUG: {}", stats.debug_count);

            if !stats.errors_per_hour.is_empty() {
                println!("\n=== Error Distribution by Hour ===");
                let mut hours: Vec<_> = stats.errors_per_hour.iter().collect();
                hours.sort_by_key(|(h, _)| h.clone());
                for (hour, count) in hours {
                    println!("{}:00 - {} errors", hour, count);
                }
            }
        }
    }

    Ok(())
}

fn filter_lines(reader: BufReader<File>, keyword: &str, format: &OutputFormat) -> Result<()> {
    let re = Regex::new(&format!("(?i){}", regex::escape(keyword)))?;
    let mut count = 0;

    for line in reader.lines() {
        let line = line?;
        if re.is_match(&line) {
            count += 1;
            match format {
                OutputFormat::Json => {
                    println!("{}", serde_json::json!({"line": count, "content": line}));
                }
                _ => {
                    println!("{}", line);
                }
            }
        }
    }

    eprintln!("\nFound {} matching lines", count);
    Ok(())
}

5.2 Testing Log Analysis

BASH
# Analyze log
./target/release/sysmgr log --file /var/log/syslog --stats

# JSON format statistics
./target/release/sysmgr log --file /var/log/syslog --stats --format json

# Filter keyword
./target/release/sysmgr log --file /var/log/syslog --keyword "error"

# Filter and output JSON
./target/release/sysmgr log --file /var/log/syslog --keyword "failed" --format json

6. Automation Tasks Module

6.1 Implementing Cron Management

RUST
// src/commands/cron.rs
use crate::cli::{CronAction, OutputFormat};
use anyhow::Result;
use serde::{Deserialize, Serialize};
use std::fs;
use std::path::PathBuf;

#[derive(Serialize, Deserialize, Debug)]
struct CronJob {
    id: u32,
    schedule: String,
    command: String,
    enabled: bool,
}

#[derive(Serialize, Deserialize, Debug, Default)]
struct CronConfig {
    jobs: Vec<CronJob>,
}

fn config_path() -> PathBuf {
    let home = std::env::var("HOME").unwrap_or_else(|_| ".".to_string());
    PathBuf::from(home).join(".sysmgr").join("cron.json")
}

fn load_config() -> Result<CronConfig> {
    let path = config_path();
    if !path.exists() {
        return Ok(CronConfig::default());
    }
    let content = fs::read_to_string(path)?;
    Ok(serde_json::from_str(&content)?)
}

fn save_config(config: &CronConfig) -> Result<()> {
    let path = config_path();
    if let Some(parent) = path.parent() {
        fs::create_dir_all(parent)?;
    }
    fs::write(path, serde_json::to_string_pretty(config)?)?;
    Ok(())
}

pub async fn run(action: CronAction, format: &OutputFormat) -> Result<()> {
    match action {
        CronAction::List => {
            let config = load_config()?;
            match format {
                OutputFormat::Json => {
                    println!("{}", serde_json::to_string_pretty(&config.jobs)?);
                }
                _ => {
                    println!("{:<5} {:<20} {:<40} {:<8}", "ID", "Schedule", "Command", "Status");
                    println!("{}", "-".repeat(75));
                    for job in &config.jobs {
                        let status = if job.enabled { "Enabled" } else { "Disabled" };
                        println!("{:<5} {:<20} {:<40} {:<8}", job.id, job.schedule, job.command, status);
                    }
                }
            }
        }
        CronAction::Add { schedule, command } => {
            let mut config = load_config()?;
            let new_id = config.jobs.iter().map(|j| j.id).max().unwrap_or(0) + 1;
            config.jobs.push(CronJob {
                id: new_id,
                schedule,
                command,
                enabled: true,
            });
            save_config(&config)?;
            println!("Added task ID: {}", new_id);
        }
        CronAction::Remove { id } => {
            let mut config = load_config()?;
            let initial_len = config.jobs.len();
            config.jobs.retain(|j| j.id != id);
            if config.jobs.len() == initial_len {
                println!("Task ID not found: {}", id);
            } else {
                save_config(&config)?;
                println!("Removed task ID: {}", id);
            }
        }
    }
    Ok(())
}

6.2 Testing Automation Tasks

BASH
# Add scheduled task
./target/release/sysmgr cron add "0 */6 * * *" "/usr/bin/backup.sh"

# List all tasks
./target/release/sysmgr cron list

# JSON format list
./target/release/sysmgr cron list --format json

# Remove task
./target/release/sysmgr cron remove 1

7. Advanced Features

7.1 Cross-platform Compilation

BASH
# Compile for Linux x86_64
cargo build --release --target x86_64-unknown-linux-gnu

# Compile for Linux ARM64
cargo build --release --target aarch64-unknown-linux-gnu

# Compile for macOS
cargo build --release --target x86_64-apple-darwin

# Compile for Windows
cargo build --release --target x86_64-pc-windows-gnu

7.2 Static Linking (Avoiding glibc Version Issues)

BASH
# Use musl for static linking
rustup target add x86_64-unknown-linux-musl
cargo build --release --target x86_64-unknown-linux-musl

# Generated binary can run on any Linux distribution
ls -lh target/x86_64-unknown-linux-musl/release/sysmgr

7.3 Packaging and Distribution

BASH
# Package as Debian package using cargo-deb
cargo install cargo-deb
cargo deb

# Package as RPM using cargo-generate-rpm
cargo install cargo-generate-rpm
cargo generate-rpm

# Generate tar.gz distribution package
tar -czvf sysmgr-0.1.0-linux-x86_64.tar.gz \
    -C target/release sysmgr

8. Performance Optimization

8.1 Compilation Optimization

TOML
# Cargo.toml
[profile.release]
opt-level = 3
lto = true
codegen-units = 1
panic = "abort"
strip = true

8.2 Runtime Optimization

  • Use mimalloc instead of default allocator
RUST
// src/main.rs
#[global_allocator]
static GLOBAL: mimalloc::MiMalloc = mimalloc::MiMalloc;
  • Asynchronous I/O for processing large logs
RUST
use tokio::io::AsyncBufReadExt;

async fn process_large_log(path: &str) -> Result<()> {
    let file = tokio::fs::File::open(path).await?;
    let reader = tokio::io::BufReader::new(file);
    let mut lines = reader.lines();

    while let Some(line) = lines.next_line().await? {
        // Process each line
    }
    Ok(())
}

9. Conclusion

This article guided you through building a fully-featured Linux server management CLI tool using Rust, covering:

  1. CLI framework construction: Using clap to define clear command structure
  2. System information collection: Real-time monitoring of CPU, memory, disk, network
  3. Process management: Listing, filtering, terminating, monitoring processes
  4. Log analysis: Filtering, statistics, error distribution analysis
  5. Automation tasks: CRUD management of Cron tasks
  6. Cross-platform compilation: Single source code compiling to multi-platform binaries
  7. Performance optimization: Static linking, LTO, mimalloc, etc.

Rust's advantages in server management tool development are fully demonstrated:

  • Safety: Compile-time prevention of data races and memory issues
  • Performance: Execution efficiency comparable to C/C++
  • Portability: Easy cross-compilation and deployment to different architectures
  • Ecosystem: Rich crates accelerating development

Related Resources: - sysinfo documentation - clap documentation - tokio documentation - Rust CLI Application Guide