mirror of
https://github.com/uutils/coreutils
synced 2024-12-14 15:22:38 +00:00
df: add support for --total option
Add support for the `--total` option to `df`, which displays the total of each numeric column. For example, $ df --total Filesystem 1K-blocks Used Available Use% Mounted on udev 3858016 0 3858016 0% /dev ... /dev/loop14 63488 63488 0 100% /snap/core20/1361 total 258775268 98099712 148220200 40% -
This commit is contained in:
parent
41acdb5471
commit
d0ebd1c9d0
3 changed files with 117 additions and 0 deletions
|
@ -106,6 +106,11 @@ impl From<&ArgMatches> for BlockSize {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Parameters that control the behavior of `df`.
|
||||||
|
///
|
||||||
|
/// Most of these parameters control which rows and which columns are
|
||||||
|
/// displayed. The `block_size` determines the units to use when
|
||||||
|
/// displaying numbers of bytes or inodes.
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
struct Options {
|
struct Options {
|
||||||
show_local_fs: bool,
|
show_local_fs: bool,
|
||||||
|
@ -115,6 +120,9 @@ struct Options {
|
||||||
show_inode_instead: bool,
|
show_inode_instead: bool,
|
||||||
block_size: BlockSize,
|
block_size: BlockSize,
|
||||||
fs_selector: FsSelector,
|
fs_selector: FsSelector,
|
||||||
|
|
||||||
|
/// Whether to show a final row comprising the totals for each column.
|
||||||
|
show_total: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Options {
|
impl Options {
|
||||||
|
@ -128,6 +136,7 @@ impl Options {
|
||||||
show_inode_instead: matches.is_present(OPT_INODES),
|
show_inode_instead: matches.is_present(OPT_INODES),
|
||||||
block_size: BlockSize::from(matches),
|
block_size: BlockSize::from(matches),
|
||||||
fs_selector: FsSelector::from(matches),
|
fs_selector: FsSelector::from(matches),
|
||||||
|
show_total: matches.is_present(OPT_TOTAL),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -307,9 +316,15 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||||
.filter(|fs| fs.usage.blocks != 0 || opt.show_all_fs || opt.show_listed_fs)
|
.filter(|fs| fs.usage.blocks != 0 || opt.show_all_fs || opt.show_listed_fs)
|
||||||
.map(Into::into)
|
.map(Into::into)
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
println!("{}", Header::new(&opt));
|
println!("{}", Header::new(&opt));
|
||||||
|
let mut total = Row::new("total");
|
||||||
for row in data {
|
for row in data {
|
||||||
println!("{}", DisplayRow::new(&row, &opt));
|
println!("{}", DisplayRow::new(&row, &opt));
|
||||||
|
total += row;
|
||||||
|
}
|
||||||
|
if opt.show_total {
|
||||||
|
println!("{}", DisplayRow::new(&total, &opt));
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
@ -15,6 +15,7 @@ use crate::{BlockSize, Filesystem, Options};
|
||||||
use uucore::fsext::{FsUsage, MountInfo};
|
use uucore::fsext::{FsUsage, MountInfo};
|
||||||
|
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
use std::ops::AddAssign;
|
||||||
|
|
||||||
/// A row in the filesystem usage data table.
|
/// A row in the filesystem usage data table.
|
||||||
///
|
///
|
||||||
|
@ -67,6 +68,63 @@ pub(crate) struct Row {
|
||||||
inodes_usage: Option<f64>,
|
inodes_usage: Option<f64>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Row {
|
||||||
|
pub(crate) fn new(source: &str) -> Self {
|
||||||
|
Self {
|
||||||
|
fs_device: source.into(),
|
||||||
|
fs_type: "-".into(),
|
||||||
|
fs_mount: "-".into(),
|
||||||
|
bytes: 0,
|
||||||
|
bytes_used: 0,
|
||||||
|
bytes_free: 0,
|
||||||
|
bytes_usage: None,
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
bytes_capacity: None,
|
||||||
|
inodes: 0,
|
||||||
|
inodes_used: 0,
|
||||||
|
inodes_free: 0,
|
||||||
|
inodes_usage: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AddAssign for Row {
|
||||||
|
/// Sum the numeric values of two rows.
|
||||||
|
///
|
||||||
|
/// The `Row::fs_device` field is set to `"total"` and the
|
||||||
|
/// remaining `String` fields are set to `"-"`.
|
||||||
|
fn add_assign(&mut self, rhs: Self) {
|
||||||
|
let bytes = self.bytes + rhs.bytes;
|
||||||
|
let bytes_used = self.bytes_used + rhs.bytes_used;
|
||||||
|
let inodes = self.inodes + rhs.inodes;
|
||||||
|
let inodes_used = self.inodes_used + rhs.inodes_used;
|
||||||
|
*self = Self {
|
||||||
|
fs_device: "total".into(),
|
||||||
|
fs_type: "-".into(),
|
||||||
|
fs_mount: "-".into(),
|
||||||
|
bytes,
|
||||||
|
bytes_used,
|
||||||
|
bytes_free: self.bytes_free + rhs.bytes_free,
|
||||||
|
bytes_usage: if bytes == 0 {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(bytes_used as f64 / bytes as f64)
|
||||||
|
},
|
||||||
|
// TODO Figure out how to compute this.
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
bytes_capacity: None,
|
||||||
|
inodes,
|
||||||
|
inodes_used,
|
||||||
|
inodes_free: self.inodes_free + rhs.inodes_free,
|
||||||
|
inodes_usage: if inodes == 0 {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(inodes_used as f64 / inodes as f64)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<Filesystem> for Row {
|
impl From<Filesystem> for Row {
|
||||||
fn from(fs: Filesystem) -> Self {
|
fn from(fs: Filesystem) -> Self {
|
||||||
let MountInfo {
|
let MountInfo {
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
// spell-checker:ignore udev
|
||||||
use crate::common::util::*;
|
use crate::common::util::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -77,4 +78,47 @@ fn test_type_option() {
|
||||||
new_ucmd!().args(&["-t", "ext4", "-t", "ext3"]).succeeds();
|
new_ucmd!().args(&["-t", "ext4", "-t", "ext3"]).succeeds();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_total() {
|
||||||
|
// Example output:
|
||||||
|
//
|
||||||
|
// Filesystem 1K-blocks Used Available Use% Mounted on
|
||||||
|
// udev 3858016 0 3858016 0% /dev
|
||||||
|
// ...
|
||||||
|
// /dev/loop14 63488 63488 0 100% /snap/core20/1361
|
||||||
|
// total 258775268 98099712 148220200 40% -
|
||||||
|
let output = new_ucmd!().arg("--total").succeeds().stdout_move_str();
|
||||||
|
|
||||||
|
// Skip the header line.
|
||||||
|
let lines: Vec<&str> = output.lines().skip(1).collect();
|
||||||
|
|
||||||
|
// Parse the values from the last row.
|
||||||
|
let last_line = lines.last().unwrap();
|
||||||
|
let mut iter = last_line.split_whitespace();
|
||||||
|
assert_eq!(iter.next().unwrap(), "total");
|
||||||
|
let reported_total_size = iter.next().unwrap().parse().unwrap();
|
||||||
|
let reported_total_used = iter.next().unwrap().parse().unwrap();
|
||||||
|
let reported_total_avail = iter.next().unwrap().parse().unwrap();
|
||||||
|
|
||||||
|
// Loop over each row except the last, computing the sum of each column.
|
||||||
|
let mut computed_total_size = 0;
|
||||||
|
let mut computed_total_used = 0;
|
||||||
|
let mut computed_total_avail = 0;
|
||||||
|
let n = lines.len();
|
||||||
|
for line in &lines[..n - 1] {
|
||||||
|
let mut iter = line.split_whitespace();
|
||||||
|
iter.next().unwrap();
|
||||||
|
computed_total_size += iter.next().unwrap().parse::<u64>().unwrap();
|
||||||
|
computed_total_used += iter.next().unwrap().parse::<u64>().unwrap();
|
||||||
|
computed_total_avail += iter.next().unwrap().parse::<u64>().unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that the sum of each column matches the reported value in
|
||||||
|
// the last row.
|
||||||
|
assert_eq!(computed_total_size, reported_total_size);
|
||||||
|
assert_eq!(computed_total_used, reported_total_used);
|
||||||
|
assert_eq!(computed_total_avail, reported_total_avail);
|
||||||
|
// TODO We could also check here that the use percentage matches.
|
||||||
|
}
|
||||||
|
|
||||||
// ToDO: more tests...
|
// ToDO: more tests...
|
||||||
|
|
Loading…
Reference in a new issue